Add fix for playlists with less than 100 videos

This commit is contained in:
Omar Roth 2018-09-22 14:13:10 -05:00
parent d886f8d1e3
commit 35ac887133
2 changed files with 79 additions and 56 deletions

View file

@ -390,13 +390,14 @@ get "/playlist" do |env|
page = env.params.query["page"]?.try &.to_i? page = env.params.query["page"]?.try &.to_i?
page ||= 1 page ||= 1
playlist = fetch_playlist(plid)
begin begin
videos = extract_playlist(plid, page) videos = fetch_playlist_videos(plid, page, playlist.video_count)
rescue ex rescue ex
error_message = ex.message error_message = ex.message
next templated "error" next templated "error"
end end
playlist = fetch_playlist(plid)
templated "playlist" templated "playlist"
end end
@ -470,11 +471,11 @@ get "/search" do |env|
elsif subscriptions elsif subscriptions
videos = PG_DB.query_all("SELECT id,title,published,updated,ucid,author FROM ( videos = PG_DB.query_all("SELECT id,title,published,updated,ucid,author FROM (
SELECT *, SELECT *,
to_tsvector(channel_videos.title) || to_tsvector(channel_videos.title) ||
to_tsvector(channel_videos.author) to_tsvector(channel_videos.author)
as document as document
FROM channel_videos WHERE ucid IN (#{arg_array(ucids, 3)}) FROM channel_videos WHERE ucid IN (#{arg_array(ucids, 3)})
) v_search WHERE v_search.document @@ plainto_tsquery($1) LIMIT 20 OFFSET $2;", [search_query, (page - 1) * 20] + ucids, as: ChannelVideo) ) v_search WHERE v_search.document @@ plainto_tsquery($1) LIMIT 20 OFFSET $2;", [search_query, (page - 1) * 20] + ucids, as: ChannelVideo)
count = videos.size count = videos.size
else else
begin begin
@ -2822,15 +2823,15 @@ get "/api/v1/playlists/:plid" do |env|
page = env.params.query["page"]?.try &.to_i? page = env.params.query["page"]?.try &.to_i?
page ||= 1 page ||= 1
playlist = fetch_playlist(plid)
begin begin
videos = extract_playlist(plid, page) videos = fetch_playlist_videos(plid, page, playlist.video_count)
rescue ex rescue ex
error_message = {"error" => "Playlist is empty"}.to_json error_message = {"error" => "Playlist is empty"}.to_json
halt env, status_code: 404, response: error_message halt env, status_code: 404, response: error_message
end end
playlist = fetch_playlist(plid)
response = JSON.build do |json| response = JSON.build do |json|
json.object do json.object do
json.field "title", playlist.title json.field "title", playlist.title

View file

@ -25,58 +25,75 @@ class PlaylistVideo
}) })
end end
def extract_playlist(plid, page) def fetch_playlist_videos(plid, page, video_count)
index = (page - 1) * 100
url = produce_playlist_url(plid, index)
client = make_client(YT_URL) client = make_client(YT_URL)
response = client.get(url)
response = JSON.parse(response.body) if video_count > 100
if !response["content_html"]? || response["content_html"].as_s.empty? index = (page - 1) * 100
raise "Playlist does not exist" url = produce_playlist_url(plid, index)
response = client.get(url)
response = JSON.parse(response.body)
if !response["content_html"]? || response["content_html"].as_s.empty?
raise "Playlist is empty"
end
document = XML.parse_html(response["content_html"].as_s)
nodeset = document.xpath_nodes(%q(.//tr[contains(@class, "pl-video")]))
videos = extract_playlist(plid, nodeset, index)
else
if page > 1
videos = [] of PlaylistVideo
else
response = client.get("/playlist?list=#{plid}&disable_polymer=1")
document = XML.parse_html(response.body)
nodeset = document.xpath_nodes(%q(.//tr[contains(@class, "pl-video")]))
videos = extract_playlist(plid, nodeset, 0)
end
end end
return videos
end
def extract_playlist(plid, nodeset, index)
videos = [] of PlaylistVideo videos = [] of PlaylistVideo
document = XML.parse_html(response["content_html"].as_s) nodeset.each_with_index do |video, offset|
anchor = document.xpath_node(%q(//div[@class="pl-video-owner"]/a)) anchor = video.xpath_node(%q(.//td[@class="pl-video-title"]))
if anchor if !anchor
document.xpath_nodes(%q(.//tr[contains(@class, "pl-video")])).each_with_index do |video, offset| next
anchor = video.xpath_node(%q(.//td[@class="pl-video-title"]))
if !anchor
next
end
title = anchor.xpath_node(%q(.//a)).not_nil!.content.strip(" \n")
id = anchor.xpath_node(%q(.//a)).not_nil!["href"].lchop("/watch?v=")[0, 11]
anchor = anchor.xpath_node(%q(.//div[@class="pl-video-owner"]/a))
if anchor
author = anchor.content
ucid = anchor["href"].split("/")[2]
else
author = ""
ucid = ""
end
anchor = video.xpath_node(%q(.//td[@class="pl-video-time"]/div/div[1]))
if anchor && !anchor.content.empty?
length_seconds = decode_length_seconds(anchor.content)
else
length_seconds = 0
end
videos << PlaylistVideo.new(
title,
id,
author,
ucid,
length_seconds,
Time.now,
[plid],
index + offset,
)
end end
title = anchor.xpath_node(%q(.//a)).not_nil!.content.strip(" \n")
id = anchor.xpath_node(%q(.//a)).not_nil!["href"].lchop("/watch?v=")[0, 11]
anchor = anchor.xpath_node(%q(.//div[@class="pl-video-owner"]/a))
if anchor
author = anchor.content
ucid = anchor["href"].split("/")[2]
else
author = ""
ucid = ""
end
anchor = video.xpath_node(%q(.//td[@class="pl-video-time"]/div/div[1]))
if anchor && !anchor.content.empty?
length_seconds = decode_length_seconds(anchor.content)
else
length_seconds = 0
end
videos << PlaylistVideo.new(
title,
id,
author,
ucid,
length_seconds,
Time.now,
[plid],
index + offset,
)
end end
return videos return videos
@ -112,13 +129,18 @@ def produce_playlist_url(id, index)
continuation = Base64.urlsafe_encode(continuation) continuation = Base64.urlsafe_encode(continuation)
continuation = URI.escape(continuation) continuation = URI.escape(continuation)
url = "/browse_ajax?action_continuation=1&continuation=#{continuation}" url = "/browse_ajax?continuation=#{continuation}"
return url return url
end end
def fetch_playlist(plid) def fetch_playlist(plid)
client = make_client(YT_URL) client = make_client(YT_URL)
if plid.starts_with? "UC"
plid = "UU#{plid.lchop("UC")}"
end
response = client.get("/playlist?list=#{plid}&disable_polymer=1") response = client.get("/playlist?list=#{plid}&disable_polymer=1")
body = response.body.gsub(<<-END_BUTTON body = response.body.gsub(<<-END_BUTTON
<button class="yt-uix-button yt-uix-button-size-default yt-uix-button-link yt-uix-expander-head playlist-description-expander yt-uix-inlineedit-ignore-edit" type="button" onclick=";return false;"><span class="yt-uix-button-content"> less <img alt="" src="/yts/img/pixel-vfl3z5WfW.gif"> <button class="yt-uix-button yt-uix-button-size-default yt-uix-button-link yt-uix-expander-head playlist-description-expander yt-uix-inlineedit-ignore-edit" type="button" onclick=";return false;"><span class="yt-uix-button-content"> less <img alt="" src="/yts/img/pixel-vfl3z5WfW.gif">