From 2e8bc6c8c525f12a418f0836dc49a01d77e9ae15 Mon Sep 17 00:00:00 2001 From: Luca Schlecker Date: Tue, 29 Dec 2020 23:27:16 +0100 Subject: [PATCH] make the way mp3s are saved configurable Signed-off-by: Luca Schlecker --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++-- src/bottle/cli.cr | 2 +- src/bottle/config.cr | 12 +++++++++ src/bottle/pattern.cr | 28 +++++++++++++++++++++ src/glue/album.cr | 2 +- src/glue/playlist.cr | 2 +- src/glue/song.cr | 24 +++++++++++------- 7 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 src/bottle/pattern.cr diff --git a/README.md b/README.md index f77811a..7079000 100755 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ If you're one of those cool people who compiles from source ```yaml binary_directory: ~/.irs/bin music_directory: ~/Music + filename_pattern: "{track_number} - {title}" + directory_pattern: "{artist}/{album}" client_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX single_folder_playlist: @@ -122,6 +124,8 @@ Here's what they do: ```yaml binary_directory: ~/.irs/bin music_directory: ~/Music +filename_pattern: "{track_number} - {title}" +directory_pattern: "{artist}/{album}" client_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX single_folder_playlist: @@ -132,8 +136,8 @@ single_folder_playlist: - `binary_directory`: a path specifying where the downloaded binaries should be placed - `music_directory`: a path specifying where downloaded mp3s should be placed. - Note that there will be more structure created inside that folder, usually - in the format of `music-dir>artist-name>album-name>track` + - `filename_pattern`: a pattern for the output filename of the mp3 + - `directory_pattern`: a pattern for the folder structure your mp3s are saved in - `client_key`: a client key from your spotify API application - `client_secret`: a client secret key from your spotify API application - `single_folder_playlist/enabled`: if set to true, all mp3s from a downloaded @@ -145,6 +149,55 @@ single_folder_playlist: the album name and album image of the mp3 with the title of your playlist and the image for your playlist respectively + +In a pattern following keywords will be replaced: + +| Keyword | Replacement | Example | +| :----: | :----: | :----: | +| `{artist}` | Artist Name | Queen | +| `{title}` | Track Title | Bohemian Rhapsody | +| `{album}` | Album Name | Stone Cold Classics | +| `{track_number}` | Track Number | 9 | +| `{total_tracks}` | Total Tracks in Album | 14 | +| `{disc_number}` | Disc Number | 1 | +| `{day}` | Release Day | 01 | +| `{month}` | Release Month | 01 | +| `{year}` | Release Year | 2006 | +| `{id}` | Spotify ID | 6l8GvAyoUZwWDgF1e4822w | + +Beware OS-restrictions when naming your mp3s. + +Pattern Examples: +```yaml +music_directory: ~/Music +filename_pattern: "{track_number} - {title}" +directory_pattern: "{artist}/{album}" +``` +Outputs: `~/Music/Queen/Stone Cold Classics/9 - Bohemian Rhapsody.mp3` +

+```yaml +music_directory: ~/Music +filename_pattern: "{artist} - {title}" +directory_pattern: "" +``` +Outputs: `~/Music/Queen - Bohemian Rhapsody.mp3` +

+```yaml +music_directory: ~/Music +filename_pattern: "{track_number} of {total_tracks} - {title}" +directory_pattern: "{year}/{artist}/{album}" +``` +Outputs: `~/Music/2006/Queen/Stone Cold Classics/9 of 14 - Bohemian Rhapsody.mp3` +

+```yaml +music_directory: ~/Music +filename_pattern: "{track_number}. {title}" +directory_pattern: "irs/{artist} - {album}" +``` +Outputs: `~/Music/irs/Queen - Stone Cold Classics/9. Bohemian Rhapsody.mp3` +
+ + ## How it works **At it's core** `irs` downloads individual songs. It does this by interfacing diff --git a/src/bottle/cli.cr b/src/bottle/cli.cr index f774915..6ff7351 100755 --- a/src/bottle/cli.cr +++ b/src/bottle/cli.cr @@ -83,7 +83,7 @@ class CLI s = Song.new(@args["song"], @args["artist"]) s.provide_client_keys(Config.client_key, Config.client_secret) s.grab_it(@args["url"]?) - s.organize_it(Config.music_directory) + s.organize_it() elsif @args["album"]? && @args["artist"]? a = Album.new(@args["album"], @args["artist"]) a.provide_client_keys(Config.client_key, Config.client_secret) diff --git a/src/bottle/config.cr b/src/bottle/config.cr index 027086f..3dde482 100755 --- a/src/bottle/config.cr +++ b/src/bottle/config.cr @@ -9,6 +9,8 @@ EXAMPLE_CONFIG = <<-EOP #{Style.dim "===="} #{Style.blue "binary_directory"}: #{Style.green "~/.irs/bin"} #{Style.blue "music_directory"}: #{Style.green "~/Music"} +#{Style.blue "filename_pattern"}: #{Style.green "\"{track_number} - {title}\""} +#{Style.blue "directory_pattern"}: #{Style.green "\"{artist}/{album}\""} #{Style.blue "client_key"}: #{Style.green "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"} #{Style.blue "client_secret"}: #{Style.green "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"} #{Style.blue "single_folder_playlist"}: @@ -24,6 +26,8 @@ module Config @@arguments = [ "binary_directory", "music_directory", + "filename_pattern", + "directory_pattern", "client_key", "client_secret", "single_folder_playlist: enabled", @@ -50,6 +54,14 @@ module Config path = @@conf["music_directory"].to_s return Path[path].expand(home: true).to_s end + + def filename_pattern : String + return @@conf["filename_pattern"].to_s + end + + def directory_pattern : String + return @@conf["directory_pattern"].to_s + end def client_key : String return @@conf["client_key"].to_s diff --git a/src/bottle/pattern.cr b/src/bottle/pattern.cr new file mode 100644 index 0000000..e446b30 --- /dev/null +++ b/src/bottle/pattern.cr @@ -0,0 +1,28 @@ +module Pattern + extend self + + def parse(formatString : String, metadata : JSON::Any) + formatted : String = formatString + + date : Array(String) = (metadata["album"]? || JSON.parse("{}"))["release_date"]?.to_s.split('-') + + keys : Hash(String, String) = { + "artist" => ((metadata.dig?("artists") || JSON.parse("{}"))[0]? || JSON.parse("{}"))["name"]?.to_s, + "title" => metadata["name"]?.to_s, + "album" => (metadata["album"]? || JSON.parse("{}"))["name"]?.to_s, + "track_number" => metadata["track_number"]?.to_s, + "disc_number" => metadata["disc_number"]?.to_s, + "total_tracks" => (metadata["album"]? || JSON.parse("{}"))["total_tracks"]?.to_s, + "year" => date[0]?.to_s, + "month" => date[1]?.to_s, + "day" => date[2]?.to_s, + "id" => metadata["id"]?.to_s + } + + keys.each do |pair| + formatted = formatted.gsub("{#{pair[0]}}", pair[1] || "") + end + + return formatted + end +end diff --git a/src/glue/album.cr b/src/glue/album.cr index 7f8d019..908456a 100755 --- a/src/glue/album.cr +++ b/src/glue/album.cr @@ -42,6 +42,6 @@ class Album < SpotifyList end private def organize(song : Song) - song.organize_it(@home_music_directory) + song.organize_it() end end diff --git a/src/glue/playlist.cr b/src/glue/playlist.cr index 069b599..b7a0861 100755 --- a/src/glue/playlist.cr +++ b/src/glue/playlist.cr @@ -69,7 +69,7 @@ class Playlist < SpotifyList safe_filename = song.filename.gsub(/[\/]/, "").gsub(" ", " ") File.rename("./" + song.filename, (path / safe_filename).to_s) else - song.organize_it(@home_music_directory) + song.organize_it() end end end diff --git a/src/glue/song.cr b/src/glue/song.cr index e567073..0de193f 100755 --- a/src/glue/song.cr +++ b/src/glue/song.cr @@ -4,6 +4,8 @@ require "../search/youtube" require "../interact/ripper" require "../interact/tagger" +require "../bottle/config" +require "../bottle/pattern" require "../bottle/styles" class Song @@ -83,7 +85,7 @@ class Song end data = @metadata.as(JSON::Any) - @filename = data["track_number"].to_s + " - #{data["name"].to_s}.mp3" + @filename = "#{Pattern.parse(Config.filename_pattern, data)}.mp3" if ask_url outputter("url", 4) @@ -154,20 +156,24 @@ class Song outputter("finished", 0) end - # Will organize the song into the user's provided music directory as - # music_directory > artist_name > album_name > song + # Will organize the song into the user's provided music directory + # in the user's provided structure # Must be called AFTER the song has been downloaded. # # ``` # s = Song.new("Bohemian Rhapsody", "Queen").grab_it - # s.organize_it("/home/cooper/Music") - # # Will move the mp3 file to + # s.organize_it() + # # With + # # directory_pattern = "{artist}/{album}" + # # filename_pattern = "{track_number} - {title}" + # # Mp3 will be moved to # # /home/cooper/Music/Queen/A Night At The Opera/1 - Bohemian Rhapsody.mp3 # ``` - def organize_it(music_directory : String) - path = Path[music_directory].expand(home: true) - path = path / @artist_name.gsub(/[\/]/, "").gsub(" ", " ") - path = path / @album.gsub(/[\/]/, "").gsub(" ", " ") + def organize_it() + path = Path[Config.music_directory].expand(home: true) + Pattern.parse(Config.directory_pattern, @metadata.as(JSON::Any)).split('/').each do |dir| + path = path / dir.gsub(/[\/]/, "").gsub(" ", " ") + end strpath = path.to_s if !File.directory?(strpath) FileUtils.mkdir_p(strpath)