make the way mp3s are saved configurable

Signed-off-by: Luca Schlecker <luca.schlecker@hotmail.com>
This commit is contained in:
Luca Schlecker 2020-12-29 23:27:16 +01:00
parent 2c364c38c2
commit 2e8bc6c8c5
7 changed files with 113 additions and 14 deletions

View file

@ -96,6 +96,8 @@ If you're one of those cool people who compiles from source
```yaml ```yaml
binary_directory: ~/.irs/bin binary_directory: ~/.irs/bin
music_directory: ~/Music music_directory: ~/Music
filename_pattern: "{track_number} - {title}"
directory_pattern: "{artist}/{album}"
client_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX client_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
single_folder_playlist: single_folder_playlist:
@ -122,6 +124,8 @@ Here's what they do:
```yaml ```yaml
binary_directory: ~/.irs/bin binary_directory: ~/.irs/bin
music_directory: ~/Music music_directory: ~/Music
filename_pattern: "{track_number} - {title}"
directory_pattern: "{artist}/{album}"
client_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX client_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
single_folder_playlist: single_folder_playlist:
@ -132,8 +136,8 @@ single_folder_playlist:
- `binary_directory`: a path specifying where the downloaded binaries should - `binary_directory`: a path specifying where the downloaded binaries should
be placed be placed
- `music_directory`: a path specifying where downloaded mp3s 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 - `filename_pattern`: a pattern for the output filename of the mp3
in the format of `music-dir>artist-name>album-name>track` - `directory_pattern`: a pattern for the folder structure your mp3s are saved in
- `client_key`: a client key from your spotify API application - `client_key`: a client key from your spotify API application
- `client_secret`: a client secret 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 - `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 the album name and album image of the mp3 with the title of your playlist
and the image for your playlist respectively 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`
<br><br>
```yaml
music_directory: ~/Music
filename_pattern: "{artist} - {title}"
directory_pattern: ""
```
Outputs: `~/Music/Queen - Bohemian Rhapsody.mp3`
<br><br>
```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`
<br><br>
```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`
<br>
## How it works ## How it works
**At it's core** `irs` downloads individual songs. It does this by interfacing **At it's core** `irs` downloads individual songs. It does this by interfacing

View file

@ -83,7 +83,7 @@ class CLI
s = Song.new(@args["song"], @args["artist"]) s = Song.new(@args["song"], @args["artist"])
s.provide_client_keys(Config.client_key, Config.client_secret) s.provide_client_keys(Config.client_key, Config.client_secret)
s.grab_it(@args["url"]?) s.grab_it(@args["url"]?)
s.organize_it(Config.music_directory) s.organize_it()
elsif @args["album"]? && @args["artist"]? elsif @args["album"]? && @args["artist"]?
a = Album.new(@args["album"], @args["artist"]) a = Album.new(@args["album"], @args["artist"])
a.provide_client_keys(Config.client_key, Config.client_secret) a.provide_client_keys(Config.client_key, Config.client_secret)

View file

@ -9,6 +9,8 @@ EXAMPLE_CONFIG = <<-EOP
#{Style.dim "===="} #{Style.dim "===="}
#{Style.blue "binary_directory"}: #{Style.green "~/.irs/bin"} #{Style.blue "binary_directory"}: #{Style.green "~/.irs/bin"}
#{Style.blue "music_directory"}: #{Style.green "~/Music"} #{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_key"}: #{Style.green "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
#{Style.blue "client_secret"}: #{Style.green "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"} #{Style.blue "client_secret"}: #{Style.green "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
#{Style.blue "single_folder_playlist"}: #{Style.blue "single_folder_playlist"}:
@ -24,6 +26,8 @@ module Config
@@arguments = [ @@arguments = [
"binary_directory", "binary_directory",
"music_directory", "music_directory",
"filename_pattern",
"directory_pattern",
"client_key", "client_key",
"client_secret", "client_secret",
"single_folder_playlist: enabled", "single_folder_playlist: enabled",
@ -50,6 +54,14 @@ module Config
path = @@conf["music_directory"].to_s path = @@conf["music_directory"].to_s
return Path[path].expand(home: true).to_s return Path[path].expand(home: true).to_s
end 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 def client_key : String
return @@conf["client_key"].to_s return @@conf["client_key"].to_s

28
src/bottle/pattern.cr Normal file
View file

@ -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

View file

@ -42,6 +42,6 @@ class Album < SpotifyList
end end
private def organize(song : Song) private def organize(song : Song)
song.organize_it(@home_music_directory) song.organize_it()
end end
end end

View file

@ -69,7 +69,7 @@ class Playlist < SpotifyList
safe_filename = song.filename.gsub(/[\/]/, "").gsub(" ", " ") safe_filename = song.filename.gsub(/[\/]/, "").gsub(" ", " ")
File.rename("./" + song.filename, (path / safe_filename).to_s) File.rename("./" + song.filename, (path / safe_filename).to_s)
else else
song.organize_it(@home_music_directory) song.organize_it()
end end
end end
end end

View file

@ -4,6 +4,8 @@ require "../search/youtube"
require "../interact/ripper" require "../interact/ripper"
require "../interact/tagger" require "../interact/tagger"
require "../bottle/config"
require "../bottle/pattern"
require "../bottle/styles" require "../bottle/styles"
class Song class Song
@ -83,7 +85,7 @@ class Song
end end
data = @metadata.as(JSON::Any) 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 if ask_url
outputter("url", 4) outputter("url", 4)
@ -154,20 +156,24 @@ class Song
outputter("finished", 0) outputter("finished", 0)
end end
# Will organize the song into the user's provided music directory as # Will organize the song into the user's provided music directory
# music_directory > artist_name > album_name > song # in the user's provided structure
# Must be called AFTER the song has been downloaded. # Must be called AFTER the song has been downloaded.
# #
# ``` # ```
# s = Song.new("Bohemian Rhapsody", "Queen").grab_it # s = Song.new("Bohemian Rhapsody", "Queen").grab_it
# s.organize_it("/home/cooper/Music") # s.organize_it()
# # Will move the mp3 file to # # 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 # # /home/cooper/Music/Queen/A Night At The Opera/1 - Bohemian Rhapsody.mp3
# ``` # ```
def organize_it(music_directory : String) def organize_it()
path = Path[music_directory].expand(home: true) path = Path[Config.music_directory].expand(home: true)
path = path / @artist_name.gsub(/[\/]/, "").gsub(" ", " ") Pattern.parse(Config.directory_pattern, @metadata.as(JSON::Any)).split('/').each do |dir|
path = path / @album.gsub(/[\/]/, "").gsub(" ", " ") path = path / dir.gsub(/[\/]/, "").gsub(" ", " ")
end
strpath = path.to_s strpath = path.to_s
if !File.directory?(strpath) if !File.directory?(strpath)
FileUtils.mkdir_p(strpath) FileUtils.mkdir_p(strpath)