mirror of
				https://github.com/iv-org/invidious.git
				synced 2025-10-31 12:42:09 +00:00 
			
		
		
		
	Document and reorganize code 4 chan's feat. chans
This commit is contained in:
		| @@ -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)} | # Fetches the featured channel categories of a channel | ||||||
|   # Continuation to load more channel catagories | # | ||||||
|   if continuation.is_a?(String) | # Returned as an array of Category objects containing different channels. | ||||||
|     initial_data = request_youtube_api_browse(continuation) | def fetch_channel_featured_channels(ucid) : Array(Category) | ||||||
|     items = extract_items(initial_data) |   initial_data = request_youtube_api_browse(ucid, "EghjaGFubmVscw%3D%3D") | ||||||
|     continuation_token = fetch_continuation_token(initial_data) |  | ||||||
|  |  | ||||||
|     return [Category.new({ |   channels_tab = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]) | ||||||
|       title:            query_title.not_nil!, # If continuation contents is requested then the query_title has to be passed along. |  | ||||||
|  |   # 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"]? | ||||||
|  |  | ||||||
|  |   # If the featured channel tabs lacks categories then that means the channel doesn't feature any other channels. | ||||||
|  |   if !submenu | ||||||
|  |     return [] of Category | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   # Fetches the fallback title. | ||||||
|  |   submenu_data = submenu["channelSubMenuRenderer"]["contentTypeSubMenuItems"] | ||||||
|  |   fallback_title = submenu_data.as_a.select(&.["selected"].as_bool)[0]["title"].as_s | ||||||
|  |  | ||||||
|  |   items = extract_items(initial_data) | ||||||
|  |  | ||||||
|  |   category_array = [] of Category | ||||||
|  |   items.each do |category| | ||||||
|  |     # The items can either be an Array of Categories or an Array of channels. | ||||||
|  |     if !category.is_a?(Category) | ||||||
|  |       break | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     # category.title = category.title.empty? ? fallback_title : category.title | ||||||
|  |     category_array << category | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   # 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, |       contents:         items, | ||||||
|       description_html: "", |       description_html: "", | ||||||
|       url:              nil, |       url:              nil, | ||||||
|       badges:           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 |  | ||||||
|  |  | ||||||
|     channels_tab = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]) |  | ||||||
|     submenu = channels_tab["content"]["sectionListRenderer"]["subMenu"]? |  | ||||||
|  |  | ||||||
|     # There's no submenu data if the channel doesn't feature any channels. |  | ||||||
|     if !submenu |  | ||||||
|       return {[] of Category, continuation_token} |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     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) |  | ||||||
|     category_array = [] of Category |  | ||||||
|     items.each do |category| |  | ||||||
|       # Tell compiler that the result from extract_items has to be an array of Categories |  | ||||||
|       if !category.is_a?(Category) |  | ||||||
|         next |  | ||||||
|       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, |  | ||||||
|       }) |  | ||||||
|     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 category_array.empty? |  | ||||||
|       category_array << Category.new({ |  | ||||||
|         title:            fallback_title, |  | ||||||
|         contents:         items, |  | ||||||
|         description_html: "", |  | ||||||
|         url:              url, |  | ||||||
|         badges:           nil, |  | ||||||
|       }) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     return category_array, continuation_token |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   return category_array | ||||||
| end | 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 = { |   object = { | ||||||
|     "2:string"  => "channels", |     "2:string"  => "channels", | ||||||
|     "4:varint"  => view, |     "4:varint"  => view, | ||||||
| @@ -90,3 +66,50 @@ def produce_featured_channel_browse_param(view : Int64, shelf_id : Int64) | |||||||
|  |  | ||||||
|   return browse_params |   return browse_params | ||||||
| end | 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 | ||||||
|   | |||||||
| @@ -116,36 +116,33 @@ class Invidious::Routes::Channels < Invidious::Routes::BaseRoute | |||||||
|  |  | ||||||
|     view = env.params.query["view"]? |     view = env.params.query["view"]? | ||||||
|     shelf_id = env.params.query["shelf_id"]? |     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 | ||||||
|  |     else | ||||||
|  |       offset = 0 | ||||||
|  |     end | ||||||
|  |  | ||||||
|     # Category title isn't returned when requesting a specific category or continuation data |     # Category title isn't returned when requesting a specific category or continuation data | ||||||
|     # so we have it in through a url param |     # so we have it in through a url param | ||||||
|     current_category_title = env.params.query["title"]? |     current_category_title = env.params.query["title"]? | ||||||
|  |  | ||||||
|  |     previous_continuation = env.params.query["previous"]? | ||||||
|  |  | ||||||
|     if continuation |     if continuation | ||||||
|       offset = env.params.query["offset"]? |       featured_channel_categories, continuation_token = fetch_channel_featured_channels_category_continuation(continuation, current_category_title) | ||||||
|       if offset |  | ||||||
|         offset = offset.to_i |  | ||||||
|       else |  | ||||||
|         offset = 0 |  | ||||||
|       end |  | ||||||
|  |  | ||||||
|       # Previous continuation |  | ||||||
|       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! |  | ||||||
|     elsif view && shelf_id |     elsif view && shelf_id | ||||||
|       offset = env.params.query["offset"]? |       featured_channel_categories, continuation_token = fetch_selected_channel_featuring_category(ucid, view, shelf_id) | ||||||
|       if offset |  | ||||||
|         offset = offset.to_i |  | ||||||
|       else |  | ||||||
|         offset = 0 |  | ||||||
|       end |  | ||||||
|  |  | ||||||
|       featured_channel_categories, continuation_token = fetch_channel_featured_channels(ucid, "EghjaGFubmVscw%3D%3D", view, shelf_id, continuation, current_category_title).not_nil! |  | ||||||
|     else |     else | ||||||
|       previous_continuation = nil |       continuation_token = nil | ||||||
|       offset = 0 |       featured_channel_categories = fetch_channel_featured_channels(ucid) | ||||||
|  |     end | ||||||
|  |  | ||||||
|       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 |     end | ||||||
|  |  | ||||||
|     templated "channel/featured_channels", buffer_footer: true |     templated "channel/featured_channels", buffer_footer: true | ||||||
|   | |||||||
| @@ -91,6 +91,7 @@ | |||||||
|  |  | ||||||
| <% if !featured_channel_categories.empty? %> | <% if !featured_channel_categories.empty? %> | ||||||
|     <% base_url = "/channel/#{channel.ucid}/channels?view=#{view}&shelf_id=#{shelf_id}" %> |     <% base_url = "/channel/#{channel.ucid}/channels?view=#{view}&shelf_id=#{shelf_id}" %> | ||||||
|  |  | ||||||
|     <div class="pure-g h-box"> |     <div class="pure-g h-box"> | ||||||
|         <div class="pure-u-1 pure-u-lg-1-5"> |         <div class="pure-u-1 pure-u-lg-1-5"> | ||||||
|             <% if previous_continuation %> |             <% if previous_continuation %> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 syeopite
					syeopite