Add support for featured channels page
parent
a777eda66a
commit
e9dcac9bd4
@ -0,0 +1,170 @@
|
||||
struct FeaturedChannel
|
||||
include DB::Serializable
|
||||
|
||||
property author : String
|
||||
property ucid : String
|
||||
property author_thumbnail : String
|
||||
property subscriber_count : Int32
|
||||
property video_count : Int32
|
||||
property description_html : String?
|
||||
|
||||
def to_json(locale, json : JSON::Builder)
|
||||
json.object do
|
||||
json.field "author", self.author
|
||||
json.field "authorId", self.ucid
|
||||
json.field "authorUrl", "/channel/#{self.ucid}"
|
||||
json.field "authorThumbnails" do
|
||||
json.array do
|
||||
qualities = {32, 48, 76, 100, 176, 512}
|
||||
|
||||
qualities.each do |quality|
|
||||
json.object do
|
||||
json.field "url", self.author_thumbnail.gsub(/=\d+/, "=s#{quality}")
|
||||
json.field "width", quality
|
||||
json.field "height", quality
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json.field "description", html_to_content(self.description_html)
|
||||
json.field "descriptionHtml", self.description_html
|
||||
json.field "subCount", self.subscriber_count
|
||||
json.field "videoCount", self.video_count
|
||||
json.field "badges", self.badges
|
||||
end
|
||||
end
|
||||
|
||||
def to_json(locale, json : JSON::Builder | Nil = nil)
|
||||
if json
|
||||
to_json(locale, json)
|
||||
else
|
||||
JSON.build do |json|
|
||||
to_json(locale, json)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
struct Category
|
||||
include DB::Serializable
|
||||
|
||||
property title : String
|
||||
property contents : Array(FeaturedChannel) | FeaturedChannel
|
||||
property browse_endpoint_param : String?
|
||||
property continuation_token : String?
|
||||
|
||||
def to_json(locale, json : JSON::Builder)
|
||||
json.object do
|
||||
json.field "title", self.title
|
||||
json.field "contents", self.contents
|
||||
end
|
||||
end
|
||||
|
||||
def to_json(locale, json : JSON::Builder | Nil = nil)
|
||||
if json
|
||||
to_json(locale, json)
|
||||
else
|
||||
JSON.build do |json|
|
||||
to_json(locale, json)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _extract_channel_data(channel)
|
||||
ucid = channel["channelId"].as_s
|
||||
author = channel["title"]["simpleText"].as_s
|
||||
author_thumbnail = channel["thumbnail"]["thumbnails"].as_a[0]["url"].as_s
|
||||
subscriber_count = channel["subscriberCountText"]?.try &.["simpleText"]?.try &.as_s?
|
||||
.try { |text| short_text_to_number(text.split(" ")[0]) } || 0
|
||||
|
||||
video_count = channel["videoCountText"]?.try &.["runs"][0]["text"].as_s.gsub(/\D/, "").to_i || 0
|
||||
|
||||
if channel["descriptionSnippet"]?
|
||||
description = channel["descriptionSnippet"]["runs"][0]["text"].as_s
|
||||
description_html = HTML.escape(description).gsub("\n", "")
|
||||
else
|
||||
description_html = nil
|
||||
end
|
||||
|
||||
FeaturedChannel.new({
|
||||
author: author,
|
||||
ucid: ucid,
|
||||
author_thumbnail: author_thumbnail,
|
||||
subscriber_count: subscriber_count,
|
||||
video_count: video_count,
|
||||
description_html: description_html
|
||||
})
|
||||
end
|
||||
|
||||
def process_featured_channels(data, submenu_data, title=nil, continuation_items=false)
|
||||
all_categories = [] of Category
|
||||
|
||||
if submenu_data.is_a?(Bool)
|
||||
return all_categories
|
||||
end
|
||||
|
||||
# Extraction process differs when there's more than one category
|
||||
if data.size > 1
|
||||
data.each do |raw_category|
|
||||
raw_category = raw_category["itemSectionRenderer"]["contents"].as_a[0]["shelfRenderer"]
|
||||
|
||||
category_title = raw_category["title"]["runs"][0]["text"].as_s
|
||||
browse_endpoint_param = raw_category["endpoint"]["browseEndpoint"]["params"].as_s
|
||||
|
||||
# Category has multiple channels
|
||||
if raw_category["content"].as_h.has_key?("horizontalListRenderer")
|
||||
contents = [] of FeaturedChannel
|
||||
raw_category["content"]["horizontalListRenderer"]["items"].as_a.each do |channel|
|
||||
contents << _extract_channel_data(channel["gridChannelRenderer"])
|
||||
end
|
||||
# Single channel
|
||||
else
|
||||
channel = raw_category["content"]["expandedShelfContentsRenderer"]["items"][0]["channelRenderer"]
|
||||
contents = _extract_channel_data(channel)
|
||||
end
|
||||
|
||||
all_categories << Category.new({
|
||||
title: category_title,
|
||||
contents: contents,
|
||||
browse_endpoint_param: browse_endpoint_param,
|
||||
continuation_token: nil
|
||||
})
|
||||
end
|
||||
else
|
||||
if !continuation_items
|
||||
raw_category_contents = data[0]["itemSectionRenderer"]["contents"].as_a[0]["gridRenderer"]["items"].as_a
|
||||
else
|
||||
raw_category_contents = data[0].as_a
|
||||
end
|
||||
|
||||
category_title = submenu_data.try &.[0]["title"].as_s || title || ""
|
||||
|
||||
browse_endpoint_param = nil # Not needed
|
||||
continuation_token = nil
|
||||
|
||||
# If a continuation token is needed it'll always be after at least twelve channels
|
||||
if raw_category_contents.size > 12
|
||||
continuation_token = raw_category_contents[-1]["continuationItemRenderer"]?.try &.["continuationEndpoint"]["continuationCommand"]["token"].as_s || nil
|
||||
|
||||
if !continuation_token.nil?
|
||||
raw_category_contents = raw_category_contents[0..-2]
|
||||
end
|
||||
end
|
||||
|
||||
contents = [] of FeaturedChannel
|
||||
raw_category_contents.each do |channel|
|
||||
contents << _extract_channel_data(channel["gridChannelRenderer"])
|
||||
end
|
||||
|
||||
all_categories << Category.new({
|
||||
title: category_title,
|
||||
contents: contents,
|
||||
browse_endpoint_param: browse_endpoint_param,
|
||||
continuation_token: continuation_token
|
||||
})
|
||||
end
|
||||
|
||||
return all_categories
|
||||
end
|
@ -0,0 +1,115 @@
|
||||
<% content_for "header" do %>
|
||||
<title><%= channel.author %> - Invidious</title>
|
||||
<link rel="stylesheet" href="/css/channel.css?v=<%= ASSET_COMMIT %>">
|
||||
<% end %>
|
||||
|
||||
<% content_type = 4 %>
|
||||
<% sort_options = Tuple.new %>
|
||||
<%= rendered "components/channel-information" %>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<% if !featured_channel_categories.empty? %>
|
||||
<% featured_channel_categories.each do | category | %>
|
||||
<div class="channel-section pure-u-1">
|
||||
<details open="">
|
||||
<summary style="display: revert;">
|
||||
<h3 class="category-heading">
|
||||
<% if (category_request_param = category.browse_endpoint_param).is_a?(String) %>
|
||||
<a href="/channel/<%=channel.ucid%>/channels/<%=HTML.escape(category_request_param)%>">
|
||||
<%= category.title %>
|
||||
</a>
|
||||
<%else%>
|
||||
<%= category.title %>
|
||||
<%end%>
|
||||
</h3>
|
||||
</summary>
|
||||
<% contents = category.contents%>
|
||||
<div class="pure-g section-contents">
|
||||
<% if contents.is_a?(Array(FeaturedChannel)) %>
|
||||
<% contents.each do |item|%>
|
||||
<div class="channel-profile pure-u-1 pure-u-sm-1-2 pure-u-md-1-3 pure-u-lg-1-4 pure-u-xl-1-5">
|
||||
<a class="featured-channel-icon" href="/channel/<%= item.ucid %>">
|
||||
<% if !env.get("preferences").as(Preferences).thin_mode %>
|
||||
<img src="/ggpht<%= URI.parse(item.author_thumbnail).request_target.gsub(/=s\d+/, "=s176") %>"/>
|
||||
<% end %>
|
||||
</a>
|
||||
<div class="featured-channel-about">
|
||||
<p class="featured-channel-title"><a href="/channel/<%= item.ucid %>"><%= item.author %></a></p>
|
||||
<div class="featured-channel-metadata">
|
||||
<p><%= translate(locale, "`x` subscribers", number_with_separator(item.subscriber_count)) %></p>
|
||||
<p><%= translate(locale, "`x` videos", number_with_separator(item.video_count)) %></p>
|
||||
</div>
|
||||
|
||||
<% ucid = item.ucid %>
|
||||
<% author = item.author %>
|
||||
<% sub_count_text = number_to_short_text(item.subscriber_count) %>
|
||||
<%= rendered "components/subscribe_widget" %>
|
||||
</div>
|
||||
</div>
|
||||
<%end%>
|
||||
<% elsif contents.is_a?(FeaturedChannel) %>
|
||||
<%item = contents %>
|
||||
<div class="channel-profile large-featured-channel pure-u-1">
|
||||
<a class="featured-channel-icon" href="/channel/<%= item.ucid %>">
|
||||
<% if !env.get("preferences").as(Preferences).thin_mode %>
|
||||
<img src="/ggpht<%= URI.parse(item.author_thumbnail).request_target.gsub(/=s\d+/, "=s176") %>"/>
|
||||
<% end %>
|
||||
</a>
|
||||
<div class="featured-channel-about">
|
||||
<p class="featured-channel-title"><a href="/channel/<%= item.ucid %>"><%= item.author %></a></p>
|
||||
<div class="featured-channel-metadata">
|
||||
<span><%= translate(locale, "`x` subscribers", number_with_separator(item.subscriber_count)) %></span>
|
||||
<span class="seperator"> | </span>
|
||||
<span><%= translate(locale, "`x` videos", number_with_separator(item.video_count)) %></span>
|
||||
</div>
|
||||
<p class="featured-channel-description"><%= item.description_html %></p>
|
||||
|
||||
<% ucid = contents.ucid %>
|
||||
<% author = contents.author %>
|
||||
<% sub_count_text = number_to_short_text(contents.subscriber_count) %>
|
||||
<%= rendered "components/subscribe_widget" %>
|
||||
</div>
|
||||
<%end%>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<h3 class="pure-u-1">
|
||||
<%= translate(locale, "This channel doesn't feature any other channels.")%>
|
||||
</h3>
|
||||
<% end %>
|
||||
|
||||
<!-- #<div class="channel-section pure-u-1 pure-u-md-1-4 pure-u-lg-1-6"> -->
|
||||
</div>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if previous_continuation %>
|
||||
<a href="/channel/<%=channel.ucid%>/channels/<%=category_param%>?continuation=<%=HTML.escape(previous_continuation)%>&offset=<%=offset.not_nil!-1%>&title=<%=HTML.escape(title.not_nil!)%>">
|
||||
<%= translate(locale, "Previous page") %>
|
||||
</a>
|
||||
<% elsif (offset - 1) == 0 %>
|
||||
<a href="/channel/<%=channel.ucid%>/channels/<%=category_param%>">
|
||||
<%= translate(locale, "Previous page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
</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">
|
||||
<% if (next_cont_token = featured_channel_categories[0].continuation_token) %>
|
||||
<% additional_url_param = ""%>
|
||||
<% if continuation %>
|
||||
<% additional_url_param = "&previous=#{HTML.escape(continuation)}"%>
|
||||
<%end %>
|
||||
<% if !title %>
|
||||
<% title = featured_channel_categories[0].title %>
|
||||
<%end %>
|
||||
|
||||
|
||||
<a href="/channel/<%=channel.ucid%>/channels/<%=category_param%>?continuation=<%=HTML.escape(next_cont_token)%>&offset=<%=offset.not_nil!+1%>&title=<%=HTML.escape(title)%><%=additional_url_param%>">
|
||||
<%= translate(locale, "Next page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue