diff --git a/src/glue/album.cr b/src/glue/album.cr new file mode 100644 index 0000000..2298bd1 --- /dev/null +++ b/src/glue/album.cr @@ -0,0 +1,25 @@ +require "./song" +require "./list" + + +class Album < SpotifyList + + def find_it + album = @spotify_searcher.find_item("album", { + "name" => @list_name.as(String), + "artist" => @list_author.as(String) + }) + if album + return album.as(JSON::Any) + else + puts "No album was found by that name and artist." + exit 1 + end + end + + private def set_organization(index : Int32, song : Song) + # pass + end +end + +puts Album.new("A Night At The Opera", "Queen").find_it() \ No newline at end of file diff --git a/src/glue/list.cr b/src/glue/list.cr new file mode 100644 index 0000000..cc39082 --- /dev/null +++ b/src/glue/list.cr @@ -0,0 +1,45 @@ +require "../search/spotify" +require "../search/youtube" + +require "../interact/ripper" +require "../interact/tagger" + +require "./song" + + +# A parent class for downloading albums and playlists from spotify +abstract class SpotifyList + @spotify_searcher = SpotifySearcher.new() + @file_names = [] of String + + def initialize(@list_name : String, @list_author : String?) + @spotify_searcher.authorize( + "e4198f6a3f7b48029366f22528b5dc66", + "ba057d0621a5496bbb64edccf758bde5") + end + + # Finds the list, and downloads all of the songs using the `Song` class + def grab_it + list = find_it() + contents = list["tracks"][0]["items"] + + i = 0 + contents.each do |data| + if song["track"]? + data = data["track"] + end + + song = Song.new(data["name"].to_s, data["artists"][0]["name"].to_s) + song.provide_spotify(@spotify_searcher) + set_organization(i , song) + song.grab_it() + + i += 1 + end + end + + abstract def find_it : JSON::Any + + private abstract def set_organization(song_index : Int32, song : Song) + +end \ No newline at end of file diff --git a/src/interact/logger.cr b/src/interact/logger.cr index 5eae799..a2cfc4f 100644 --- a/src/interact/logger.cr +++ b/src/interact/logger.cr @@ -4,6 +4,10 @@ class Logger @command : String + # *command* is the bash command that you want to run and capture the output + # of. *@log_name* is the name of the log file you want to temporarily create. + # *@sleept* is the time you want to wait before rechecking if the command has + # started yet, probably something you don't want to worry about def initialize(command : String, @log_name : String, @sleept = 0.01) # Have the command output its information to a log and after the command is # finished, append an end signal to the document diff --git a/src/interact/ripper.cr b/src/interact/ripper.cr index 2296e2f..e4c2d17 100644 --- a/src/interact/ripper.cr +++ b/src/interact/ripper.cr @@ -19,8 +19,6 @@ module Ripper # remove the extension that will be added on by ydl output_filename = output_filename.split(".")[..-2].join(".") - # TODO: update the logger for this. Explore overwriting stdout and - # injecting/removing text options = { "--output" => %("#{output_filename}.%(ext)s"), # auto-add correct ext # "--quiet" => "", diff --git a/src/interact/tagger.cr b/src/interact/tagger.cr index c441e1d..95de3a7 100644 --- a/src/interact/tagger.cr +++ b/src/interact/tagger.cr @@ -1,11 +1,17 @@ -# TODO: write comments/documentation - +# Uses FFMPEG binary to add metadata to mp3 files +# ``` +# t = Tags.new("bohem rap.mp3") +# t.add_album_art("a night at the opera album cover.jpg") +# t.add_text_tag("title", "Bohemian Rhapsody") +# t.save() +# ``` class Tags + # TODO: export this path to a config file @BIN_LOC = Path["~/.irs/bin".sub("~", Path.home)] @query_args = [] of String - + # initialize the class with an already created MP3 def initialize(@filename : String) if !File.exists?(@filename) raise "MP3 not found at location: #{@filename}" @@ -15,10 +21,8 @@ class Tags end - def add_text_tag(key : String, value : String) : Nil - @query_args.push(%(-metadata #{key}="#{value}")) - end - + # Add album art to the mp3. Album art must be added BEFORE text tags are. + # Check the usage above to see a working example. def add_album_art(image_location : String) : Nil if !File.exists?(image_location) raise "Image file not found at location: #{image_location}" @@ -33,6 +37,13 @@ class Tags @query_args.push(%(-metadata:s:v title="Album cover")) end + # Add a text tag to the mp3. If you want to see what text tags are supported, + # check out: https://wiki.multimedia.cx/index.php?title=FFmpeg_Metadata + def add_text_tag(key : String, value : String) : Nil + @query_args.push(%(-metadata #{key}="#{value}")) + end + + # Run the necessary commands to attach album art to the mp3 def save : Nil @query_args.push(%("_#{@filename}")) command = @BIN_LOC.to_s + "/ffmpeg " + @query_args.join(" ") diff --git a/src/search/spotify.cr b/src/search/spotify.cr index 7d486fb..1a67342 100644 --- a/src/search/spotify.cr +++ b/src/search/spotify.cr @@ -69,24 +69,34 @@ class SpotifySearcher points = rank_items(items, item_parameters) begin - return items[points[0][1]] + return get_item(item_type, items[points[0][1]]["id"].to_s) rescue IndexError return nil end end + # Get the complete metadata of an item based off of its id + # + # ``` + # SpotifySearcher.new().authorize(...).get_item("artist", "1dfeR4HaWDbWqFHLkxsg1d") + # ``` + def get_item(item_type : String, id : String) : JSON::Any? + url = @root_url.join("#{item_type}s/#{id}").to_s() + + response = HTTP::Client.get(url, headers: @access_header) + error_check(response) + + return JSON.parse(response.body) + end + # Find the genre of an artist based off of their id # # ``` # SpotifySearcher.new().authorize(...).find_genre("1dfeR4HaWDbWqFHLkxsg1d") # ``` def find_genre(id : String) : String - url = @root_url.join("artists/#{id}").to_s() - response = HTTP::Client.get(url, headers: @access_header) - error_check(response) - - genre = JSON.parse(response.body)["genres"][0].to_s + genre = get_item("artist", id)["genres"][0].to_s genre = genre.split(" ").map { |x| x.capitalize }.join(" ") return genre @@ -117,8 +127,9 @@ class SpotifySearcher query += param_encode(item_type, item_parameters[k]) # check if the key is to be excluded - elsif !query_exclude.includes?(k) - query += item_parameters[k].gsub(" ", "+") + "+" + elsif query_exclude.includes?(k) + next + # query += item_parameters[k].gsub(" ", "+") + "+" # if it's none of the above, treat it normally else @@ -206,10 +217,14 @@ class SpotifySearcher end -# puts SpotifySearcher.new() -# .authorize("e4198f6a3f7b48029366f22528b5dc66", -# "ba057d0621a5496bbb64edccf758bde5") -# .find_item("album", { -# "name" => "A Night At The Opera", -# "artist" => "Queen" -# }) \ No newline at end of file +puts SpotifySearcher.new() + .authorize("e4198f6a3f7b48029366f22528b5dc66", + "ba057d0621a5496bbb64edccf758bde5") + .find_item("playlist", { + "name" => "Brain Food", + "username" => "spotify" + # "name " => "A Night At The Opera", + # "artist" => "Queen" + # "track" => "Bohemian Rhapsody", + # "artist" => "Queen" + }) \ No newline at end of file