2019-03-30 03:00:02 +05:30
|
|
|
struct MixVideo
|
2020-07-26 20:28:50 +05:30
|
|
|
include DB::Serializable
|
|
|
|
|
|
|
|
property title : String
|
|
|
|
property id : String
|
|
|
|
property author : String
|
|
|
|
property ucid : String
|
|
|
|
property length_seconds : Int32
|
|
|
|
property index : Int32
|
|
|
|
property rdid : String
|
2018-09-29 09:42:35 +05:30
|
|
|
end
|
|
|
|
|
2019-03-30 03:00:02 +05:30
|
|
|
struct Mix
|
2020-07-26 20:28:50 +05:30
|
|
|
include DB::Serializable
|
|
|
|
|
|
|
|
property title : String
|
|
|
|
property id : String
|
|
|
|
property videos : Array(MixVideo)
|
2018-09-29 09:42:35 +05:30
|
|
|
end
|
|
|
|
|
2018-12-21 03:02:09 +05:30
|
|
|
def fetch_mix(rdid, video_id, cookies = nil, locale = nil)
|
2018-09-29 09:42:35 +05:30
|
|
|
headers = HTTP::Headers.new
|
|
|
|
|
|
|
|
if cookies
|
|
|
|
headers = cookies.add_request_headers(headers)
|
|
|
|
end
|
|
|
|
|
2020-07-26 21:20:18 +05:30
|
|
|
video_id = "CvFH_6DNRCY" if rdid.starts_with? "OLAK5uy_"
|
|
|
|
response = YT_POOL.client &.get("/watch?v=#{video_id}&list=#{rdid}&gl=US&hl=en", headers)
|
2019-07-11 17:57:42 +05:30
|
|
|
initial_data = extract_initial_data(response.body)
|
2018-09-29 09:42:35 +05:30
|
|
|
|
2019-07-11 17:57:42 +05:30
|
|
|
if !initial_data["contents"]["twoColumnWatchNextResults"]["playlist"]?
|
2020-11-30 15:29:21 +05:30
|
|
|
raise InfoException.new("Could not create mix.")
|
2018-10-08 07:41:33 +05:30
|
|
|
end
|
|
|
|
|
2019-07-11 17:57:42 +05:30
|
|
|
playlist = initial_data["contents"]["twoColumnWatchNextResults"]["playlist"]["playlist"]
|
2018-09-29 09:42:35 +05:30
|
|
|
mix_title = playlist["title"].as_s
|
|
|
|
|
|
|
|
contents = playlist["contents"].as_a
|
2019-02-18 23:13:57 +05:30
|
|
|
if contents.map { |video| video["playlistPanelVideoRenderer"]["videoId"] }.includes? video_id
|
|
|
|
until contents[0]["playlistPanelVideoRenderer"]["videoId"].as_s == video_id
|
|
|
|
contents.shift
|
|
|
|
end
|
2018-09-29 09:42:35 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
videos = [] of MixVideo
|
|
|
|
contents.each do |item|
|
|
|
|
item = item["playlistPanelVideoRenderer"]
|
|
|
|
|
|
|
|
id = item["videoId"].as_s
|
2019-02-16 04:58:54 +05:30
|
|
|
title = item["title"]?.try &.["simpleText"].as_s
|
2020-07-26 20:28:50 +05:30
|
|
|
next if !title
|
|
|
|
|
2018-09-29 09:42:35 +05:30
|
|
|
author = item["longBylineText"]["runs"][0]["text"].as_s
|
|
|
|
ucid = item["longBylineText"]["runs"][0]["navigationEndpoint"]["browseEndpoint"]["browseId"].as_s
|
|
|
|
length_seconds = decode_length_seconds(item["lengthText"]["simpleText"].as_s)
|
|
|
|
index = item["navigationEndpoint"]["watchEndpoint"]["index"].as_i
|
|
|
|
|
2020-07-26 20:28:50 +05:30
|
|
|
videos << MixVideo.new({
|
|
|
|
title: title,
|
|
|
|
id: id,
|
|
|
|
author: author,
|
|
|
|
ucid: ucid,
|
|
|
|
length_seconds: length_seconds,
|
|
|
|
index: index,
|
|
|
|
rdid: rdid,
|
|
|
|
})
|
2018-09-29 09:42:35 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
if !cookies
|
2018-12-21 03:02:09 +05:30
|
|
|
next_page = fetch_mix(rdid, videos[-1].id, response.cookies, locale)
|
2018-09-29 09:42:35 +05:30
|
|
|
videos += next_page.videos
|
|
|
|
end
|
|
|
|
|
|
|
|
videos.uniq! { |video| video.id }
|
|
|
|
videos = videos.first(50)
|
2020-07-26 20:28:50 +05:30
|
|
|
return Mix.new({
|
|
|
|
title: mix_title,
|
|
|
|
id: rdid,
|
|
|
|
videos: videos,
|
|
|
|
})
|
2018-09-29 09:42:35 +05:30
|
|
|
end
|
2018-10-08 07:41:33 +05:30
|
|
|
|
|
|
|
def template_mix(mix)
|
|
|
|
html = <<-END_HTML
|
|
|
|
<h3>
|
|
|
|
<a href="/mix?list=#{mix["mixId"]}">
|
|
|
|
#{mix["title"]}
|
|
|
|
</a>
|
|
|
|
</h3>
|
|
|
|
<div class="pure-menu pure-menu-scrollable playlist-restricted">
|
|
|
|
<ol class="pure-menu-list">
|
|
|
|
END_HTML
|
|
|
|
|
|
|
|
mix["videos"].as_a.each do |video|
|
|
|
|
html += <<-END_HTML
|
|
|
|
<li class="pure-menu-item">
|
|
|
|
<a href="/watch?v=#{video["videoId"]}&list=#{mix["mixId"]}">
|
2019-03-03 21:33:24 +05:30
|
|
|
<div class="thumbnail">
|
|
|
|
<img class="thumbnail" src="/vi/#{video["videoId"]}/mqdefault.jpg">
|
|
|
|
<p class="length">#{recode_length_seconds(video["lengthSeconds"].as_i)}</p>
|
|
|
|
</div>
|
2018-10-08 07:41:33 +05:30
|
|
|
<p style="width:100%">#{video["title"]}</p>
|
|
|
|
<p>
|
2019-05-02 06:33:39 +05:30
|
|
|
<b style="width:100%">#{video["author"]}</b>
|
2018-10-08 07:41:33 +05:30
|
|
|
</p>
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
END_HTML
|
|
|
|
end
|
|
|
|
|
|
|
|
html += <<-END_HTML
|
|
|
|
</ol>
|
|
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
END_HTML
|
|
|
|
|
|
|
|
html
|
|
|
|
end
|