Channels: Add support for releases and podcasts tabs (#3980)

This commit is contained in:
Samantaz Fox 2023-07-16 17:35:39 +02:00
commit 598ba7bade
No known key found for this signature in database
GPG Key ID: F42821059186176E
8 changed files with 134 additions and 6 deletions

View File

@ -474,6 +474,8 @@
"channel_tab_videos_label": "Videos", "channel_tab_videos_label": "Videos",
"channel_tab_shorts_label": "Shorts", "channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Livestreams", "channel_tab_streams_label": "Livestreams",
"channel_tab_podcasts_label": "Podcasts",
"channel_tab_releases_label": "Releases",
"channel_tab_playlists_label": "Playlists", "channel_tab_playlists_label": "Playlists",
"channel_tab_community_label": "Community", "channel_tab_community_label": "Community",
"channel_tab_channels_label": "Channels" "channel_tab_channels_label": "Channels"

View File

@ -26,3 +26,21 @@ def fetch_channel_playlists(ucid, author, continuation, sort_by)
return extract_items(initial_data, author, ucid) return extract_items(initial_data, author, ucid)
end end
def fetch_channel_podcasts(ucid, author, continuation)
if continuation
initial_data = YoutubeAPI.browse(continuation)
else
initial_data = YoutubeAPI.browse(ucid, params: "Eghwb2RjYXN0c_IGBQoDugEA")
end
return extract_items(initial_data, author, ucid)
end
def fetch_channel_releases(ucid, author, continuation)
if continuation
initial_data = YoutubeAPI.browse(continuation)
else
initial_data = YoutubeAPI.browse(ucid, params: "EghyZWxlYXNlc_IGBQoDsgEA")
end
return extract_items(initial_data, author, ucid)
end

View File

@ -5,6 +5,8 @@ module Invidious::Frontend::ChannelPage
Videos Videos
Shorts Shorts
Streams Streams
Podcasts
Releases
Playlists Playlists
Community Community
Channels Channels

View File

@ -245,7 +245,7 @@ module Invidious::Routes::API::V1::Channels
channel = nil # Make the compiler happy channel = nil # Make the compiler happy
get_channel() get_channel()
items, continuation = fetch_channel_playlists(channel.ucid, channel.author, continuation, sort_by) items, next_continuation = fetch_channel_playlists(channel.ucid, channel.author, continuation, sort_by)
JSON.build do |json| JSON.build do |json|
json.object do json.object do
@ -257,7 +257,65 @@ module Invidious::Routes::API::V1::Channels
end end
end end
json.field "continuation", continuation json.field "continuation", next_continuation if next_continuation
end
end
end
def self.podcasts(env)
locale = env.get("preferences").as(Preferences).locale
env.response.content_type = "application/json"
ucid = env.params.url["ucid"]
continuation = env.params.query["continuation"]?
# Use the macro defined above
channel = nil # Make the compiler happy
get_channel()
items, next_continuation = fetch_channel_podcasts(channel.ucid, channel.author, continuation)
JSON.build do |json|
json.object do
json.field "playlists" do
json.array do
items.each do |item|
item.to_json(locale, json) if item.is_a?(SearchPlaylist)
end
end
end
json.field "continuation", next_continuation if next_continuation
end
end
end
def self.releases(env)
locale = env.get("preferences").as(Preferences).locale
env.response.content_type = "application/json"
ucid = env.params.url["ucid"]
continuation = env.params.query["continuation"]?
# Use the macro defined above
channel = nil # Make the compiler happy
get_channel()
items, next_continuation = fetch_channel_releases(channel.ucid, channel.author, continuation)
JSON.build do |json|
json.object do
json.field "playlists" do
json.array do
items.each do |item|
item.to_json(locale, json) if item.is_a?(SearchPlaylist)
end
end
end
json.field "continuation", next_continuation if next_continuation
end end
end end
end end

View File

@ -27,7 +27,7 @@ module Invidious::Routes::Channels
item.author item.author
end end
end end
items = items.select(SearchPlaylist).map(&.as(SearchPlaylist)) items = items.select(SearchPlaylist)
items.each(&.author = "") items.each(&.author = "")
else else
sort_options = {"newest", "oldest", "popular"} sort_options = {"newest", "oldest", "popular"}
@ -105,13 +105,53 @@ module Invidious::Routes::Channels
channel.ucid, channel.author, continuation, (sort_by || "last") channel.ucid, channel.author, continuation, (sort_by || "last")
) )
items = items.select(SearchPlaylist).map(&.as(SearchPlaylist)) items = items.select(SearchPlaylist)
items.each(&.author = "") items.each(&.author = "")
selected_tab = Frontend::ChannelPage::TabsAvailable::Playlists selected_tab = Frontend::ChannelPage::TabsAvailable::Playlists
templated "channel" templated "channel"
end end
def self.podcasts(env)
data = self.fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
sort_by = ""
sort_options = [] of String
items, next_continuation = fetch_channel_podcasts(
channel.ucid, channel.author, continuation
)
items = items.select(SearchPlaylist)
items.each(&.author = "")
selected_tab = Frontend::ChannelPage::TabsAvailable::Podcasts
templated "channel"
end
def self.releases(env)
data = self.fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
sort_by = ""
sort_options = [] of String
items, next_continuation = fetch_channel_releases(
channel.ucid, channel.author, continuation
)
items = items.select(SearchPlaylist)
items.each(&.author = "")
selected_tab = Frontend::ChannelPage::TabsAvailable::Releases
templated "channel"
end
def self.community(env) def self.community(env)
data = self.fetch_basic_information(env) data = self.fetch_basic_information(env)
if !data.is_a?(Tuple) if !data.is_a?(Tuple)

View File

@ -118,6 +118,8 @@ module Invidious::Routing
get "/channel/:ucid/videos", Routes::Channels, :videos get "/channel/:ucid/videos", Routes::Channels, :videos
get "/channel/:ucid/shorts", Routes::Channels, :shorts get "/channel/:ucid/shorts", Routes::Channels, :shorts
get "/channel/:ucid/streams", Routes::Channels, :streams get "/channel/:ucid/streams", Routes::Channels, :streams
get "/channel/:ucid/podcasts", Routes::Channels, :podcasts
get "/channel/:ucid/releases", Routes::Channels, :releases
get "/channel/:ucid/playlists", Routes::Channels, :playlists get "/channel/:ucid/playlists", Routes::Channels, :playlists
get "/channel/:ucid/community", Routes::Channels, :community get "/channel/:ucid/community", Routes::Channels, :community
get "/channel/:ucid/channels", Routes::Channels, :channels get "/channel/:ucid/channels", Routes::Channels, :channels
@ -228,6 +230,9 @@ module Invidious::Routing
get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home
get "/api/v1/channels/:ucid/shorts", {{namespace}}::Channels, :shorts get "/api/v1/channels/:ucid/shorts", {{namespace}}::Channels, :shorts
get "/api/v1/channels/:ucid/streams", {{namespace}}::Channels, :streams get "/api/v1/channels/:ucid/streams", {{namespace}}::Channels, :streams
get "/api/v1/channels/:ucid/podcasts", {{namespace}}::Channels, :podcasts
get "/api/v1/channels/:ucid/releases", {{namespace}}::Channels, :releases
get "/api/v1/channels/:ucid/channels", {{namespace}}::Channels, :channels get "/api/v1/channels/:ucid/channels", {{namespace}}::Channels, :channels
{% for route in {"videos", "latest", "playlists", "community", "search"} %} {% for route in {"videos", "latest", "playlists", "community", "search"} %}

View File

@ -9,6 +9,8 @@
when .streams? then "/channel/#{ucid}/streams" when .streams? then "/channel/#{ucid}/streams"
when .playlists? then "/channel/#{ucid}/playlists" when .playlists? then "/channel/#{ucid}/playlists"
when .channels? then "/channel/#{ucid}/channels" when .channels? then "/channel/#{ucid}/channels"
when .podcasts? then "/channel/#{ucid}/podcasts"
when .releases? then "/channel/#{ucid}/releases"
else else
"/channel/#{ucid}" "/channel/#{ucid}"
end end

View File

@ -408,8 +408,8 @@ private module Parsers
# Returns nil when the given object isn't a RichItemRenderer # Returns nil when the given object isn't a RichItemRenderer
# #
# A richItemRenderer seems to be a simple wrapper for a videoRenderer, used # A richItemRenderer seems to be a simple wrapper for a videoRenderer, used
# by the result page for hashtags. It is located inside a continuationItems # by the result page for hashtags and for the podcast tab on channels.
# container. # It is located inside a continuationItems container for hashtags.
# #
module RichItemRendererParser module RichItemRendererParser
def self.process(item : JSON::Any, author_fallback : AuthorFallback) def self.process(item : JSON::Any, author_fallback : AuthorFallback)
@ -421,6 +421,7 @@ private module Parsers
private def self.parse(item_contents, author_fallback) private def self.parse(item_contents, author_fallback)
child = VideoRendererParser.process(item_contents, author_fallback) child = VideoRendererParser.process(item_contents, author_fallback)
child ||= ReelItemRendererParser.process(item_contents, author_fallback) child ||= ReelItemRendererParser.process(item_contents, author_fallback)
child ||= PlaylistRendererParser.process(item_contents, author_fallback)
return child return child
end end