Skip to content

Commit

Permalink
Channel: Render age restricted channels
Browse files Browse the repository at this point in the history
  • Loading branch information
ChunkyProgrammer committed Dec 7, 2023
1 parent 9e8baa3 commit 122cc56
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 110 deletions.
150 changes: 81 additions & 69 deletions src/invidious/channels/about.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ record AboutChannel,
is_family_friendly : Bool,
allowed_regions : Array(String),
tabs : Array(String),
verified : Bool
verified : Bool,
is_age_gated : Bool

def get_about_info(ucid, locale) : AboutChannel
begin
Expand Down Expand Up @@ -43,36 +44,91 @@ def get_about_info(ucid, locale) : AboutChannel
auto_generated = true
end

if auto_generated
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?
tab_names = [] of String
total_views = 0_i64
joined = Time.unix(0)

description_node = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"]
if ageGate = initdata.dig?("contents", "twoColumnBrowseResultsRenderer", "tabs", 0, "tabRenderer", "content", "sectionListRenderer", "contents", 0, "channelAgeGateRenderer")
description_node = nil
author = ageGate["channelTitle"].as_s
ucid = initdata["responseContext"]["serviceTrackingParams"][0]["params"][0]["value"].as_s
author_url = "https://www.youtube.com/channel/" + ucid
author_thumbnail = ageGate["avatar"]["thumbnails"][0]["url"].as_s
banner = nil
is_family_friendly = false
is_age_gated = true
tab_names = ["videos", "shorts", "streams"]
auto_generated = false
else
author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s
author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s
author_thumbnail = initdata["metadata"]["channelMetadataRenderer"]["avatar"]["thumbnails"][0]["url"].as_s
author_verified = has_verified_badge?(initdata.dig?("header", "c4TabbedHeaderRenderer", "badges"))
if auto_generated
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

ucid = initdata["metadata"]["channelMetadataRenderer"]["externalId"].as_s
# Raises a KeyError on failure.
banners = initdata["header"]["interactiveTabbedHeaderRenderer"]?.try &.["banner"]?.try &.["thumbnails"]?
banner = banners.try &.[-1]?.try &.["url"].as_s?

# Raises a KeyError on failure.
banners = initdata["header"]["c4TabbedHeaderRenderer"]?.try &.["banner"]?.try &.["thumbnails"]?
banner = banners.try &.[-1]?.try &.["url"].as_s?
description_node = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"]
else
author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s
author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s
author_thumbnail = initdata["metadata"]["channelMetadataRenderer"]["avatar"]["thumbnails"][0]["url"].as_s
author_verified = has_verified_badge?(initdata.dig?("header", "c4TabbedHeaderRenderer", "badges"))

# if banner.includes? "channels/c4/default_banner"
# banner = nil
# end
ucid = initdata["metadata"]["channelMetadataRenderer"]["externalId"].as_s

description_node = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]?
end
# Raises a KeyError on failure.
banners = initdata["header"]["c4TabbedHeaderRenderer"]?.try &.["banner"]?.try &.["thumbnails"]?
banner = banners.try &.[-1]?.try &.["url"].as_s?

is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
# if banner.includes? "channels/c4/default_banner"
# banner = nil
# end

description_node = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]?
end
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
if tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?
# Get the name of the tabs available on this channel
tab_names = tabs_json.as_a.compact_map do |entry|
name = entry.dig?("tabRenderer", "title").try &.as_s.downcase

# This is a small fix to not add extra code on the HTML side
# I.e, the URL for the "live" tab is .../streams, so use "streams"
# everywhere for the sake of simplicity
(name == "live") ? "streams" : name
end

# Get the currently active tab ("About")
about_tab = extract_selected_tab(tabs_json)

# Try to find the about metadata section
channel_about_meta = about_tab.dig?(
"content",
"sectionListRenderer", "contents", 0,
"itemSectionRenderer", "contents", 0,
"channelAboutFullMetadataRenderer"
)

if !channel_about_meta.nil?
total_views = channel_about_meta.dig?("viewCountText", "simpleText").try &.as_s.gsub(/\D/, "").to_i64? || 0_i64

# The joined text is split to several sub strings. The reduce joins those strings before parsing the date.
joined = extract_text(channel_about_meta["joinedDateText"]?)
.try { |text| Time.parse(text, "Joined %b %-d, %Y", Time::Location.local) } || Time.unix(0)

# Normal Auto-generated channels
# https://support.google.com/youtube/answer/2579942
# For auto-generated channels, channel_about_meta only has
# ["description"]["simpleText"] and ["primaryLinks"][0]["title"]["simpleText"]
auto_generated = (
(channel_about_meta["primaryLinks"]?.try &.size) == 1 && \
extract_text(channel_about_meta.dig?("primaryLinks", 0, "title")) == "Auto-generated by YouTube"
)
end
end
end

allowed_regions = initdata
.dig?("microformat", "microformatDataRenderer", "availableCountries")
Expand All @@ -91,51 +147,6 @@ def get_about_info(ucid, locale) : AboutChannel
end
end

total_views = 0_i64
joined = Time.unix(0)

tab_names = [] of String

if tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?
# Get the name of the tabs available on this channel
tab_names = tabs_json.as_a.compact_map do |entry|
name = entry.dig?("tabRenderer", "title").try &.as_s.downcase

# This is a small fix to not add extra code on the HTML side
# I.e, the URL for the "live" tab is .../streams, so use "streams"
# everywhere for the sake of simplicity
(name == "live") ? "streams" : name
end

# Get the currently active tab ("About")
about_tab = extract_selected_tab(tabs_json)

# Try to find the about metadata section
channel_about_meta = about_tab.dig?(
"content",
"sectionListRenderer", "contents", 0,
"itemSectionRenderer", "contents", 0,
"channelAboutFullMetadataRenderer"
)

if !channel_about_meta.nil?
total_views = channel_about_meta.dig?("viewCountText", "simpleText").try &.as_s.gsub(/\D/, "").to_i64? || 0_i64

# The joined text is split to several sub strings. The reduce joins those strings before parsing the date.
joined = extract_text(channel_about_meta["joinedDateText"]?)
.try { |text| Time.parse(text, "Joined %b %-d, %Y", Time::Location.local) } || Time.unix(0)

# Normal Auto-generated channels
# https://support.google.com/youtube/answer/2579942
# For auto-generated channels, channel_about_meta only has
# ["description"]["simpleText"] and ["primaryLinks"][0]["title"]["simpleText"]
auto_generated = (
(channel_about_meta["primaryLinks"]?.try &.size) == 1 && \
extract_text(channel_about_meta.dig?("primaryLinks", 0, "title")) == "Auto-generated by YouTube"
)
end
end

sub_count = initdata
.dig?("header", "c4TabbedHeaderRenderer", "subscriberCountText", "simpleText").try &.as_s?
.try { |text| short_text_to_number(text.split(" ")[0]).to_i32 } || 0
Expand All @@ -156,6 +167,7 @@ def get_about_info(ucid, locale) : AboutChannel
allowed_regions: allowed_regions,
tabs: tab_names,
verified: author_verified || false,
is_age_gated: is_age_gated || false,
)
end

Expand Down
7 changes: 7 additions & 0 deletions src/invidious/playlists.cr
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ struct PlaylistVideo
XML.build { |xml| to_xml(xml) }
end

def to_json(locale : String?, json : JSON::Builder)
to_json(json)
end

def to_json(json : JSON::Builder, index : Int32? = nil)
json.object do
json.field "type", "video"

json.field "title", self.title
json.field "videoId", self.id

Expand All @@ -67,6 +73,7 @@ struct PlaylistVideo
end

json.field "lengthSeconds", self.length_seconds
json.field "liveNow", self.live_now
end
end

Expand Down
89 changes: 67 additions & 22 deletions src/invidious/routes/api/v1/channels.cr
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,21 @@ module Invidious::Routes::API::V1::Channels
# Retrieve "sort by" setting from URL parameters
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"

begin
videos, _ = Channel::Tabs.get_videos(channel, sort_by: sort_by)
rescue ex
return error_json(500, ex)
if channel.is_age_gated
begin
playlist = get_playlist(channel.ucid.sub("UC", "UULF"))
videos = get_playlist_videos(playlist, offset: 0)
rescue ex : InfoException
# playlist doesnt exist.
videos = [] of PlaylistVideo
end
next_continuation = nil
else
begin
videos, _ = Channel::Tabs.get_videos(channel, sort_by: sort_by)
rescue ex
return error_json(500, ex)
end
end

JSON.build do |json|
Expand Down Expand Up @@ -84,6 +95,7 @@ module Invidious::Routes::API::V1::Channels
json.field "joined", channel.joined.to_unix

json.field "autoGenerated", channel.auto_generated
json.field "ageGated", channel.is_age_gated
json.field "isFamilyFriendly", channel.is_family_friendly
json.field "description", html_to_content(channel.description_html)
json.field "descriptionHtml", channel.description_html
Expand Down Expand Up @@ -141,12 +153,23 @@ module Invidious::Routes::API::V1::Channels
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"
continuation = env.params.query["continuation"]?

begin
videos, next_continuation = Channel::Tabs.get_60_videos(
channel, continuation: continuation, sort_by: sort_by
)
rescue ex
return error_json(500, ex)
if channel.is_age_gated
begin
playlist = get_playlist(channel.ucid.sub("UC", "UULF"))
videos = get_playlist_videos(playlist, offset: 0)
rescue ex : InfoException
# playlist doesnt exist.
videos = [] of PlaylistVideo
end
next_continuation = nil
else
begin
videos, next_continuation = Channel::Tabs.get_60_videos(
channel, continuation: continuation, sort_by: sort_by
)
rescue ex
return error_json(500, ex)
end
end

return JSON.build do |json|
Expand Down Expand Up @@ -175,12 +198,23 @@ module Invidious::Routes::API::V1::Channels
# Retrieve continuation from URL parameters
continuation = env.params.query["continuation"]?

begin
videos, next_continuation = Channel::Tabs.get_shorts(
channel, continuation: continuation
)
rescue ex
return error_json(500, ex)
if channel.is_age_gated
begin
playlist = get_playlist(channel.ucid.sub("UC", "UUSH"))
videos = get_playlist_videos(playlist, offset: 0)
rescue ex : InfoException
# playlist doesnt exist.
videos = [] of PlaylistVideo
end
next_continuation = nil
else
begin
videos, next_continuation = Channel::Tabs.get_shorts(
channel, continuation: continuation
)
rescue ex
return error_json(500, ex)
end
end

return JSON.build do |json|
Expand Down Expand Up @@ -209,12 +243,23 @@ module Invidious::Routes::API::V1::Channels
# Retrieve continuation from URL parameters
continuation = env.params.query["continuation"]?

begin
videos, next_continuation = Channel::Tabs.get_60_livestreams(
channel, continuation: continuation
)
rescue ex
return error_json(500, ex)
if channel.is_age_gated
begin
playlist = get_playlist(channel.ucid.sub("UC", "UULV"))
videos = get_playlist_videos(playlist, offset: 0)
rescue ex : InfoException
# playlist doesnt exist.
videos = [] of PlaylistVideo
end
next_continuation = nil
else
begin
videos, next_continuation = Channel::Tabs.get_60_livestreams(
channel, continuation: continuation
)
rescue ex
return error_json(500, ex)
end
end

return JSON.build do |json|
Expand Down
Loading

0 comments on commit 122cc56

Please sign in to comment.