@ -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 ( {
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
channels_tab = extract_selected_tab ( initial_data [ " contents " ] [ " twoColumnBrowseResultsRenderer " ] [ " tabs " ] )
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 " ]?
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
if ! submenu
return { [ ] of Category , continuation_token }
return [ ] of Category
end
end
# Fetches the fallback title.
submenu_data = submenu [ " channelSubMenuRenderer " ] [ " contentTypeSubMenuItems " ]
submenu_data = submenu [ " channelSubMenuRenderer " ] [ " contentTypeSubMenuItems " ]
fallback_title = submenu_data . as_a . select ( & . [ " selected " ] . as_bool ) [ 0 ] [ " title " ] . as_s
items = extract_items ( initial_data )
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
category_array = [ ] of Category
items . each do | 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 )
if ! category . is_a? ( Category )
next
break
end
end
category_array << Category . new ( {
# category.title = category.title.empty? ? fallback_title : category.title
title : category . title . empty? ? fallback_title : category . title ,
category_array << category
contents : category . contents ,
description_html : category . description_html ,
url : category . url ,
badges : nil ,
} )
end
end
# If no categories has been parsed then it means two things .
# If the returned data is only an array of channels then it means that the featured channel tab only has one category.
# - We're currently viewing a specific channel category
# This is due to the fact that InnerTube uses a "gridRenderer" (an array of items) when only one category is present.
# - We're currently requesting the continuation contents for that specific category.
# However, as the InnerTube result is a "gridRenderer" and not an "shelfRenderer", an object representing an
# And since Youtube dyanmically loads more channels onto the page via JS, the data returned from InnerTube will only contains
# 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
# channels. Thus, we'll just go ahead and create one for the template to use .
# which we fetched above. We can then use all of these values together to produce a Category object .
if category_array . empty?
if category_array . empty?
category_array << Category . new ( {
category_array << Category . new ( {
title : fallback_title ,
title : fallback_title ,
contents : items ,
contents : items ,
description_html : " " ,
description_html : " " ,
url : ur l,
url : ni l,
badges : nil ,
badges : nil ,
} )
} )
end
end
return category_array , continuation_token
return category_array
end
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