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)