mirror of
https://github.com/iv-org/invidious.git
synced 2026-09-26 11:35:27 -05:00
Merge 6969e61f5e83138a71d1a2420e83504ccdd702e7 into c88230067b7a3abbb569ac0938bf8d897c8340be
This commit is contained in:
commit
81606dae75
@ -501,6 +501,7 @@
|
|||||||
"channel_tab_podcasts_label": "Podcasts",
|
"channel_tab_podcasts_label": "Podcasts",
|
||||||
"channel_tab_releases_label": "Releases",
|
"channel_tab_releases_label": "Releases",
|
||||||
"channel_tab_courses_label": "Courses",
|
"channel_tab_courses_label": "Courses",
|
||||||
|
"channel_tab_shows_label": "Shows",
|
||||||
"channel_tab_playlists_label": "Playlists",
|
"channel_tab_playlists_label": "Playlists",
|
||||||
"channel_tab_community_label": "Community",
|
"channel_tab_community_label": "Community",
|
||||||
"channel_tab_posts_label": "Posts",
|
"channel_tab_posts_label": "Posts",
|
||||||
|
|||||||
@ -53,3 +53,12 @@ def fetch_channel_courses(ucid, author, continuation)
|
|||||||
end
|
end
|
||||||
return extract_items(initial_data, author, ucid)
|
return extract_items(initial_data, author, ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_channel_shows(ucid, author, continuation)
|
||||||
|
if continuation
|
||||||
|
initial_data = YoutubeAPI.browse(continuation)
|
||||||
|
else
|
||||||
|
initial_data = YoutubeAPI.browse(ucid, params: "EgVzaG93c_IGBAoCYgA%3D")
|
||||||
|
end
|
||||||
|
return extract_items(initial_data, author, ucid)
|
||||||
|
end
|
||||||
|
|||||||
@ -8,6 +8,7 @@ module Invidious::Frontend::ChannelPage
|
|||||||
Podcasts
|
Podcasts
|
||||||
Releases
|
Releases
|
||||||
Courses
|
Courses
|
||||||
|
Shows
|
||||||
Playlists
|
Playlists
|
||||||
Posts
|
Posts
|
||||||
Channels
|
Channels
|
||||||
|
|||||||
@ -396,6 +396,35 @@ module Invidious::Routes::API::V1::Channels
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.shows(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_shows(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.community(env)
|
def self.community(env)
|
||||||
locale = env.get("preferences").as(Preferences).locale
|
locale = env.get("preferences").as(Preferences).locale
|
||||||
|
|
||||||
|
|||||||
@ -217,6 +217,30 @@ module Invidious::Routes::Channels
|
|||||||
templated "channel"
|
templated "channel"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.shows(env)
|
||||||
|
data = self.fetch_basic_information(env)
|
||||||
|
return data if !data.is_a?(Tuple)
|
||||||
|
|
||||||
|
locale, user, subscriptions, continuation, ucid, channel = data
|
||||||
|
|
||||||
|
if !channel.tabs.includes? "shows"
|
||||||
|
return env.redirect "/channel/#{channel.ucid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
sort_by = ""
|
||||||
|
sort_options = [] of String
|
||||||
|
|
||||||
|
items, next_continuation = fetch_channel_shows(
|
||||||
|
channel.ucid, channel.author, continuation
|
||||||
|
)
|
||||||
|
|
||||||
|
items = items.select(SearchPlaylist)
|
||||||
|
items.each(&.author = "")
|
||||||
|
|
||||||
|
selected_tab = Frontend::ChannelPage::TabsAvailable::Shows
|
||||||
|
templated "channel"
|
||||||
|
end
|
||||||
|
|
||||||
def self.community(env)
|
def self.community(env)
|
||||||
return env.redirect env.request.path.sub("posts", "community") if env.request.path.split("/").last == "posts"
|
return env.redirect env.request.path.sub("posts", "community") if env.request.path.split("/").last == "posts"
|
||||||
|
|
||||||
@ -331,7 +355,7 @@ module Invidious::Routes::Channels
|
|||||||
|
|
||||||
private KNOWN_TABS = {
|
private KNOWN_TABS = {
|
||||||
"home", "videos", "shorts", "streams", "podcasts",
|
"home", "videos", "shorts", "streams", "podcasts",
|
||||||
"releases", "courses", "playlists", "community", "channels", "about",
|
"releases", "courses", "shows", "playlists", "community", "channels", "about",
|
||||||
"posts",
|
"posts",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -387,6 +387,7 @@ module Invidious::Routes::Playlists
|
|||||||
referer = get_referer(env)
|
referer = get_referer(env)
|
||||||
|
|
||||||
plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "")
|
plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "")
|
||||||
|
plid ||= env.params.url["id"]?.try &.[2..]
|
||||||
if !plid
|
if !plid
|
||||||
return env.redirect "/"
|
return env.redirect "/"
|
||||||
end
|
end
|
||||||
|
|||||||
@ -122,6 +122,7 @@ module Invidious::Routing
|
|||||||
get "/channel/:ucid/podcasts", Routes::Channels, :podcasts
|
get "/channel/:ucid/podcasts", Routes::Channels, :podcasts
|
||||||
get "/channel/:ucid/releases", Routes::Channels, :releases
|
get "/channel/:ucid/releases", Routes::Channels, :releases
|
||||||
get "/channel/:ucid/courses", Routes::Channels, :courses
|
get "/channel/:ucid/courses", Routes::Channels, :courses
|
||||||
|
get "/channel/:ucid/shows", Routes::Channels, :shows
|
||||||
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/posts", Routes::Channels, :community
|
get "/channel/:ucid/posts", Routes::Channels, :community
|
||||||
@ -177,6 +178,7 @@ module Invidious::Routing
|
|||||||
|
|
||||||
def register_yt_playlist_routes
|
def register_yt_playlist_routes
|
||||||
get "/playlist", Routes::Playlists, :show
|
get "/playlist", Routes::Playlists, :show
|
||||||
|
get "/show/:id", Routes::Playlists, :show
|
||||||
get "/mix", Routes::Playlists, :mix
|
get "/mix", Routes::Playlists, :mix
|
||||||
get "/watch_videos", Routes::Playlists, :watch_videos
|
get "/watch_videos", Routes::Playlists, :watch_videos
|
||||||
end
|
end
|
||||||
@ -265,6 +267,7 @@ module Invidious::Routing
|
|||||||
get "/api/v1/channels/:ucid/podcasts", {{namespace}}::Channels, :podcasts
|
get "/api/v1/channels/:ucid/podcasts", {{namespace}}::Channels, :podcasts
|
||||||
get "/api/v1/channels/:ucid/releases", {{namespace}}::Channels, :releases
|
get "/api/v1/channels/:ucid/releases", {{namespace}}::Channels, :releases
|
||||||
get "/api/v1/channels/:ucid/courses", {{namespace}}::Channels, :courses
|
get "/api/v1/channels/:ucid/courses", {{namespace}}::Channels, :courses
|
||||||
|
get "/api/v1/channels/:ucid/shows", {{namespace}}::Channels, :shows
|
||||||
get "/api/v1/channels/:ucid/playlists", {{namespace}}::Channels, :playlists
|
get "/api/v1/channels/:ucid/playlists", {{namespace}}::Channels, :playlists
|
||||||
get "/api/v1/channels/:ucid/community", {{namespace}}::Channels, :community
|
get "/api/v1/channels/:ucid/community", {{namespace}}::Channels, :community
|
||||||
get "/api/v1/channels/:ucid/posts", {{namespace}}::Channels, :community
|
get "/api/v1/channels/:ucid/posts", {{namespace}}::Channels, :community
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
when .podcasts? then "/channel/#{ucid}/podcasts"
|
when .podcasts? then "/channel/#{ucid}/podcasts"
|
||||||
when .releases? then "/channel/#{ucid}/releases"
|
when .releases? then "/channel/#{ucid}/releases"
|
||||||
when .courses? then "/channel/#{ucid}/courses"
|
when .courses? then "/channel/#{ucid}/courses"
|
||||||
|
when .shows? then "/channel/#{ucid}/shows"
|
||||||
else
|
else
|
||||||
"/channel/#{ucid}"
|
"/channel/#{ucid}"
|
||||||
end
|
end
|
||||||
|
|||||||
@ -15,6 +15,7 @@ private ITEM_PARSERS = {
|
|||||||
Parsers::VideoRendererParser,
|
Parsers::VideoRendererParser,
|
||||||
Parsers::ChannelRendererParser,
|
Parsers::ChannelRendererParser,
|
||||||
Parsers::GridPlaylistRendererParser,
|
Parsers::GridPlaylistRendererParser,
|
||||||
|
Parsers::GridShowRenderer,
|
||||||
Parsers::PlaylistRendererParser,
|
Parsers::PlaylistRendererParser,
|
||||||
Parsers::CategoryRendererParser,
|
Parsers::CategoryRendererParser,
|
||||||
Parsers::ReelItemRendererParser,
|
Parsers::ReelItemRendererParser,
|
||||||
@ -338,6 +339,50 @@ private module Parsers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Parses a InnerTube gridShowRenderer into a SearchPlaylist. Returns nil when the given object isn't a gridShowRenderer
|
||||||
|
#
|
||||||
|
# A gridShowRenderer renders a playlist, that is located in a grid, to click on within the YouTube and Invidious UI.
|
||||||
|
# It is **not** the playlist itself.
|
||||||
|
#
|
||||||
|
# See specs for example.
|
||||||
|
#
|
||||||
|
# `gridShowRenderer`s can be found on the "shows" tab of channels.
|
||||||
|
#
|
||||||
|
module GridShowRenderer
|
||||||
|
extend self
|
||||||
|
include BaseParser
|
||||||
|
|
||||||
|
def process(item : JSON::Any, author_fallback : AuthorFallback)
|
||||||
|
if item_contents = item["gridShowRenderer"]?
|
||||||
|
return self.parse(item_contents, author_fallback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def parse_internal(item_contents, author_fallback)
|
||||||
|
title = extract_text(item_contents["title"]) || ""
|
||||||
|
plid = item_contents.dig?("navigationEndpoint", "commandMetadata", "webCommandMetadata", "url").try &.as_s.gsub("/show/VL", "").split("?sbp")[0] || ""
|
||||||
|
author_verified = has_verified_badge?(item_contents["ownerBadges"]?)
|
||||||
|
|
||||||
|
video_count = item_contents.dig?("thumbnailOverlays", 0, "thumbnailOverlayBottomPanelRenderer", "text", "runs", 0, "text").try(&.as_s.to_i) || 0
|
||||||
|
playlist_thumbnail = HelperExtractors.get_thumbnails(item_contents.dig("thumbnailRenderer", "showCustomThumbnailRenderer"))
|
||||||
|
|
||||||
|
SearchPlaylist.new({
|
||||||
|
title: title,
|
||||||
|
id: plid,
|
||||||
|
author: author_fallback.name,
|
||||||
|
ucid: author_fallback.id,
|
||||||
|
video_count: video_count,
|
||||||
|
videos: [] of SearchPlaylistVideo,
|
||||||
|
thumbnail: playlist_thumbnail,
|
||||||
|
author_verified: author_verified,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parser_name
|
||||||
|
return {{@type.name}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Parses a InnerTube playlistRenderer into a SearchPlaylist. Returns nil when the given object isn't a playlistRenderer
|
# Parses a InnerTube playlistRenderer into a SearchPlaylist. Returns nil when the given object isn't a playlistRenderer
|
||||||
#
|
#
|
||||||
# A playlistRenderer renders a playlist to click on within the YouTube and Invidious UI. It is **not** the playlist itself.
|
# A playlistRenderer renders a playlist to click on within the YouTube and Invidious UI. It is **not** the playlist itself.
|
||||||
@ -522,6 +567,7 @@ private module Parsers
|
|||||||
child ||= ReelItemRendererParser.process(item_contents, author_fallback)
|
child ||= ReelItemRendererParser.process(item_contents, author_fallback)
|
||||||
child ||= PlaylistRendererParser.process(item_contents, author_fallback)
|
child ||= PlaylistRendererParser.process(item_contents, author_fallback)
|
||||||
child ||= LockupViewModelParser.process(item_contents, author_fallback)
|
child ||= LockupViewModelParser.process(item_contents, author_fallback)
|
||||||
|
child ||= GridShowRenderer.process(item_contents, author_fallback)
|
||||||
child ||= ShortsLockupViewModelParser.process(item_contents, author_fallback)
|
child ||= ShortsLockupViewModelParser.process(item_contents, author_fallback)
|
||||||
return child
|
return child
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user