mirror of
https://github.com/cooperhammond/irs.git
synced 2024-12-31 18:55:28 +00:00
finished youtube searcher
This commit is contained in:
parent
ddcd611585
commit
45f4d998a4
|
@ -1,7 +1,7 @@
|
||||||
|
require "http"
|
||||||
|
require "json"
|
||||||
require "base64"
|
require "base64"
|
||||||
|
|
||||||
require "spotify"
|
|
||||||
|
|
||||||
|
|
||||||
class SpotifySearcher
|
class SpotifySearcher
|
||||||
@root_url = Path["https://api.spotify.com/v1/"]
|
@root_url = Path["https://api.spotify.com/v1/"]
|
||||||
|
@ -74,7 +74,8 @@ class SpotifySearcher
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generates url to run a GET request against
|
# Generates url to run a GET request against to the Spotify open API
|
||||||
|
# Returns a `String.`
|
||||||
private def __generate_query(item_type : String, item_parameters : Hash,
|
private def __generate_query(item_type : String, item_parameters : Hash,
|
||||||
offset : Int32, limit : Int32)
|
offset : Int32, limit : Int32)
|
||||||
query = ""
|
query = ""
|
||||||
|
@ -106,6 +107,7 @@ class SpotifySearcher
|
||||||
|
|
||||||
# Ranks the given items based off of the info from parameters.
|
# Ranks the given items based off of the info from parameters.
|
||||||
# Meant to find the item that the user desires.
|
# Meant to find the item that the user desires.
|
||||||
|
# Returns an `Array` of `Array(Int32)` or [[3, 1], [...], ...]
|
||||||
private def __rank_items(items : Array, parameters : Hash)
|
private def __rank_items(items : Array, parameters : Hash)
|
||||||
points = [] of Array(Int32)
|
points = [] of Array(Int32)
|
||||||
index = 0
|
index = 0
|
||||||
|
@ -163,7 +165,7 @@ class SpotifySearcher
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a parameter encoded for the spotify api
|
# Returns a `String` encoded for the spotify api
|
||||||
#
|
#
|
||||||
# ```
|
# ```
|
||||||
# __query_encode("album", "A Night At The Opera")
|
# __query_encode("album", "A Night At The Opera")
|
||||||
|
@ -176,10 +178,10 @@ class SpotifySearcher
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
puts SpotifySearcher.new()
|
# puts SpotifySearcher.new()
|
||||||
.authorize("e4198f6a3f7b48029366f22528b5dc66",
|
# .authorize("e4198f6a3f7b48029366f22528b5dc66",
|
||||||
"ba057d0621a5496bbb64edccf758bde5")
|
# "ba057d0621a5496bbb64edccf758bde5")
|
||||||
.find_item("track", {
|
# .find_item("album", {
|
||||||
"name" => "Bohemian Rhapsody",
|
# "name" => "A Night At The Opera",
|
||||||
"artist" => "Queen"
|
# "artist" => "Queen"
|
||||||
})
|
# })
|
119
src/search/youtube.cr
Normal file
119
src/search/youtube.cr
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
require "http"
|
||||||
|
require "xml"
|
||||||
|
|
||||||
|
|
||||||
|
module Youtube
|
||||||
|
|
||||||
|
extend self
|
||||||
|
|
||||||
|
VALID_LINK_CLASSES = [
|
||||||
|
"yt-simple-endpoint style-scope ytd-video-renderer",
|
||||||
|
"yt-uix-tile-link yt-ui-ellipsis yt-ui-ellipsis-2 yt-uix-sessionlink spf-link "
|
||||||
|
]
|
||||||
|
|
||||||
|
# Finds a youtube url based off of the given information.
|
||||||
|
# The query to youtube is constructed like this:
|
||||||
|
# "<song_name> <artist_name> <search terms>"
|
||||||
|
# If *download_first* is provided, the first link found will be downloaded.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# Youtube.find_url("Bohemian Rhapsody", "Queen")
|
||||||
|
# => "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||||
|
# ```
|
||||||
|
def find_url(song_name : String, artist_name : String, search_terms = "", download_first = false)
|
||||||
|
query = (song_name + " " + artist_name + " " + search_terms).strip.gsub(" ", "+")
|
||||||
|
|
||||||
|
url = "https://www.youtube.com/results?search_query=" + query
|
||||||
|
|
||||||
|
response = HTTP::Client.get(url)
|
||||||
|
|
||||||
|
valid_nodes = __get_video_link_nodes(response.body)
|
||||||
|
|
||||||
|
if valid_nodes.size == 0
|
||||||
|
puts "There were no results for that query."
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
root = "https://youtube.com"
|
||||||
|
|
||||||
|
return root + valid_nodes[0]["href"] if download_first
|
||||||
|
|
||||||
|
ranked = __rank_videos(song_name, artist_name, valid_nodes)
|
||||||
|
|
||||||
|
return root + valid_nodes[ranked[0][1]]["href"]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Will rank videos according to their title and the user input
|
||||||
|
# Returns an `Array` of Arrays each layed out like
|
||||||
|
# [<points>, <original index>].
|
||||||
|
private def __rank_videos(song_name, artist_name, nodes : Array(XML::Node))
|
||||||
|
points = [] of Array(Int32)
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
nodes.each do |node|
|
||||||
|
pts = 0
|
||||||
|
|
||||||
|
pts += __points_compare(song_name, node["title"])
|
||||||
|
pts += __points_compare(artist_name, node["title"])
|
||||||
|
|
||||||
|
points.push([pts, index])
|
||||||
|
index += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
points.sort!{ |a, b| b[0] <=> a[0] }
|
||||||
|
|
||||||
|
return points
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns an `Int` based off the number of points worth assigning to the
|
||||||
|
# matchiness of the string. First the strings are downcased and then all
|
||||||
|
# nonalphanumeric characters are stripped.
|
||||||
|
# If *item1* includes *item2*, return 3 pts.
|
||||||
|
# If after the items have been blanked, *item1* includes *item2*,
|
||||||
|
# return 1 pts.
|
||||||
|
# Else, return 0 pts.
|
||||||
|
private def __points_compare(item1 : String, item2 : String)
|
||||||
|
if item1.includes?(item2)
|
||||||
|
return 3
|
||||||
|
end
|
||||||
|
|
||||||
|
item1 = item1.downcase.gsub(/[^a-z0-9]/, "")
|
||||||
|
item2 = item2.downcase.gsub(/[^a-z0-9]/, "")
|
||||||
|
|
||||||
|
if item1.includes?(item2)
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Finds valid video links from a `HTTP::Client.get` request
|
||||||
|
# Returns an `Array` of `XML::Node`
|
||||||
|
private def __get_video_link_nodes(doc : String)
|
||||||
|
nodes = XML.parse(doc).xpath_nodes("//a")
|
||||||
|
valid_nodes = [] of XML::Node
|
||||||
|
|
||||||
|
nodes.each do |node|
|
||||||
|
if __video_link_node?(node)
|
||||||
|
valid_nodes.push(node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return valid_nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tests if the provided `XML::Node` has a valid link to a video
|
||||||
|
# Returns a `Bool`
|
||||||
|
private def __video_link_node?(node : XML::Node)
|
||||||
|
# If this passes, then the node links to a playlist, not a video
|
||||||
|
if node["href"]?
|
||||||
|
return false if node["href"].includes?("&list=")
|
||||||
|
end
|
||||||
|
|
||||||
|
VALID_LINK_CLASSES.each do |valid_class|
|
||||||
|
if node["class"]?
|
||||||
|
return true if node["class"].includes?(valid_class)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue