|
|
@ -821,6 +821,19 @@ def get_about_info(ucid, locale)
|
|
|
|
raise ChannelRedirect.new(channel_id: browse_endpoint["browseId"].to_s)
|
|
|
|
raise ChannelRedirect.new(channel_id: browse_endpoint["browseId"].to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto_generated = false
|
|
|
|
|
|
|
|
if !initdata.has_key?("metadata")
|
|
|
|
|
|
|
|
auto_generated = true
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if auto_generated
|
|
|
|
|
|
|
|
return get_auto_generated_channel_info(initdata, about, ucid)
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return get_normal_channel_info(initdata, about)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_normal_channel_info(initdata, about)
|
|
|
|
author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s
|
|
|
|
author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s
|
|
|
|
author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s
|
|
|
|
author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s
|
|
|
|
author_thumbnail = initdata["metadata"]["channelMetadataRenderer"]["avatar"]["thumbnails"][0]["url"].as_s
|
|
|
|
author_thumbnail = initdata["metadata"]["channelMetadataRenderer"]["avatar"]["thumbnails"][0]["url"].as_s
|
|
|
@ -929,6 +942,69 @@ def get_about_info(ucid, locale)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_auto_generated_channel_info(initdata, about, ucid)
|
|
|
|
|
|
|
|
author = initdata["header"]["interactiveTabbedHeaderRenderer"]["title"]["simpleText"].as_s
|
|
|
|
|
|
|
|
author_url = initdata["microformat"]["microformatDataRenderer"]["urlCanonical"].as_s
|
|
|
|
|
|
|
|
author_thumbnail = initdata["header"]["interactiveTabbedHeaderRenderer"]["boxArt"]["thumbnails"][0]["url"].as_s
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Raises a KeyError on failure.
|
|
|
|
|
|
|
|
banners = initdata["header"]["interactiveTabbedHeaderRenderer"]?.try &.["banner"]?.try &.["thumbnails"]?
|
|
|
|
|
|
|
|
banner = banners.try &.[-1]?.try &.["url"].as_s?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
description = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"]["simpleText"].as_s
|
|
|
|
|
|
|
|
description_html = HTML.escape(description).gsub("\n", "<br>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paid = false
|
|
|
|
|
|
|
|
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
|
|
|
|
|
|
|
|
allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map { |a| a.as_s }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
related_channels = [] of AboutRelatedChannel
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
total_views = 0_i64
|
|
|
|
|
|
|
|
joined = Time.unix(0)
|
|
|
|
|
|
|
|
tabs = [] of String
|
|
|
|
|
|
|
|
auto_generated = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?.try &.as_a?
|
|
|
|
|
|
|
|
if !tabs_json.nil?
|
|
|
|
|
|
|
|
# Retrieve information from the tabs array. The index we are looking for varies between channels.
|
|
|
|
|
|
|
|
tabs_json.each do |node|
|
|
|
|
|
|
|
|
# Try to find the about section which is located in only one of the tabs.
|
|
|
|
|
|
|
|
channel_about_meta = node["tabRenderer"]?.try &.["content"]?.try &.["sectionListRenderer"]?
|
|
|
|
|
|
|
|
.try &.["contents"]?.try &.[0]?.try &.["itemSectionRenderer"]?.try &.["contents"]?
|
|
|
|
|
|
|
|
.try &.[0]?.try &.["channelAboutFullMetadataRenderer"]?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !channel_about_meta.nil?
|
|
|
|
|
|
|
|
# The joined text is split to several sub strings. The reduce joins those strings before parsing the date.
|
|
|
|
|
|
|
|
joined = channel_about_meta["joinedDateText"]?.try &.["runs"]?.try &.as_a.reduce("") { |acc, node| acc + node["text"].as_s }
|
|
|
|
|
|
|
|
.try { |text| Time.parse(text, "Joined %b %-d, %Y", Time::Location.local) } || Time.unix(0)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
tabs = tabs_json.reject { |node| node["tabRenderer"]?.nil? }.map { |node| node["tabRenderer"]["title"].as_s.downcase }
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub_count = initdata["header"]["c4TabbedHeaderRenderer"]?.try &.["subscriberCountText"]?.try &.["simpleText"]?.try &.as_s?
|
|
|
|
|
|
|
|
.try { |text| short_text_to_number(text.split(" ")[0]) } || 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AboutChannel.new({
|
|
|
|
|
|
|
|
ucid: ucid,
|
|
|
|
|
|
|
|
author: author,
|
|
|
|
|
|
|
|
auto_generated: auto_generated,
|
|
|
|
|
|
|
|
author_url: author_url,
|
|
|
|
|
|
|
|
author_thumbnail: author_thumbnail,
|
|
|
|
|
|
|
|
banner: banner,
|
|
|
|
|
|
|
|
description_html: description_html,
|
|
|
|
|
|
|
|
paid: paid,
|
|
|
|
|
|
|
|
total_views: total_views,
|
|
|
|
|
|
|
|
sub_count: sub_count,
|
|
|
|
|
|
|
|
joined: joined,
|
|
|
|
|
|
|
|
is_family_friendly: is_family_friendly,
|
|
|
|
|
|
|
|
allowed_regions: allowed_regions,
|
|
|
|
|
|
|
|
related_channels: related_channels,
|
|
|
|
|
|
|
|
tabs: tabs,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def get_channel_videos_response(ucid, page = 1, auto_generated = nil, sort_by = "newest")
|
|
|
|
def get_channel_videos_response(ucid, page = 1, auto_generated = nil, sort_by = "newest")
|
|
|
|
continuation = produce_channel_videos_continuation(ucid, page,
|
|
|
|
continuation = produce_channel_videos_continuation(ucid, page,
|
|
|
|
auto_generated: auto_generated, sort_by: sort_by, v2: true)
|
|
|
|
auto_generated: auto_generated, sort_by: sort_by, v2: true)
|
|
|
|