forked from midou/invidious
switch to innertube API for about channels (#2255)
This commit is contained in:
parent
f2b69fd812
commit
12b46bbd41
@ -1338,7 +1338,6 @@ get "/feed/channel/:ucid" do |env|
|
|||||||
description_html: description_html,
|
description_html: description_html,
|
||||||
length_seconds: 0,
|
length_seconds: 0,
|
||||||
live_now: false,
|
live_now: false,
|
||||||
paid: false,
|
|
||||||
premium: false,
|
premium: false,
|
||||||
premiere_timestamp: nil,
|
premiere_timestamp: nil,
|
||||||
})
|
})
|
||||||
@ -2154,7 +2153,6 @@ get "/api/v1/channels/:ucid" do |env|
|
|||||||
json.field "subCount", channel.sub_count
|
json.field "subCount", channel.sub_count
|
||||||
json.field "totalViews", channel.total_views
|
json.field "totalViews", channel.total_views
|
||||||
json.field "joined", channel.joined.to_unix
|
json.field "joined", channel.joined.to_unix
|
||||||
json.field "paid", channel.paid
|
|
||||||
|
|
||||||
json.field "autoGenerated", channel.auto_generated
|
json.field "autoGenerated", channel.auto_generated
|
||||||
json.field "isFamilyFriendly", channel.is_family_friendly
|
json.field "isFamilyFriendly", channel.is_family_friendly
|
||||||
|
@ -9,7 +9,6 @@ struct AboutChannel
|
|||||||
property author_thumbnail : String
|
property author_thumbnail : String
|
||||||
property banner : String?
|
property banner : String?
|
||||||
property description_html : String
|
property description_html : String
|
||||||
property paid : Bool
|
|
||||||
property total_views : Int64
|
property total_views : Int64
|
||||||
property sub_count : Int32
|
property sub_count : Int32
|
||||||
property joined : Time
|
property joined : Time
|
||||||
@ -29,29 +28,15 @@ struct AboutRelatedChannel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_about_info(ucid, locale)
|
def get_about_info(ucid, locale)
|
||||||
result = YT_POOL.client &.get("/channel/#{ucid}/about?gl=US&hl=en")
|
begin
|
||||||
if result.status_code != 200
|
# "EgVhYm91dA==" is the base64-encoded protobuf object {"2:string":"about"}
|
||||||
result = YT_POOL.client &.get("/user/#{ucid}/about?gl=US&hl=en")
|
initdata = YoutubeAPI.browse(browse_id: ucid, params: "EgVhYm91dA==")
|
||||||
|
rescue
|
||||||
|
raise InfoException.new("Could not get channel info.")
|
||||||
end
|
end
|
||||||
|
|
||||||
if md = result.headers["location"]?.try &.match(/\/channel\/(?<ucid>UC[a-zA-Z0-9_-]{22})/)
|
if initdata.dig?("alerts", 0, "alertRenderer", "type") == "ERROR"
|
||||||
raise ChannelRedirect.new(channel_id: md["ucid"])
|
raise InfoException.new(initdata["alerts"][0]["alertRenderer"]["text"]["simpleText"].as_s)
|
||||||
end
|
|
||||||
|
|
||||||
if result.status_code != 200
|
|
||||||
raise InfoException.new("This channel does not exist.")
|
|
||||||
end
|
|
||||||
|
|
||||||
about = XML.parse_html(result.body)
|
|
||||||
if about.xpath_node(%q(//div[contains(@class, "channel-empty-message")]))
|
|
||||||
raise InfoException.new("This channel does not exist.")
|
|
||||||
end
|
|
||||||
|
|
||||||
initdata = extract_initial_data(result.body)
|
|
||||||
if initdata.empty?
|
|
||||||
error_message = about.xpath_node(%q(//div[@class="yt-alert-content"])).try &.content.strip
|
|
||||||
error_message ||= translate(locale, "Could not get channel info.")
|
|
||||||
raise InfoException.new(error_message)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if browse_endpoint = initdata["onResponseReceivedActions"]?.try &.[0]?.try &.["navigateAction"]?.try &.["endpoint"]?.try &.["browseEndpoint"]?
|
if browse_endpoint = initdata["onResponseReceivedActions"]?.try &.[0]?.try &.["navigateAction"]?.try &.["endpoint"]?.try &.["browseEndpoint"]?
|
||||||
@ -76,7 +61,6 @@ def get_about_info(ucid, locale)
|
|||||||
description = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"]["simpleText"].as_s
|
description = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"]["simpleText"].as_s
|
||||||
description_html = HTML.escape(description).gsub("\n", "<br>")
|
description_html = HTML.escape(description).gsub("\n", "<br>")
|
||||||
|
|
||||||
paid = false
|
|
||||||
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
|
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
|
||||||
allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map { |a| a.as_s }
|
allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map { |a| a.as_s }
|
||||||
|
|
||||||
@ -99,9 +83,8 @@ def get_about_info(ucid, locale)
|
|||||||
description = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]?.try &.as_s? || ""
|
description = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]?.try &.as_s? || ""
|
||||||
description_html = HTML.escape(description).gsub("\n", "<br>")
|
description_html = HTML.escape(description).gsub("\n", "<br>")
|
||||||
|
|
||||||
paid = about.xpath_node(%q(//meta[@itemprop="paid"])).not_nil!["content"] == "True"
|
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
|
||||||
is_family_friendly = about.xpath_node(%q(//meta[@itemprop="isFamilyFriendly"])).not_nil!["content"] == "True"
|
allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map { |a| a.as_s }
|
||||||
allowed_regions = about.xpath_node(%q(//meta[@itemprop="regionsAllowed"])).not_nil!["content"].split(",")
|
|
||||||
|
|
||||||
related_channels = initdata["contents"]["twoColumnBrowseResultsRenderer"]
|
related_channels = initdata["contents"]["twoColumnBrowseResultsRenderer"]
|
||||||
.["secondaryContents"]?.try &.["browseSecondaryContentsRenderer"]["contents"][0]?
|
.["secondaryContents"]?.try &.["browseSecondaryContentsRenderer"]["contents"][0]?
|
||||||
@ -180,7 +163,6 @@ def get_about_info(ucid, locale)
|
|||||||
author_thumbnail: author_thumbnail,
|
author_thumbnail: author_thumbnail,
|
||||||
banner: banner,
|
banner: banner,
|
||||||
description_html: description_html,
|
description_html: description_html,
|
||||||
paid: paid,
|
|
||||||
total_views: total_views,
|
total_views: total_views,
|
||||||
sub_count: sub_count,
|
sub_count: sub_count,
|
||||||
joined: joined,
|
joined: joined,
|
||||||
|
@ -268,7 +268,6 @@ def extract_item(item : JSON::Any, author_fallback : String? = nil, author_id_fa
|
|||||||
.try &.["text"]?.try &.["simpleText"]?.try &.as_s.try { |t| decode_length_seconds(t) } || 0
|
.try &.["text"]?.try &.["simpleText"]?.try &.as_s.try { |t| decode_length_seconds(t) } || 0
|
||||||
|
|
||||||
live_now = false
|
live_now = false
|
||||||
paid = false
|
|
||||||
premium = false
|
premium = false
|
||||||
|
|
||||||
premiere_timestamp = i["upcomingEventData"]?.try &.["startTime"]?.try { |t| Time.unix(t.as_s.to_i64) }
|
premiere_timestamp = i["upcomingEventData"]?.try &.["startTime"]?.try { |t| Time.unix(t.as_s.to_i64) }
|
||||||
@ -281,8 +280,6 @@ def extract_item(item : JSON::Any, author_fallback : String? = nil, author_id_fa
|
|||||||
when "New", "4K", "CC"
|
when "New", "4K", "CC"
|
||||||
# TODO
|
# TODO
|
||||||
when "Premium"
|
when "Premium"
|
||||||
paid = true
|
|
||||||
|
|
||||||
# TODO: Potentially available as i["topStandaloneBadge"]["metadataBadgeRenderer"]
|
# TODO: Potentially available as i["topStandaloneBadge"]["metadataBadgeRenderer"]
|
||||||
premium = true
|
premium = true
|
||||||
else nil # Ignore
|
else nil # Ignore
|
||||||
@ -299,7 +296,6 @@ def extract_item(item : JSON::Any, author_fallback : String? = nil, author_id_fa
|
|||||||
description_html: description_html,
|
description_html: description_html,
|
||||||
length_seconds: length_seconds,
|
length_seconds: length_seconds,
|
||||||
live_now: live_now,
|
live_now: live_now,
|
||||||
paid: paid,
|
|
||||||
premium: premium,
|
premium: premium,
|
||||||
premiere_timestamp: premiere_timestamp,
|
premiere_timestamp: premiere_timestamp,
|
||||||
})
|
})
|
||||||
|
@ -34,7 +34,6 @@ module Invidious::Routes::Channels
|
|||||||
sort_by ||= "newest"
|
sort_by ||= "newest"
|
||||||
|
|
||||||
count, items = get_60_videos(channel.ucid, channel.author, page, channel.auto_generated, sort_by)
|
count, items = get_60_videos(channel.ucid, channel.author, page, channel.auto_generated, sort_by)
|
||||||
items.reject! &.paid
|
|
||||||
end
|
end
|
||||||
|
|
||||||
templated "channel"
|
templated "channel"
|
||||||
|
@ -10,7 +10,6 @@ struct SearchVideo
|
|||||||
property description_html : String
|
property description_html : String
|
||||||
property length_seconds : Int32
|
property length_seconds : Int32
|
||||||
property live_now : Bool
|
property live_now : Bool
|
||||||
property paid : Bool
|
|
||||||
property premium : Bool
|
property premium : Bool
|
||||||
property premiere_timestamp : Time?
|
property premiere_timestamp : Time?
|
||||||
|
|
||||||
@ -91,7 +90,6 @@ struct SearchVideo
|
|||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(self.published, locale))
|
json.field "publishedText", translate(locale, "`x` ago", recode_date(self.published, locale))
|
||||||
json.field "lengthSeconds", self.length_seconds
|
json.field "lengthSeconds", self.length_seconds
|
||||||
json.field "liveNow", self.live_now
|
json.field "liveNow", self.live_now
|
||||||
json.field "paid", self.paid
|
|
||||||
json.field "premium", self.premium
|
json.field "premium", self.premium
|
||||||
json.field "isUpcoming", self.is_upcoming
|
json.field "isUpcoming", self.is_upcoming
|
||||||
|
|
||||||
|
@ -301,7 +301,6 @@ struct Video
|
|||||||
json.field "likeCount", self.likes
|
json.field "likeCount", self.likes
|
||||||
json.field "dislikeCount", self.dislikes
|
json.field "dislikeCount", self.dislikes
|
||||||
|
|
||||||
json.field "paid", self.paid
|
|
||||||
json.field "premium", self.premium
|
json.field "premium", self.premium
|
||||||
json.field "isFamilyFriendly", self.is_family_friendly
|
json.field "isFamilyFriendly", self.is_family_friendly
|
||||||
json.field "allowedRegions", self.allowed_regions
|
json.field "allowedRegions", self.allowed_regions
|
||||||
@ -693,12 +692,6 @@ struct Video
|
|||||||
items
|
items
|
||||||
end
|
end
|
||||||
|
|
||||||
def paid
|
|
||||||
reason = info["playabilityStatus"]?.try &.["reason"]?
|
|
||||||
paid = reason == "This video requires payment to watch." ? true : false
|
|
||||||
paid
|
|
||||||
end
|
|
||||||
|
|
||||||
def premium
|
def premium
|
||||||
keywords.includes? "YouTube Red"
|
keywords.includes? "YouTube Red"
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user