Newer
Older
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
if(!tab.tabRenderer)
continue;
const tabRenderer = tab.tabRenderer; //TODO: Check if this is ever null?
const isDefault = tabRenderer.selected;
const title = tabRenderer.title;
const content = tabRenderer.content;
if(!content)
continue; //.endpoint
const tabContentRendererName = Object.keys(content)[0];
if(!tabContentRendererName) continue;
let tabResult = undefined;
switchKey(content, {
richGridRenderer(renderer) {
tabResult = extractRichGridRenderer_Shelves(content[tabContentRendererName], contextData);
},
sectionListRenderer(renderer) {
if(!renderer.contents)
return;
tabResult = extractSectionListRenderer_Sections(renderer, contextData);
},
default() {
if(bridge.devSubmit) bridge.devSubmit("extractSingleColumnBrowseResultsRenderer_Tabs - Unknown tab renderer: " + tabContentRendererName, JSON.stringify(content));
throw new ScriptException("Unknown tab renderer: " + tabContentRendererName);
}
});
if(tabResult) {
tabResult.isDefault = !!isDefault;
tabResult.title = title;
tabs.push(tabResult);
}
}
return tabs;
}
function extractTwoColumnBrowseResultsRenderer_Tabs(renderer, contextData) {
const tabs = [];
if(!renderer.tabs)
{
if(bridge.devSubmit) bridge.devSubmit("extractTwoColumnBrowseResultsRenderer_Tabs - No tabs found", JSON.stringify(renderer));
throw new ScriptException("No tabs found");
}
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
for(let i = 0; i < renderer.tabs.length; i++) {
const tab = renderer.tabs[i];
if(!tab.tabRenderer && !tab.expandableTabRenderer)
continue;
const tabRenderer = tab.tabRenderer ?? tab.expandableTabRenderer;
const isDefault = tabRenderer.selected;
const title = tabRenderer.title;
const content = tabRenderer.content;
if(!content)
continue; //.endpoint
const tabContentRendererName = Object.keys(content)[0];
if(!tabContentRendererName) continue;
let tabResult = undefined;
switchKey(content, {
richGridRenderer(renderer) {
tabResult = extractRichGridRenderer_Shelves(renderer, contextData);
},
sectionListRenderer(renderer) {
//Channel sectioned tabs..
tabResult = extractSectionListRenderer_Sections(renderer, contextData)
},
default() {
if(bridge.devSubmit) bridge.devSubmit("extractTwoColumnBrowseResultsRenderer_Tabs - Unknown tab renderer: " + tabContentRendererName, JSON.stringify(renderer));
throw new ScriptException("Unknown tab renderer: " + tabContentRendererName);
}
});
if(tabResult) {
tabResult.isDefault = !!isDefault;
tabResult.title = title;
tabs.push(tabResult);
}
}
return tabs;
}
function extractRichGridRenderer_Shelves(richGridRenderer, contextData) {
const contents = richGridRenderer.contents;
let continuation = null;
for(let ci = 0; ci < contents.length; ci++) {
const content = contents[ci];
switchKey(content, {
richSectionRenderer(renderer) {
shelves.push(extractRichSectionRenderer_Shelf(renderer, contextData));
},
richItemRenderer(renderer) {
videos.push(extractRichItemRenderer_Video(renderer, contextData));
},
continuationItemRenderer(renderer) {
continuation = extractContinuationItemRenderer_Continuation(renderer, contextData);
},
const items = extractItemSectionRenderer_Shelves(renderer, contextData);
if(items.shelves)
shelves = shelves.concat(items.shelves);
if(items.videos)
videos = videos.concat(items.videos);
},
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
default(name) {
log("Unknown shelf/section renderer in extractRichGridRenderer_Shelves: " + name);
}
});
}
return {
shelves: shelves.filter(x=>x != null),
videos: videos.filter(x=>x != null),
continuation: continuation
};
}
function extractSectionListRenderer_Sections(sectionListRenderer, contextData) {
const contents = sectionListRenderer.contents;
let shelves = [];
let videos = [];
let channels = [];
let playlists = [];
let continuation = null;
for(let i = 0; i < contents.length; i++) {
const item = contents[i];
switchKey(item, {
itemSectionRenderer(renderer) {
const items = extractItemSectionRenderer_Shelves(renderer, contextData);
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
if(items.videos.length > 0)
videos.push(...items.videos);
if(items.channels.length > 0)
channels.push(...items.channels);
if(items.playlists.length > 0)
playlists.push(...items.playlists);
if(items.shelves)
shelves.push(...items.shelves);
},
continuationItemRenderer(renderer) {
continuation = extractContinuationItemRenderer_Continuation(renderer, contextData);
}
});
}
return {
shelves: shelves,
videos: videos,
channels: channels,
playlists: playlists,
continuation: continuation
};
}
function extractItemSectionRenderer_Shelves(itemSectionRenderer, contextData) {
const contents = itemSectionRenderer.contents ?? itemSectionRenderer.results;
let shelves = [];
let videos = [];
let channels = [];
let playlists = [];
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
contents.forEach((item)=>{
switchKey(item, {
channelFeaturedContentRenderer(renderer) {
if(renderer.items) {
let videos = switchKeyVideos(renderer.items, contextData);
if(videos && videos.length > 0) {
shelves.push({
name: "Featured",
type: "Shelf",
videos: videos
});
}
}
},
channelRenderer(renderer) {
const channel = extractChannelRenderer_AuthorLink(renderer);
if(channel)
channels.push(channel);
},
playlistRenderer(renderer) {
const playlist = extractPlaylistRenderer_Playlist(renderer);
if(playlist)
playlists.push(playlist);
},
shelfRenderer(renderer) {
const shelf = extractShelfRenderer_Shelf(renderer);
if(shelf)
shelves.push(shelf);
},
gridRenderer(renderer) {
const shelf = extractGridRenderer_Shelf(renderer, contextData);
if(shelf.playlists.length > 0)
playlists.push(...shelf.playlists);
},
continuationItemRenderer(renderer) {
const token = renderer?.continuationEndpoint?.continuationCommand?.token
if(token)
continuationToken = token;
},
default() {
const video = switchKeyVideo(item, contextData);
if(video)
videos.push(video);
}
});
});
return {
shelves: shelves.filter(x=>x != null),
videos: videos.filter(x=>x != null),
channels: channels.filter(x=>x != null),
playlists: playlists.filter(x=>x != null),
continuation: continuationToken
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
function extractGridRenderer_Shelf(gridRenderer, contextData) {
const contents = gridRenderer.items;
let shelves = [];
let videos = [];
let channels = [];
let playlists = [];
contents.forEach((item)=>{
switchKey(item, {
gridPlaylistRenderer(renderer) {
const playlist = extractPlaylistRenderer_Playlist(renderer, contextData);
if(playlist)
playlists.push(playlist);
},
default() {
const video = switchKeyVideo(item, contextData);
if(video)
videos.push(video);
}
});
});
return {
videos: videos.filter(x=>x != null),
channels: channels.filter(x=>x != null),
playlists: playlists.filter(x=>x != null)
};
}
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
function switchKeyVideos(contents, contextData) {
let videos = [];
for(let content of contents) {
const video = switchKeyVideo(content, contextData);
if(video)
videos.push(video);
}
return videos;
}
function switchKeyVideo(content, contextData) {
return switchKey(content, {
channelFeaturedContentRenderer(renderer) {
return extractVideoRenderer_Video(renderer, contextData);
},
videoRenderer(renderer) {
return extractVideoRenderer_Video(renderer, contextData);
},
compactVideoRenderer(renderer) {
return extractVideoWithContextRenderer_Video(renderer, contextData);
},
videoWithContextRenderer(renderer) {
return extractVideoWithContextRenderer_Video(renderer, contextData);
},
reelItemRenderer(renderer) {
return extractReelItemRenderer_Video(renderer, contextData);
},
adSlotRenderer(adSlot) {
return null;
},
default(name) {
return null;
}
});
}
//#endregion
//#region Element Extractors
function extractShelfRenderer_Shelf(shelfRenderer, contextData) {
let name = extractText_String(shelfRenderer.title);
return switchKey(shelfRenderer.content, {
expandedShelfContentsRenderer(renderer) {
return {
name: name,
type: "Shelf",
videos: switchKeyVideos(renderer.items)
};
},
default() {
return null;
}
});
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
}
function extractContinuationItemRenderer_Continuation(continuationItemRenderer) {
return {
url: continuationItemRenderer.continuationEndpoint.commandMetadata.apiUrl, //TODO: See if this is useful at all
token: continuationItemRenderer.continuationEndpoint.continuationCommand.token
};
}
function extractRichSectionRenderer_Shelf(sectionRenderer, contextData) {
const content = sectionRenderer.content;
return switchKey(content, {
richShelfRenderer(renderer) {
return extractRichShelfRenderer_Shelf(renderer, contextData);
},
default(name) {
log("Unknown shelf renderer in extractRichSectionRenderer_Shelf: " + name);
return null;
//throw new ScriptException("Unknown shelf renderer: " + name);
}
});
}
function extractRichShelfRenderer_Shelf(shelfRenderer, contextData) {
const shelf = {
name: extractRuns_String(shelfRenderer.title?.runs),
type: "Shelf",
videos: []
};
for(let itemi = 0; itemi < shelfRenderer.contents.length; itemi++) {
const item = shelfRenderer.contents[itemi];
switchKey(item, {
richItemRenderer(renderer) {
shelf.videos.push(extractRichItemRenderer_Video(renderer), contextData);
},
default(name) {
log("Unknown shelf renderer in extractRichShelfRenderer_Shelf: " + name);
//throw new ScriptException("Unknown item renderer: " + name);
}
});
}
shelf.videos = shelf.videos.filter(x=>x != null);
return shelf;
}
//#endregion
//#region Item Extractor
function extractRichItemRenderer_Video(itemRenderer, contextData) {
const content = itemRenderer.content;
return switchKeyVideo(content, contextData);
}
function extractVideoWithContextRenderer_Video(videoRenderer, contextData) {
const liveBadges = videoRenderer.thumbnailOverlays?.filter(x=>
x.thumbnailOverlayTimeStatusRenderer?.accessibility?.accessibilityData?.label == "LIVE");
let isLive = liveBadges != null && liveBadges.length > 0;
isLive = isLive || ((videoRenderer.badges?.filter(x=>x.metadataBadgeRenderer?.style == "BADGE_STYLE_TYPE_LIVE_NOW")?.length ?? 0) > 0)
let plannedDate = null;
if(videoRenderer.upcomingEventData?.startTime)
plannedDate = parseInt(videoRenderer.upcomingEventData.startTime);
isLive = isLive || !!plannedDate
//if(!isLive && !videoRenderer.publishedTimeText?.simpleText)
// return null; //Not a normal video
const author = (contextData && contextData.authorLink) ?
contextData.authorLink : extractVideoWithContextRenderer_AuthorLink(videoRenderer);
if(IS_TESTING)
//if(!videoRenderer?.lengthText?.runs || !videoRenderer.publishedTimeText?.runs)
// isLive = true; //If no length, live after all?
if(videoRenderer?.shortViewCountText)
viewCount = extractHumanNumber_Integer(extractText_String(videoRenderer.shortViewCountText));
else log("No viewcount found on video " + videoRenderer.videoId);
const title = (videoRenderer.headline) ? extractText_String(videoRenderer.headline) : extractText_String(videoRenderer.title);
return new PlatformVideo({
id: new PlatformID(PLATFORM, videoRenderer.videoId, config.id),
name: escapeUnicode(title),
thumbnails: extractThumbnail_Thumbnails(videoRenderer.thumbnail),
author: author,
uploadDate: plannedDate ?? parseInt(new Date().getTime() / 1000),
duration: 0,
viewCount: viewCount,
url: URL_BASE + "/watch?v=" + videoRenderer.videoId,
isLive: true,
extractType: "VideoWithContext"
});
return new PlatformVideo({
id: new PlatformID(PLATFORM, videoRenderer.videoId, config.id),
name: escapeUnicode(title),
thumbnails: extractThumbnail_Thumbnails(videoRenderer.thumbnail),
author: author,
uploadDate: parseInt(extractAgoText_Timestamp(extractText_String(videoRenderer.publishedTimeText))),
duration: extractHumanTime_Seconds(extractText_String(videoRenderer.lengthText)),
viewCount: viewCount,
url: URL_BASE + "/watch?v=" + videoRenderer.videoId,
isLive: false,
extractType: "VideoWithContext"
});
}
function extractVideoRenderer_Video(videoRenderer, contextData) {
const liveBadges = videoRenderer.badges?.filter(x=>x.metadataBadgeRenderer?.label == "LIVE");
const liveOverlays = videoRenderer.thumbnailOverlays?.filter(x=>
x.thumbnailOverlayTimeStatusRenderer?.style == "LIVE" ||
x.thumbnailOverlayTimeStatusRenderer?.accessibility?.accessibilityData?.label == "LIVE");
let isLive = (liveBadges != null && liveBadges.length > 0) ||
(liveOverlays != null && liveOverlays.length > 0);
let plannedDate = null;
if(videoRenderer.upcomingEventData?.startTime)
plannedDate = parseInt(videoRenderer.upcomingEventData.startTime);
if(plannedDate)
isLive = true;
isLive = isLive || ((videoRenderer.badges?.filter(x=>x.metadataBadgeRenderer?.style == "BADGE_STYLE_TYPE_LIVE_NOW")?.length ?? 0) > 0)
if(!isLive && !videoRenderer.publishedTimeText?.simpleText)
return null; //Not a normal video
const author = (contextData && contextData.authorLink) ?
contextData.authorLink : extractVideoRenderer_AuthorLink(videoRenderer);
if(IS_TESTING)
console.log(videoRenderer);
// if(!videoRenderer?.lengthText?.simpleText)
// isLive = true; //If no length, live after all?
if(isLive)
return new PlatformVideo({
id: new PlatformID(PLATFORM, videoRenderer.videoId, config.id),
name: escapeUnicode(extractRuns_String(videoRenderer.title.runs)),
thumbnails: extractThumbnail_Thumbnails(videoRenderer.thumbnail),
author: author,
uploadDate: plannedDate ?? parseInt(new Date().getTime()/1000),
duration: 0,
viewCount: !videoRenderer.viewCountText ? 0 : extractRuns_ViewerCount(videoRenderer.viewCountText.runs),
url: URL_BASE + "/watch?v=" + videoRenderer.videoId,
isLive: true,
extractType: "Video"
});
else
return new PlatformVideo({
id: new PlatformID(PLATFORM, videoRenderer.videoId, config.id),
name: escapeUnicode(extractRuns_String(videoRenderer.title.runs)),
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
thumbnails: extractThumbnail_Thumbnails(videoRenderer.thumbnail),
author: author,
uploadDate: parseInt(extractAgoText_Timestamp(videoRenderer.publishedTimeText.simpleText)),
duration: extractHumanTime_Seconds(videoRenderer.lengthText.simpleText),
viewCount: extractFirstNumber_Integer(extractText_String(videoRenderer.viewCountText)),
url: URL_BASE + "/watch?v=" + videoRenderer.videoId,
isLive: false,
extractType: "Video"
});
}
function extractReelItemRenderer_Video(reelItemRenderer) {
//We don't do shorts for now..
return null;
}
function extractPlaylistVideoRenderer_Video(videoRenderer, contextData) {
if(!videoRenderer.lengthText)
return null;
const author = (contextData && contextData.authorLink) ?
contextData.authorLink : extractRuns_AuthorLink(videoRenderer.shortBylineText?.runs);
if(IS_TESTING)
console.log(videoRenderer);
let date = 0;
if(videoRenderer?.publishedTimeText?.simpleText)
date = parseInt(extractAgoText_Timestamp(videoRenderer.publishedTimeText.simpleText));
return new PlatformVideo({
id: new PlatformID(PLATFORM, videoRenderer.videoId, config.id),
name: escapeUnicode(extractRuns_String(videoRenderer.title.runs)),
thumbnails: extractThumbnail_Thumbnails(videoRenderer.thumbnail),
author: author,
uploadDate: date,
duration: extractHumanTime_Seconds(extractText_String(videoRenderer.lengthText)),
viewCount: 0,//extractFirstNumber_Integer(videoRenderer.viewCountText.simpleText),
url: URL_BASE + "/watch?v=" + videoRenderer.videoId,
isLive: false,
extractType: "Video"
});
}
function extractPlaylistRenderer_Playlist(playlistRenderer, contextData) {
const author = (contextData && contextData.authorLink) ?
contextData.authorLink : extractRuns_AuthorLink(playlistRenderer.shortBylineText?.runs);
let thumbnail = (playlistRenderer.thumbnails && playlistRenderer.thumbnails.length > 0) ? extractThumbnail_BestUrl(playlistRenderer.thumbnails[0]) : null;
if(!thumbnail && playlistRenderer.thumbnail)
thumbnail = extractThumbnail_BestUrl(playlistRenderer.thumbnail);
return new PlatformPlaylist({
id: new PlatformID(PLATFORM, playlistRenderer.playlistId, config.id),
author: author,
name: extractText_String(playlistRenderer.title),
url: URL_PLAYLIST + playlistRenderer.playlistId,
videoCount: extractFirstNumber_Integer(extractText_String(playlistRenderer.videoCountText)),
});
}
function extractChannelRenderer_AuthorLink(channelRenderer) {
const id = channelRenderer.channelId;
const name = extractText_String(channelRenderer.title);
const channelUrl = extractNavigationEndpoint_Url(channelRenderer.navigationEndpoint);
let thumbUrl = extractThumbnail_BestUrl(channelRenderer.thumbnail);
if(thumbUrl.startsWith("//"))
thumbUrl = "https:" + thumbUrl;
const subscribers = extractHumanNumber_Integer(extractText_String(channelRenderer.videoCountText));
return new PlatformAuthorLink(new PlatformID(PLATFORM, id, config.id, PLATFORM_CLAIMTYPE), name, channelUrl, thumbUrl, subscribers);
}
function extractRuns_AuthorLink(runs) {
if(!runs || runs.length == 0)
return null;
const id = runs[0]?.navigationEndpoint?.browseEndpoint?.browseId;
const name = extractRuns_String(runs);
const channelUrl = extractNavigationEndpoint_Url(runs[0]?.navigationEndpoint);
const thumbUrl = null;
return new PlatformAuthorLink(new PlatformID(PLATFORM, id, config.id, PLATFORM_CLAIMTYPE), name, channelUrl, thumbUrl ?? "");
return new Thumbnails(thumbnail.thumbnails.map(x=>new Thumbnail(escapeUnicode(x.url), x.height)));
}
function extractThumbnail_BestUrl(thumbnail) {
if(!thumbnail?.thumbnails || thumbnail.thumbnails.length <= 0)
return null;
let bestUrl = thumbnail.thumbnails[0].url;
let bestHeight = thumbnail.thumbnails[0].height;
for(let thumb of thumbnail.thumbnails)
if(thumb.height > bestHeight) {
bestUrl = thumb.url;
bestHeight = thumb.height;
}
return bestUrl;
}
function extractVideoWithContextRenderer_AuthorLink(videoRenderer) {
let id = videoRenderer.channelThumbnail?.channelThumbnailWithLinkRenderer?.navigationEndpoint?.browseEndpoint?.browseId;
const name = extractRuns_String(videoRenderer.shortBylineText.runs);
const channelThumbs = videoRenderer.channelThumbnail.channelThumbnailWithLinkRenderer?.thumbnail?.thumbnails;
const thumbUrl = channelThumbs && channelThumbs.length > 0 ? channelThumbs[0].url : null;
let channelUrl = videoRenderer.channelThumbnail?.channelThumbnailWithLinkRenderer?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl;
if(channelUrl) channelUrl = URL_BASE + channelUrl;
return new PlatformAuthorLink(new PlatformID(PLATFORM, id, config.id, PLATFORM_CLAIMTYPE), name, channelUrl, thumbUrl);
}
function extractVideoRenderer_AuthorLink(videoRenderer) {
const id = videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer?.navigationEndpoint?.browseEndpoint?.browseId;
const name = extractText_String(videoRenderer.ownerText)//extractRuns_String(videoRenderer.ownerText.runs);
const channelIcon = videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer;
const thumbUrl = channelIcon.thumbnail.thumbnails[0].url;
const channelUrl = (!id) ? extractRuns_Url(videoRenderer.ownerText.runs) : URL_BASE + "/channel/" + id;
return new PlatformAuthorLink(new PlatformID(PLATFORM, id, config.id, PLATFORM_CLAIMTYPE), name, channelUrl, thumbUrl);
function extractCommentRenderer_Comment(contextUrl, commentRenderer, replyCount, replyContinuation, useLogin, useMobile) {
const authorName = extractText_String(commentRenderer.authorText) ?? "";
const authorEndpoint = commentRenderer.authorEndpoint?.commandMetadata?.webCommandMetadata?.url ?? "";
const authorThumbnail = (commentRenderer.authorThumbnail?.thumbnails ?
commentRenderer.authorThumbnail.thumbnails[commentRenderer.authorThumbnail.thumbnails.length - 1].url :
""
);
return new YTComment({
contextUrl: contextUrl,
author: new PlatformAuthorLink(new PlatformID(PLATFORM, null, config.id, PLATFORM_CLAIMTYPE), authorName, URL_BASE + authorEndpoint, authorThumbnail),
message: extractRuns_String(commentRenderer.contentText?.runs) ?? "",
rating: new RatingLikes(commentRenderer?.voteCount?.simpleText ? extractHumanNumber_Integer(commentRenderer.voteCount.simpleText) : 0),
date: (commentRenderer.publishedTimeText?.runs ? extractAgoTextRuns_Timestamp(commentRenderer.publishedTimeText.runs) : 0),
replyCount: replyCount ?? 0,
context: { replyContinuation: replyContinuation, useLogin: useLogin + "", useMobile: useMobile + "" }
function convertIfOtherUrl(url) {
url = convertIfShortUrl(url);
url = convertIfEmbedUrl(url);
Kelvin
committed
url = convertIfMusicUrl(url);
return url;
}
function convertIfMusicUrl(url) {
const musicMatch = url.match(REGEX_VIDEO_URL_DESKTOP);
if(musicMatch && musicMatch.length == 3 && musicMatch[1]?.toLowerCase() == "music")
url = URL_BASE + "/watch?v=" + musicMatch[1];
return url;
}
function convertIfEmbedUrl(url) {
const embedMatch = url.match(REGEX_VIDEO_URL_EMBED);
if(embedMatch && embedMatch.length == 3) {
let id = embedMatch[2];
if(id.indexOf("?") > 0)
id = id.substring(0, id.indexOf("?"));
url = URL_BASE + "/watch?v=" + id;
}
return url;
}
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
function convertIfShortUrl(url) {
const shortMatch = url.match(REGEX_VIDEO_URL_SHORT);
if(shortMatch && shortMatch.length == 3) {
let id = shortMatch[2];
if(id.indexOf("?") > 0)
id = id.substring(0, id.indexOf("?"));
url = URL_BASE + "/watch?v=" + id;
}
return url;
}
//#region Basic Extractors
function extractText_String(item) {
if(typeof item === 'string')
return item;
if(item?.simpleText)
return item.simpleText;
if(item?.runs)
return extractRuns_String(item.runs);
if(item)
log("Unknown string object: " + JSON.stringify(item, null, " "));
return null;
}
function extractRuns_String(runs) {
if(!runs)
return null;
let str = "";
for(let runi = 0; runi < runs.length; runi++) {
const run = runs[runi];
if(run.text)
str += run.text;
else if(run.emoji?.image?.accessibility?.accessibilityData?.label)
str += "__" + run.emoji?.image?.accessibility?.accessibilityData?.label + "__"
}
return str;
}
function extractRuns_Html(runs) {
if(!runs)
return null;
let str = "";
for(let runi = 0; runi < runs.length; runi++) {
const run = runs[runi];
if(run.text)
str += run.text;
}
return str;
}
function extractRuns_Url(runs) {
for(let runi = 0; runi < runs.length; runi++) {
const run = runs[runi];
if(run.navigationEndpoint && run.navigationEndpoint.browseEndpoint && run.navigationEndpoint.browseEndpoint.canonicalBaseUrl)
return URL_BASE + run.navigationEndpoint.browseEndpoint.canonicalBaseUrl;
}
}
function extractNavigationEndpoint_Url(navEndpoint, baseUrl) {
if(!baseUrl)
baseUrl = URL_BASE;
if(!navEndpoint)
return null;
if(navEndpoint?.browseEndpoint?.browseId && navEndpoint?.browseEndpoint?.canonicalBaseUrl && navEndpoint.browseEndpoint.canonicalBaseUrl.startsWith("/@"))
return baseUrl + "/channel/" + navEndpoint?.browseEndpoint?.browseId;
if(navEndpoint?.browseEndpoint?.canonicalBaseUrl)
return baseUrl + navEndpoint?.browseEndpoint?.canonicalBaseUrl;
if(navEndpoint.commandMetadata?.webCommandMetadata?.url)
return baseUrl + navEndpoint.commandMetadata?.webCommandMetadata?.url;
return null;
}
function extractAgoTextRuns_Timestamp(runs) {
const runStr = (typeof runs === "string") ? runs : extractRuns_String(runs);
return extractAgoText_Timestamp(runStr);
}
function extractAgoText_Timestamp(str) {
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
const match = str.match(REGEX_HUMAN_AGO);
if(!match)
return 0;
const value = parseInt(match[1]);
const now = parseInt(new Date().getTime() / 1000);
switch(match[2]) {
case "second":
case "seconds":
return now - value;
case "minute":
case "minutes":
return now - value * 60;
case "hour":
case "hours":
return now - value * 60 * 60;
case "day":
case "days":
return now - value * 60 * 60 * 24;
case "week":
case "weeks":
return now - value * 60 * 60 * 24 * 7;
case "month":
case "months":
return now - value * 60 * 60 * 24 * 30; //For now it will suffice
case "year":
case "years":
return now - value * 60 * 60 * 24 * 365;
default:
if(bridge.devSubmit) bridge.devSubmit("extractAgoText_Timestamp - Unknown time type: " + match[2], match[2]);
throw new ScriptException("Unknown time type: " + match[2]);
}
}
function extractRuns_ViewerCount(runs) {
if(runs && runs.length > 0) {
const item = runs[0].text.replaceAll(".", "").replaceAll(",", "");
if(isNaN(item))
return -1;
return parseInt(item);
}
return -1;
}
function extractHumanTime_Seconds(str) {
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
if(str.indexOf(" ") >= 0)
str = str.split(" ")[0];
const parts = str.split(":");
let scale = 1;
let seconds = 0;
for(let i = parts.length-1; i >= 0; i--) {
if(isNaN(parts[i]))
return seconds;
seconds += parseInt(parts[i]) * scale;
scale *= 60;
}
return parseInt(seconds);
}
function extractFirstNumber_Integer(str) {
if(str) {
const parts = str.split(' ');
if(parts && parts.length > 0) {
const num = parts[0].replaceAll(".","").replaceAll(",","");
if(isNaN(num))
return -1;
return parseInt(num);
}
}
return -1;
}
function extractHumanNumber_Integer(str) {
if(!str)
return -1;
const match = str.match(REGEX_HUMAN_NUMBER);
if(!match)
return extractFirstNumber_Integer(str);
const value = parseFloat(match[1]);
switch(match[2]) {
case "T":
return parseInt(1000000000000 * value);
case "B":
return parseInt(1000000000 * value);
case "M":
return parseInt(1000000 * value);
case "K":
return parseInt(1000 * value);
default:
return parseInt(value);
}
}
function extractDate_Timestamp(dateStr) {
if(!dateStr)
return -1;
if(dateStr.indexOf("ago") > 0)
return extractAgoText_Timestamp(dateStr);
let matchDate = dateStr.match(REGEX_DATE_HUMAN);
if(matchDate) return extractHumanDate_Timestamp(matchDate.slice(1));
matchDate = dateStr.match(REGEX_DATE_EU);
if(matchDate) return new Date(matchDate[0]).getTime() / 1000;
matchDate = dateStr.match(REGEX_DATE_EU);
if(matchDate) return new Date(matchDate[0]).getTime() / 1000;
return -1;
}
function extractHumanDate_Timestamp(dateParts) {
if(dateParts.length != 3)
return -1;
let day = -1;
let month = -1;
let year = -1;
for(let i = 0; i < dateParts.length; i++) {
const part = dateParts[i];
if(part.length > 2) {
const newMonth = monthNameToNumber(part);
if(newMonth > 0)
month = newMonth;
}
if(part.length == 4 && !isNaN(part))
year = parseInt(part);
if(part.length <= 2 && !isNaN(part))
day = parseInt(part);
}
return (day > 0 && month > 0 && year > 0) ?
new Date(year + "-" + month + "-" + day).getTime() / 1000 :
-1;
}
function escapeUnicode(str) {
if(!str)
return str;
return str.replace("\\u0026", "&");
}
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
//#endregion
//#region Filters
const FILTER_DATE_HOUR = 1;
const FILTER_DATE_DAY = 2;
const FILTER_DATE_WEEK = 3;
const FILTER_DATE_MONTH = 4;
const FILTER_DATE_YEAR = 5;
const FILTER_DURATION_4MIN = 1;
const FILTER_DURATION_4_20MIN = 3;
const FILTER_DURATION_20MIN = 2;
const FILTER_HD = 32;
const FILTER_SUBS = 40;
const FILTER_LIVE = 64;
const FILTER_4K = 112;
const FILTER_CreativeCommons = 48;
const FILTER_360 = 120
const FILTER_VR = 208;
const FILTER_3D = 56;
const FILTER_HDR = 200
const FILTERS = [
{
id: "date",
name: "Upload Date",
isMultiSelect: false,
filters: [
new FilterCapability("Last Hour", FILTER_DATE_HOUR, Type.Date.LastHour),
new FilterCapability("This Day", FILTER_DATE_DAY, Type.Date.Today),
new FilterCapability("This Week", FILTER_DATE_WEEK, Type.Date.LastWeek),
new FilterCapability("This Month", FILTER_DATE_MONTH, Type.Date.LastMonth),
new FilterCapability("This Year", FILTER_DATE_YEAR, Type.Date.LastYear),
]
},
{
id: "duration",
name: "Duration",
isMultiSelect: false,
filters: [
new FilterCapability("Under 4 minutes", FILTER_DURATION_4MIN, Type.Duration.Short),
new FilterCapability("4-20 minutes", FILTER_DURATION_4_20MIN, Type.Duration.Medium),
new FilterCapability("Over 20 minutes", FILTER_DURATION_20MIN, Type.Duration.Long)
]
},
{
id: "features",
name: "Features",
isMultiSelect: true,
filters: [
new FilterCapability("HD", FILTER_HD),
new FilterCapability("4K", FILTER_4K),
new FilterCapability("HDR", FILTER_HDR),
new FilterCapability("Subtitles", FILTER_SUBS),
new FilterCapability("Live", FILTER_LIVE),
new FilterCapability("Creative Commons", FILTER_CreativeCommons),
new FilterCapability("VR", FILTER_VR),
new FilterCapability("3D", FILTER_3D),
new FilterCapability("360", FILTER_360)
]
}
]
const SORT_RELEVANCE = 18;
const SORT_DATE = 2;
const SORT_VIEWS = 3;
const SORT_RATING = 1;
const TYPE_VIDEO = 1;
const TYPE_CHANNEL = 2;
const TYPE_PLAYLIST = 3;
const TYPE_MOVIES = 4;
const PREFIX_TYPE = 16;
const PREFIX_LENGTH = 18;
const PREFIX_ORDER = 8;
const PREFIX_DATE = 8;
const PREFIX_DURATION = 24;
function sortToByte(sort) {
switch(sort) {
case Type.Order.Chronological:
return SORT_DATE;
case SORT_RATING_STRING:
return SORT_RATING;
case SORT_VIEWS_STRING:
return SORT_VIEWS;
default:
throw new ScriptException("Unknown sort");
}
}
function searchQueryToSP(sort, type, filters) {
if(!type)
type = TYPE_VIDEO;
let filter_date = (filters?.date && filters.date.length > 0) ? filters.date[0] : null;
let filter_duration = (filters?.duration && filters.duration.length > 0) ? filters.duration[0] : null;
let filter_features = filters?.features ?? [];
const sortByte = sort ? sortToByte(sort) : null;//SORT_RELEVANCE;
let arrLength = 0;
let filterLength = 0;
if(sortByte)
arrLength += 2;
if(type) {
filterLength += 2;
arrLength += 2;
}
if(filter_date) {
filterLength += 2;
arrLength += 2;
}
if(filter_duration) {
filterLength += 2;
arrLength += 2;
}
if(filter_features.length > 0) {
for(let i = 0; i < filter_features.length; i++) {
arrLength += 2;
filterLength += 2;
if(filter_features[i] > 128) {
arrLength += 1;
filterLength += 1;
}
}
}
if(filterLength > 0)
arrLength += 2;
const array = new Uint8Array(arrLength);
let index = 0;
if(sortByte) {
array[index] = PREFIX_ORDER;
array[index + 1] = sortByte;
index += 2;
}
if(filterLength > 0) {
array[index] = PREFIX_LENGTH;
array[index + 1] = filterLength;
index += 2;
}
if(filter_date) {
array[index] = PREFIX_DATE;
array[index + 1] = filter_date;
index += 2;
}
if(filter_duration) {
array[index] = PREFIX_DURATION;
array[index + 1] = filter_duration;
index += 2;
}
if(type) {
array[index] = PREFIX_TYPE;
array[index + 1] = type;
index += 2;