Document and reorganize code 4 chan's feat. chans

This commit is contained in:
syeopite 2021-06-25 14:49:18 -07:00
parent 56cc28bed3
commit 8d4ffb50fc
No known key found for this signature in database
GPG key ID: 6FA616E5A5294A82
3 changed files with 120 additions and 94 deletions

View file

@ -1,82 +1,58 @@
def fetch_channel_featured_channels(ucid, params, view = nil, shelf_id = nil, continuation = nil, query_title = nil) : {Array(Category), (String | Nil)}
# Continuation to load more channel catagories
if continuation.is_a?(String)
initial_data = request_youtube_api_browse(continuation)
items = extract_items(initial_data)
continuation_token = fetch_continuation_token(initial_data)
return [Category.new({
title: query_title.not_nil!, # If continuation contents is requested then the query_title has to be passed along.
contents: items,
description_html: "",
url: nil,
badges: nil,
})], continuation_token
else
url = nil
if view && shelf_id
url = "/channel/#{ucid}/channels?view=#{view}&shelf_id=#{shelf_id}"
params = produce_featured_channel_browse_param(view.to_i64, shelf_id.to_i64)
initial_data = request_youtube_api_browse(ucid, params)
continuation_token = fetch_continuation_token(initial_data)
else
initial_data = request_youtube_api_browse(ucid, params)
continuation_token = nil
end
# Fetches the featured channel categories of a channel
#
# Returned as an array of Category objects containing different channels.
def fetch_channel_featured_channels(ucid) : Array(Category)
initial_data = request_youtube_api_browse(ucid, "EghjaGFubmVscw%3D%3D")
channels_tab = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"])
# The submenu is the content type menu, and is used to select which categories to view fully.
# As a result, it contains the category title which we'll use as a fallback, since Innertube doesn't
# return the title when the channel only has one featured channel category.
submenu = channels_tab["content"]["sectionListRenderer"]["subMenu"]?
# There's no submenu data if the channel doesn't feature any channels.
# If the featured channel tabs lacks categories then that means the channel doesn't feature any other channels.
if !submenu
return {[] of Category, continuation_token}
return [] of Category
end
# Fetches the fallback title.
submenu_data = submenu["channelSubMenuRenderer"]["contentTypeSubMenuItems"]
items = extract_items(initial_data)
fallback_title = submenu_data.as_a.select(&.["selected"].as_bool)[0]["title"].as_s
# Although extract_items parsed everything into the right structs, we still have
# to fill in the title (if missing) attribute since Youtube doesn't return it when requesting
# a full category (initial)
items = extract_items(initial_data)
category_array = [] of Category
items.each do |category|
# Tell compiler that the result from extract_items has to be an array of Categories
# The items can either be an Array of Categories or an Array of channels.
if !category.is_a?(Category)
next
break
end
category_array << Category.new({
title: category.title.empty? ? fallback_title : category.title,
contents: category.contents,
description_html: category.description_html,
url: category.url,
badges: nil,
})
# category.title = category.title.empty? ? fallback_title : category.title
category_array << category
end
# If no categories has been parsed then it means two things.
# - We're currently viewing a specific channel category
# - We're currently requesting the continuation contents for that specific category.
# And since Youtube dyanmically loads more channels onto the page via JS, the data returned from InnerTube will only contains
# channels. Thus, we'll just go ahead and create one for the template to use.
# If the returned data is only an array of channels then it means that the featured channel tab only has one category.
# This is due to the fact that InnerTube uses a "gridRenderer" (an array of items) when only one category is present.
# However, as the InnerTube result is a "gridRenderer" and not an "shelfRenderer", an object representing an
# category or section on youtube, we'll lack the category title. But, the good news is that the title of the category is still stored within the submenu
# which we fetched above. We can then use all of these values together to produce a Category object.
if category_array.empty?
category_array << Category.new({
title: fallback_title,
contents: items,
description_html: "",
url: url,
url: nil,
badges: nil,
})
end
return category_array, continuation_token
end
return category_array
end
def produce_featured_channel_browse_param(view : Int64, shelf_id : Int64)
# Produces the InnerTube parameter for requesting the contents of a specific channel featuring category
private def produce_featured_channel_browse_param(view : Int64, shelf_id : Int64)
object = {
"2:string" => "channels",
"4:varint" => view,
@ -90,3 +66,50 @@ def produce_featured_channel_browse_param(view : Int64, shelf_id : Int64)
return browse_params
end
# Fetches the first set of channels from a selected channel featuring category
def fetch_selected_channel_featuring_category(ucid, view, shelf_id) : Tuple(Category, String | Nil)
category_url = "/channel/#{ucid}/channels?view=#{view}&shelf_id=#{shelf_id}"
params = produce_featured_channel_browse_param(view.to_i64, shelf_id.to_i64)
initial_data = request_youtube_api_browse(ucid, params)
channels_tab = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"])
continuation_token = fetch_continuation_token(initial_data)
# Fetches the fallback title
submenu = channels_tab["content"]["sectionListRenderer"]["subMenu"]
submenu_data = submenu["channelSubMenuRenderer"]["contentTypeSubMenuItems"]
fallback_title = submenu_data.as_a.select(&.["selected"].as_bool)[0]["title"].as_s
items = extract_items(initial_data)
# Since the returned items from InnerTube is an array of channels, (See explanation at the end of the fetch_channel_featured_channels function)
# we lack the category title attribute. However, it is still stored as a submenu data which we fetched above. We can then use all of these
# values together to produce a Category object.
return Category.new({
title: fallback_title,
contents: items,
description_html: "",
url: category_url,
badges: nil,
}), continuation_token
end
# Fetches the next set of channels within the selected channel featuring category.
# Requires the continuation token and the query_title.
#
# TODO: The query_title here is really only used for frontend rendering.
# And since it's a URL parameter we should be able to just request it directly within the template files.
def fetch_channel_featured_channels_category_continuation(continuation, query_title) : Tuple(Category, String | Nil)
initial_data = request_youtube_api_browse(continuation)
items = extract_items(initial_data)
continuation_token = fetch_continuation_token(initial_data)
return Category.new({
title: query_title.not_nil!, # If continuation contents is requested then the query_title has to be passed along.
contents: items,
description_html: "",
url: nil,
badges: nil,
}), continuation_token
end

View file

@ -114,8 +114,10 @@ module Invidious::Routes::Channels
return env.redirect "/channel/#{channel.ucid}"
end
# When a channel only has a single category it lacks the category param option so we'll handle it here.
if continuation
view = env.params.query["view"]?
shelf_id = env.params.query["shelf_id"]?
# The offset is mainly to check if we're at the first page or not and in turn whether we should have a "previous page" button or not.
offset = env.params.query["offset"]?
if offset
offset = offset.to_i
@ -123,24 +125,24 @@ module Invidious::Routes::Channels
offset = 0
end
# Previous continuation
# Category title isn't returned when requesting a specific category or continuation data
# so we have it in through a url param
current_category_title = env.params.query["title"]?
previous_continuation = env.params.query["previous"]?
featured_channel_categories, continuation_token = fetch_channel_featured_channels(ucid, "EghjaGFubmVscw%3D%3D", nil, nil, continuation, current_category_title).not_nil!
if continuation
featured_channel_categories, continuation_token = fetch_channel_featured_channels_category_continuation(continuation, current_category_title)
elsif view && shelf_id
offset = env.params.query["offset"]?
if offset
offset = offset.to_i
featured_channel_categories, continuation_token = fetch_selected_channel_featuring_category(ucid, view, shelf_id)
else
offset = 0
continuation_token = nil
featured_channel_categories = fetch_channel_featured_channels(ucid)
end
featured_channel_categories, continuation_token = fetch_channel_featured_channels(ucid, "EghjaGFubmVscw%3D%3D", view, shelf_id, continuation, current_category_title).not_nil!
else
previous_continuation = nil
offset = 0
featured_channel_categories, continuation_token = fetch_channel_featured_channels(ucid, "EghjaGFubmVscw%3D%3D", nil, nil, current_category_title).not_nil!
# If we only got a single category we'll go ahead and wrap it within an array for easier processing in the template.
if featured_channel_categories.is_a? Category
featured_channel_categories = [featured_channel_categories]
end
templated "channel/featured_channels", buffer_footer: true

View file

@ -91,6 +91,7 @@
<% if !featured_channel_categories.empty? %>
<% base_url = "/channel/#{channel.ucid}/channels?view=#{view}&shelf_id=#{shelf_id}" %>
<div class="pure-g h-box">
<div class="pure-u-1 pure-u-lg-1-5">
<% if previous_continuation %>