class TANGO_MP3_FILE_COLLATOR
Tango mp3 file collator
note
description: "Tango mp3 file collator"
author: "Finnian Reilly"
copyright: "Copyright (c) 2001-2022 Finnian Reilly"
contact: "finnian at eiffel hyphen loop dot com"
license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
date: "2023-02-14 18:54:33 GMT (Tuesday 14th February 2023)"
revision: "22"
class
TANGO_MP3_FILE_COLLATOR
inherit
EL_APPLICATION_COMMAND
EL_MODULE_LIO
EL_MODULE_FILE_SYSTEM
EL_MODULE_OS
EL_MODULE_USER_INPUT
create
make
feature {EL_COMMAND_CLIENT} -- Initialization
make (a_dir_path: like dir_path; a_is_dry_run: like is_dry_run)
do
is_dry_run := a_is_dry_run
dir_path := a_dir_path
create song_title_counts.make_equal (100)
end
feature -- Constants
Description: STRING = "[
Collates mp3 files using the path form: <genre>/<artist>/<title>.<id>.mp3
]"
feature -- Basic operations
execute
local
file_list: like OS.file_list
per_page: INTEGER
do
per_page := 50
if dir_path.exists then
file_list := OS.file_list (dir_path, "*.mp3")
song_title_counts.accommodate (file_list.count)
if is_dry_run then
across file_list as mp3_path loop
if mp3_path.cursor_index > per_page * 0 then
relocate_mp3_file (mp3_path.item)
if mp3_path.cursor_index \\ per_page = 0 then
page := page + 1
lio.put_integer_field ("Page", page);
lio.put_new_line
from until User_input.line ("Press return to continue").is_empty loop
lio.put_new_line
end
lio.put_new_line
end
end
end
else
across file_list as mp3_path loop
relocate_mp3_file (mp3_path.item)
end
end
end
end
feature {NONE} -- Implementation
dir_path: DIR_PATH
is_dry_run: BOOLEAN
new_artist (id3_info: TL_MPEG_FILE; mp3_path: FILE_PATH): ZSTRING
local
path, result_lower: ZSTRING; found: BOOLEAN
do
Result := id3_info.tag.artist
if Result.is_empty then
path := mp3_path.to_string
Result := path
end
result_lower := Result.as_lower
across Standard_artists_lower as artist until found loop
if Result.count > artist.item.count
and then result_lower.has_substring (artist.item)
or else result_lower.has_substring (Standard_artist_words_lower.i_th (artist.cursor_index).last)
then
Result := Standard_artists [artist.cursor_index]
found := True
end
end
if Result = path then
Result := Unknown
end
Result.left_adjust
end
new_genre (id3_info: TL_MPEG_FILE): ZSTRING
do
Result := id3_info.tag.genre
if Result.is_empty or else Tango_genres.has (Result) then
Result := Tango
elseif Result.starts_with (Prefix_argentine) then
Result.remove_head (Prefix_argentine.count)
elseif Result ~ Electronica then
Result := "Tango (Electro)"
end
end
new_title (id3_info: TL_MPEG_FILE; mp3_path: FILE_PATH): ZSTRING
do
Result := id3_info.tag.title
if Result.is_empty then
Result := mp3_path.base_name
end
-- Remove numbers and other rubbish from start
from until Result.is_empty or else Result.item (1).is_alpha loop
Result.remove_head (1)
end
Result.replace_character ('_', ' ')
end
page: INTEGER
relocate_mp3_file (mp3_path: FILE_PATH)
local
id3_info: TL_MPEG_FILE; destination_mp3_path: FILE_PATH
title_count: INTEGER; genre, artist, title: ZSTRING
do
create id3_info.make (mp3_path)
genre := new_genre (id3_info)
title := new_title (id3_info, mp3_path)
artist := new_artist (id3_info, mp3_path)
destination_mp3_path := dir_path.joined_file_tuple ([genre, artist, title])
song_title_counts.search (destination_mp3_path)
if song_title_counts.found then
title_count := song_title_counts.found_item + 1
else
title_count := 1
end
song_title_counts [destination_mp3_path.twin] := title_count
destination_mp3_path.add_extension (Double_digits.formatted (title_count))
destination_mp3_path.add_extension ("mp3")
if not is_dry_run then
File_system.make_directory (destination_mp3_path.parent)
OS.move_file (mp3_path, destination_mp3_path)
File_system.delete_empty_branch (mp3_path.parent)
end
lio.put_path_field ("NEW %S", destination_mp3_path.relative_path (dir_path))
lio.put_new_line
if title ~ Unknown or else artist ~ Unknown then
lio.put_path_field ("ORIGINAL %S", mp3_path.relative_path (dir_path))
lio.put_new_line
end
id3_info.dispose
end
song_title_counts: EL_HASH_TABLE [INTEGER, FILE_PATH]
feature {NONE} -- Constants
Double_digits: FORMAT_INTEGER
once
create Result.make (2)
Result.zero_fill
end
Electronica: ZSTRING
once
Result := "Electronica"
end
Prefix_argentine: ZSTRING
once
Result := "Argentine "
end
Standard_artist_words_lower: ARRAYED_LIST [EL_ZSTRING_LIST]
once
create Result.make (Standard_artists_lower.count)
across Standard_artists_lower as name loop
Result.extend (create {EL_ZSTRING_LIST}.make_word_split (name.item))
end
end
Standard_artists: ARRAY [ZSTRING]
once
Result := <<
"Alberto Moran",
"Aníbal Troilo",
"ngel D'Agostino",
"Alfredo Gobbi",
"Astor Piazzolla", -- Correct
"Astor Piazzola",
"Baffa-Berlingieri",
"Carlos Di Sarli",
"Edgardo Donato",
"Francisco Canaro",
"Juan D'Arienzo",
"Jorge Dragone",
"Lucio Demare",
"Miguel Caló",
"Pedro Laurenz",
"Osvaldo Pugliese",
"Roberto Goyeneche",
"Rodolfo Biagi"
>>
end
Standard_artists_lower: ARRAYED_LIST [ZSTRING]
once
create Result.make (Standard_artists.count)
across Standard_artists as name loop
Result.extend (name.item.as_lower)
end
end
Tango: ZSTRING
once
Result := "Tango"
end
Tango_genres: ARRAY [ZSTRING]
once
Result := << "Latin", "(113)", "(4294967295)", "Traditional", "South/Central American" >>
Result.compare_objects
end
Unknown: ZSTRING
once
Result := "Unknown"
end
end