frontend: Add support for shorts and livestreams
This commit is contained in:
parent
5d6abd5301
commit
6c9754e663
|
@ -404,9 +404,7 @@
|
||||||
"`x` marked it with a ❤": "`x` marked it with a ❤",
|
"`x` marked it with a ❤": "`x` marked it with a ❤",
|
||||||
"Audio mode": "Audio mode",
|
"Audio mode": "Audio mode",
|
||||||
"Video mode": "Video mode",
|
"Video mode": "Video mode",
|
||||||
"Videos": "Videos",
|
|
||||||
"Playlists": "Playlists",
|
"Playlists": "Playlists",
|
||||||
"Community": "Community",
|
|
||||||
"search_filters_title": "Filters",
|
"search_filters_title": "Filters",
|
||||||
"search_filters_date_label": "Upload date",
|
"search_filters_date_label": "Upload date",
|
||||||
"search_filters_date_option_none": "Any date",
|
"search_filters_date_option_none": "Any date",
|
||||||
|
@ -472,5 +470,10 @@
|
||||||
"crash_page_read_the_faq": "read the <a href=\"`x`\">Frequently Asked Questions (FAQ)</a>",
|
"crash_page_read_the_faq": "read the <a href=\"`x`\">Frequently Asked Questions (FAQ)</a>",
|
||||||
"crash_page_search_issue": "searched for <a href=\"`x`\">existing issues on GitHub</a>",
|
"crash_page_search_issue": "searched for <a href=\"`x`\">existing issues on GitHub</a>",
|
||||||
"crash_page_report_issue": "If none of the above helped, please <a href=\"`x`\">open a new issue on GitHub</a> (preferably in English) and include the following text in your message (do NOT translate that text):",
|
"crash_page_report_issue": "If none of the above helped, please <a href=\"`x`\">open a new issue on GitHub</a> (preferably in English) and include the following text in your message (do NOT translate that text):",
|
||||||
"error_video_not_in_playlist": "The requested video doesn't exist in this playlist. <a href=\"`x`\">Click here for the playlist home page.</a>"
|
"error_video_not_in_playlist": "The requested video doesn't exist in this playlist. <a href=\"`x`\">Click here for the playlist home page.</a>",
|
||||||
|
"channel_tab_videos_label": "Videos",
|
||||||
|
"channel_tab_shorts_label": "Shorts",
|
||||||
|
"channel_tab_streams_label": "Livestreams",
|
||||||
|
"channel_tab_playlists_label": "Playlists",
|
||||||
|
"channel_tab_community_label": "Community"
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,8 +104,14 @@ def get_about_info(ucid, locale) : AboutChannel
|
||||||
|
|
||||||
if tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?
|
if tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?
|
||||||
# Get the name of the tabs available on this channel
|
# Get the name of the tabs available on this channel
|
||||||
tab_names = tabs_json.as_a
|
tab_names = tabs_json.as_a.compact_map do |entry|
|
||||||
.compact_map(&.dig?("tabRenderer", "title").try &.as_s.downcase)
|
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")
|
# Get the currently active tab ("About")
|
||||||
about_tab = extract_selected_tab(tabs_json)
|
about_tab = extract_selected_tab(tabs_json)
|
||||||
|
|
43
src/invidious/frontend/channel_page.cr
Normal file
43
src/invidious/frontend/channel_page.cr
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
module Invidious::Frontend::ChannelPage
|
||||||
|
extend self
|
||||||
|
|
||||||
|
enum TabsAvailable
|
||||||
|
Videos
|
||||||
|
Shorts
|
||||||
|
Streams
|
||||||
|
Playlists
|
||||||
|
Community
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_tabs_links(locale : String, channel : AboutChannel, selected_tab : TabsAvailable)
|
||||||
|
return String.build(1500) do |str|
|
||||||
|
base_url = "/channel/#{channel.ucid}"
|
||||||
|
|
||||||
|
TabsAvailable.each do |tab|
|
||||||
|
# Ignore playlists, as it is not supported for auto-generated channels yet
|
||||||
|
next if (tab.playlists? && channel.auto_generated)
|
||||||
|
|
||||||
|
tab_name = tab.to_s.downcase
|
||||||
|
|
||||||
|
if channel.tabs.includes? tab_name
|
||||||
|
str << %(<div class="pure-u-1 pure-md-1-3">\n)
|
||||||
|
|
||||||
|
if tab == selected_tab
|
||||||
|
str << "\t<b>"
|
||||||
|
str << translate(locale, "channel_tab_#{tab_name}_label")
|
||||||
|
str << "</b>\n"
|
||||||
|
else
|
||||||
|
# Video tab doesn't have the last path component
|
||||||
|
url = tab.videos? ? base_url : "#{base_url}/#{tab_name}"
|
||||||
|
|
||||||
|
str << %(\t<a href=") << url << %(">)
|
||||||
|
str << translate(locale, "channel_tab_#{tab_name}_label")
|
||||||
|
str << "</a>\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
str << "</div>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -18,7 +18,7 @@ module Invidious::Routes::Channels
|
||||||
sort_options = {"last", "oldest", "newest"}
|
sort_options = {"last", "oldest", "newest"}
|
||||||
sort_by ||= "last"
|
sort_by ||= "last"
|
||||||
|
|
||||||
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)
|
||||||
items.uniq! do |item|
|
items.uniq! do |item|
|
||||||
if item.responds_to?(:title)
|
if item.responds_to?(:title)
|
||||||
item.title
|
item.title
|
||||||
|
@ -32,11 +32,59 @@ module Invidious::Routes::Channels
|
||||||
sort_options = {"newest", "oldest", "popular"}
|
sort_options = {"newest", "oldest", "popular"}
|
||||||
sort_by ||= "newest"
|
sort_by ||= "newest"
|
||||||
|
|
||||||
items, continuation = Channel::Tabs.get_60_videos(
|
# Fetch items and continuation token
|
||||||
|
items, next_continuation = Channel::Tabs.get_videos(
|
||||||
channel, continuation: continuation, sort_by: sort_by
|
channel, continuation: continuation, sort_by: sort_by
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
selected_tab = Frontend::ChannelPage::TabsAvailable::Videos
|
||||||
|
templated "channel"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.shorts(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? "shorts"
|
||||||
|
return env.redirect "/channel/#{channel.ucid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: support sort option for shorts
|
||||||
|
sort_by = ""
|
||||||
|
sort_options = [] of String
|
||||||
|
|
||||||
|
# Fetch items and continuation token
|
||||||
|
items, next_continuation = Channel::Tabs.get_shorts(
|
||||||
|
channel, continuation: continuation
|
||||||
|
)
|
||||||
|
|
||||||
|
selected_tab = Frontend::ChannelPage::TabsAvailable::Shorts
|
||||||
|
templated "channel"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.streams(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? "streams"
|
||||||
|
return env.redirect "/channel/#{channel.ucid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: support sort option for livestreams
|
||||||
|
sort_by = ""
|
||||||
|
sort_options = [] of String
|
||||||
|
|
||||||
|
# Fetch items and continuation token
|
||||||
|
items, next_continuation = Channel::Tabs.get_60_livestreams(
|
||||||
|
channel, continuation: continuation
|
||||||
|
)
|
||||||
|
|
||||||
|
selected_tab = Frontend::ChannelPage::TabsAvailable::Streams
|
||||||
templated "channel"
|
templated "channel"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -124,7 +172,7 @@ module Invidious::Routes::Channels
|
||||||
end
|
end
|
||||||
|
|
||||||
selected_tab = env.request.path.split("/")[-1]
|
selected_tab = env.request.path.split("/")[-1]
|
||||||
if ["home", "videos", "playlists", "community", "channels", "about"].includes? selected_tab
|
if {"home", "videos", "shorts", "streams", "playlists", "community", "channels", "about"}.includes? selected_tab
|
||||||
url = "/channel/#{ucid}/#{selected_tab}"
|
url = "/channel/#{ucid}/#{selected_tab}"
|
||||||
else
|
else
|
||||||
url = "/channel/#{ucid}"
|
url = "/channel/#{ucid}"
|
||||||
|
|
|
@ -115,6 +115,8 @@ module Invidious::Routing
|
||||||
get "/channel/:ucid", Routes::Channels, :home
|
get "/channel/:ucid", Routes::Channels, :home
|
||||||
get "/channel/:ucid/home", Routes::Channels, :home
|
get "/channel/:ucid/home", Routes::Channels, :home
|
||||||
get "/channel/:ucid/videos", Routes::Channels, :videos
|
get "/channel/:ucid/videos", Routes::Channels, :videos
|
||||||
|
get "/channel/:ucid/shorts", Routes::Channels, :shorts
|
||||||
|
get "/channel/:ucid/streams", Routes::Channels, :streams
|
||||||
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/about", Routes::Channels, :about
|
get "/channel/:ucid/about", Routes::Channels, :about
|
||||||
|
@ -122,7 +124,7 @@ module Invidious::Routing
|
||||||
get "/user/:user/live", Routes::Channels, :live
|
get "/user/:user/live", Routes::Channels, :live
|
||||||
get "/c/:user/live", Routes::Channels, :live
|
get "/c/:user/live", Routes::Channels, :live
|
||||||
|
|
||||||
["", "/videos", "/playlists", "/community", "/about"].each do |path|
|
{"", "/videos", "/shorts", "/streams", "/playlists", "/community", "/about"}.each do |path|
|
||||||
# /c/LinusTechTips
|
# /c/LinusTechTips
|
||||||
get "/c/:user#{path}", Routes::Channels, :brand_redirect
|
get "/c/:user#{path}", Routes::Channels, :brand_redirect
|
||||||
# /user/linustechtips | Not always the same as /c/
|
# /user/linustechtips | Not always the same as /c/
|
||||||
|
|
|
@ -64,23 +64,8 @@
|
||||||
<a href="https://redirect.invidious.io<%= env.request.path %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
<a href="https://redirect.invidious.io<%= env.request.path %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% if !channel.auto_generated %>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<%= Invidious::Frontend::ChannelPage.generate_tabs_links(locale, channel, selected_tab) %>
|
||||||
<b><%= translate(locale, "Videos") %></b>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
|
||||||
<% if channel.auto_generated %>
|
|
||||||
<b><%= translate(locale, "Playlists") %></b>
|
|
||||||
<% else %>
|
|
||||||
<a href="/channel/<%= ucid %>/playlists"><%= translate(locale, "Playlists") %></a>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
|
||||||
<% if channel.tabs.includes? "community" %>
|
|
||||||
<a href="/channel/<%= ucid %>/community"><%= translate(locale, "Community") %></a>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3"></div>
|
<div class="pure-u-1-3"></div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
|
@ -111,7 +96,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
<div class="pure-u-1 pure-u-md-4-5"></div>
|
||||||
<div class="pure-u-1 pure-u-lg-3-5"></div>
|
<div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
|
||||||
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
<% if next_continuation %>
|
||||||
|
<a href="/channel/<%= ucid %>/<%= selected_tab %>?continuation=<%= next_continuation %><% if sort_by != "last" %>&sort_by=<%= URI.encode_www_form(sort_by) %><% end %>">
|
||||||
|
<%= translate(locale, "Next page") %>
|
||||||
|
</a>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,19 +50,8 @@
|
||||||
<a href="https://redirect.invidious.io<%= env.request.resource %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
<a href="https://redirect.invidious.io<%= env.request.resource %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% if !channel.auto_generated %>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<%= Invidious::Frontend::ChannelPage.generate_tabs_links(locale, channel, Invidious::Frontend::ChannelPage::TabsAvailable::Community) %>
|
||||||
<a href="/channel/<%= channel.ucid %>"><%= translate(locale, "Videos") %></a>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
|
||||||
<a href="/channel/<%= channel.ucid %>/playlists"><%= translate(locale, "Playlists") %></a>
|
|
||||||
</div>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
|
||||||
<% if channel.tabs.includes? "community" %>
|
|
||||||
<b><%= translate(locale, "Community") %></b>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-2-3"></div>
|
<div class="pure-u-2-3"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -54,19 +54,7 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<%= Invidious::Frontend::ChannelPage.generate_tabs_links(locale, channel, Invidious::Frontend::ChannelPage::TabsAvailable::Playlists) %>
|
||||||
<a href="/channel/<%= ucid %>"><%= translate(locale, "Videos") %></a>
|
|
||||||
</div>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
|
||||||
<% if !channel.auto_generated %>
|
|
||||||
<b><%= translate(locale, "Playlists") %></b>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
|
||||||
<% if channel.tabs.includes? "community" %>
|
|
||||||
<a href="/channel/<%= ucid %>/community"><%= translate(locale, "Community") %></a>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3"></div>
|
<div class="pure-u-1-3"></div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
|
|
Loading…
Reference in a new issue