From dd8c74520c1cff195d4b4e7019c20a4aa51016ba Mon Sep 17 00:00:00 2001 From: Who23 <40632266+Who23@users.noreply.github.com> Date: Mon, 7 Sep 2020 18:16:31 -0400 Subject: [PATCH] URL source for albums/playlists & Youtube module improvements - Add ability to source youtube URls for albums and playlists, through the -g flag, which prompts for user input on each song - Fix the Youtube.is_valid_url function, which now actually checks whether the given URL points to an actual video --- README.md | 3 ++- src/bottle/cli.cr | 6 ++++-- src/glue/list.cr | 11 +++++++++-- src/glue/song.cr | 13 +++++++++++-- src/search/youtube.cr | 33 +++++++++++++++++++++++++++++++-- 5 files changed, 57 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9ab8d77..f77811a 100755 --- a/README.md +++ b/README.md @@ -54,7 +54,8 @@ Arguments: -s, --song Specify song name to download -A, --album Specify the album name to download -p, --playlist Specify the playlist name to download - -u, --url Specify the youtube url to download from + -u, --url Specify the youtube url to download from (for single songs only) + -g, --give-url Specify the youtube url sources while downloading (for albums or playlists only only) Examples: $ irs --song "Bohemian Rhapsody" --artist "Queen" diff --git a/src/bottle/cli.cr b/src/bottle/cli.cr index 542d510..b6b74ae 100755 --- a/src/bottle/cli.cr +++ b/src/bottle/cli.cr @@ -21,6 +21,7 @@ class CLI [["-A", "--album"], "album", "string"], [["-p", "--playlist"], "playlist", "string"], [["-u", "--url"], "url", "string"], + [["-g", "--give-url"], "give-url", "bool"], ] @args : Hash(String, String) @@ -50,6 +51,7 @@ class CLI #{Style.blue "-A, --album "} Specify the album name to download #{Style.blue "-p, --playlist "} Specify the playlist name to download #{Style.blue "-u, --url "} Specify the youtube url to download from (for single songs only) + #{Style.blue "-g, --give-url"} Specify the youtube url sources while downloading (for albums or playlists only) #{Style.bold "Examples:"} $ #{Style.green %(irs --song "Bohemian Rhapsody" --artist "Queen")} @@ -90,11 +92,11 @@ class CLI elsif @args["album"]? && @args["artist"]? a = Album.new(@args["album"], @args["artist"]) a.provide_client_keys(Config.client_key, Config.client_secret) - a.grab_it + a.grab_it(!!@args["give-url"]) elsif @args["playlist"]? && @args["artist"]? p = Playlist.new(@args["playlist"], @args["artist"]) p.provide_client_keys(Config.client_key, Config.client_secret) - p.grab_it + p.grab_it(!!@args["give-url"]) else puts Style.red("Those arguments don't do anything when used that way.") puts "Type `irs -h` to see usage." diff --git a/src/glue/list.cr b/src/glue/list.cr index 4d4d583..99c7134 100755 --- a/src/glue/list.cr +++ b/src/glue/list.cr @@ -17,6 +17,9 @@ abstract class SpotifyList "searching" => [ Style.bold("Searching for %l by %a ... \r"), Style.green("+ ") + Style.bold("%l by %a \n") + ], + "url" => [ + Style.bold("When prompted for a URL, provide a youtube URL or press enter to scrape for one\n") ] } @@ -24,11 +27,15 @@ abstract class SpotifyList end # Finds the list, and downloads all of the songs using the `Song` class - def grab_it + def grab_it(ask_url : Bool = false) if !@spotify_searcher.authorized? raise("Need to call provide_client_keys on Album or Playlist class.") end + if ask_url + outputter("url", 0) + end + outputter("searching", 0) list = find_it() outputter("searching", 1) @@ -47,7 +54,7 @@ abstract class SpotifyList song.provide_metadata(data) puts Style.bold("[#{data["track_number"]}/#{contents.size}]") - song.grab_it + song.grab_it ask_url: ask_url organize(song) diff --git a/src/glue/song.cr b/src/glue/song.cr index dba2996..b8b2e1b 100755 --- a/src/glue/song.cr +++ b/src/glue/song.cr @@ -26,7 +26,8 @@ class Song " Searching for URL ...\r", Style.green(" + ") + Style.dim("URL found \n"), " Validating URL ...\r", - Style.green(" + ") + Style.dim("URL validated \n") + Style.green(" + ") + Style.dim("URL validated \n"), + "URL?: " ], "download" => [ " Downloading video:\n", @@ -54,7 +55,7 @@ class Song # ``` # Song.new("Bohemian Rhapsody", "Queen").grab_it # ``` - def grab_it(url : (String | Nil) = nil) + def grab_it(url : (String | Nil) = nil, ask_url : Bool = false) outputter("intro", 0) if !@spotify_searcher.authorized? && !@metadata @@ -84,6 +85,14 @@ class Song data = @metadata.as(JSON::Any) @filename = data["track_number"].to_s + " - #{data["name"].to_s}.mp3" + if ask_url + outputter("url", 4) + url = gets + if !url.nil? && url.strip == "" + url = nil + end + end + if !url outputter("url", 0) url = Youtube.find_url(@song_name, @artist_name, search_terms: "lyrics") diff --git a/src/search/youtube.cr b/src/search/youtube.cr index 635286a..de5a8b1 100755 --- a/src/search/youtube.cr +++ b/src/search/youtube.cr @@ -1,6 +1,7 @@ require "http" require "xml" require "json" +require "uri" module Youtube @@ -30,8 +31,36 @@ module Youtube # => false # ``` def is_valid_url(url : String) : Bool - response = HTTP::Client.get(url) - return !(response.status_code == 404) + uri = URI.parse url + + # is it a video on youtube, with a query + query = uri.query + if uri.host != "www.youtube.com" || uri.path != "/watch" || !query + return false + end + + + queries = query.split('&') + + # find the video ID + i = 0 + while i < queries.size + if queries[i].starts_with?("v=") + vID = queries[i][2..-1] + break + end + i += 1 + end + + if !vID + return false + end + + + # this is an internal endpoint to validate the video ID + response = HTTP::Client.get "https://www.youtube.com/get_video_info?video_id=#{vID}" + + return response.body.includes?("status=ok") end # Finds a youtube url based off of the given information.