diff --git a/app/fetchers/playlist.js b/app/fetchers/playlist.js index a1f3186..96cc398 100644 --- a/app/fetchers/playlist.js +++ b/app/fetchers/playlist.js @@ -25,7 +25,11 @@ class PlaylistFetcher { } static async parseChannelPlaylistResponse (response, channelIdType, httpAgent = null) { - const channelMetaData = response.data.response.metadata.channelMetadataRenderer + let channelPageDataResponse = response.data.response + if (typeof (channelPageDataResponse) === 'undefined') { + channelPageDataResponse = response.data[1].response + } + const channelMetaData = channelPageDataResponse.metadata.channelMetadataRenderer const channelName = channelMetaData.title const channelId = channelMetaData.externalId const ytGrabHelp = helper.create(httpAgent) @@ -36,7 +40,7 @@ class PlaylistFetcher { channelUrl: `https://www.youtube.com/channel/${channelId}` } - const playlistData = response.data.response.contents.twoColumnBrowseResultsRenderer.tabs[2].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer + const playlistData = channelPageDataResponse.contents.twoColumnBrowseResultsRenderer.tabs[2].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer if (typeof (playlistData) === 'undefined') { return { diff --git a/app/helper.js b/app/helper.js index b9f138d..4a60d91 100644 --- a/app/helper.js +++ b/app/helper.js @@ -55,15 +55,19 @@ class YoutubeGrabberHelper { } async parseChannelVideoResponse(response, channelId, channelIdType) { - if (typeof (response.data.response.alerts) !== 'undefined') { + let channelPageDataResponse = response.data.response + if (typeof (channelPageDataResponse) === 'undefined') { + channelPageDataResponse = response.data[1].response + } + if (typeof (channelPageDataResponse.alerts) !== 'undefined') { return { - alertMessage: response.data.response.alerts[0].alertRenderer.text.simpleText + alertMessage: channelPageDataResponse.alerts[0].alertRenderer.text.simpleText } } - const channelMetaData = response.data.response.metadata.channelMetadataRenderer + const channelMetaData = channelPageDataResponse.metadata.channelMetadataRenderer const channelName = channelMetaData.title - const channelVideoData = response.data.response.contents.twoColumnBrowseResultsRenderer.tabs[1].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer + const channelVideoData = channelPageDataResponse.contents.twoColumnBrowseResultsRenderer.tabs[1].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer if (typeof (channelVideoData) === 'undefined') { // Channel has no videos @@ -111,7 +115,10 @@ class YoutubeGrabberHelper { const channelId = author.channelId const channelUrl = author.navigationEndpoint.browseEndpoint.canonicalBaseUrl const thumbnail = author.thumbnail.thumbnails - const videoCount = author.videoCountText.runs[0].text + let videoCount = 0 + if ('videoCout' in author) { + videoCount = author.videoCountText.runs[0].text + } let subscriberText if (author.subscriberCountText) { if (typeof (author.subscriberCountText.runs) !== 'undefined') { diff --git a/app/youtube-grabber.js b/app/youtube-grabber.js index d189df9..e72b077 100644 --- a/app/youtube-grabber.js +++ b/app/youtube-grabber.js @@ -17,7 +17,11 @@ class YoutubeGrabber { const ytGrabHelp = YoutubeGrabberHelper.create(httpAgent) const decideResponse = await ytGrabHelp.decideUrlRequestType(channelId, 'channels?flow=grid&view=0&pbj=1', channelIdType) const channelPageResponse = decideResponse.response - const headerLinks = channelPageResponse.data.response.header.c4TabbedHeaderRenderer.headerLinks + let channelPageDataResponse = channelPageResponse.data.response + if (channelPageResponse.data.response === undefined) { + channelPageDataResponse = channelPageResponse.data[1].response + } + const headerLinks = channelPageDataResponse.header.c4TabbedHeaderRenderer.headerLinks const links = { primaryLinks: [], secondaryLinks: [] @@ -43,15 +47,16 @@ class YoutubeGrabber { }) } } - if (typeof (channelPageResponse.data.response.alerts) !== 'undefined') { + + if (typeof (channelPageDataResponse.alerts) !== 'undefined') { return { - alertMessage: channelPageResponse.data.response.alerts[0].alertRenderer.text.simpleText + alertMessage: channelPageDataResponse.alerts[0].alertRenderer.text.simpleText } } - const channelMetaData = channelPageResponse.data.response.metadata.channelMetadataRenderer - const channelHeaderData = channelPageResponse.data.response.header.c4TabbedHeaderRenderer - const headerTabs = channelPageResponse.data.response.contents.twoColumnBrowseResultsRenderer.tabs + const channelMetaData = channelPageDataResponse.metadata.channelMetadataRenderer + const channelHeaderData = channelPageDataResponse.header.c4TabbedHeaderRenderer + const headerTabs = channelPageDataResponse.contents.twoColumnBrowseResultsRenderer.tabs const channelsTab = headerTabs.filter((data) => { if (typeof data.tabRenderer !== 'undefined') { @@ -125,7 +130,7 @@ class YoutubeGrabber { isOfficialArtist = channelHeaderData.badges.some((badge) => badge.metadataBadgeRenderer.style === 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') } - const tags = channelPageResponse.data.response.microformat.microformatDataRenderer.tags || null + const tags = channelPageDataResponse.microformat.microformatDataRenderer.tags || null const channelInfo = { author: channelMetaData.title, @@ -324,8 +329,11 @@ class YoutubeGrabber { const decideResponse = await ytGrabHelp.decideUrlRequestType(channelId, `search?${urlParams}`, channelIdType) const channelPageResponse = decideResponse.response - - const channelMetaData = channelPageResponse.data.response.metadata.channelMetadataRenderer + let channelPageDataResponse = channelPageResponse.data.response + if (typeof channelPageDataResponse === 'undefined') { + channelPageDataResponse = channelPageResponse.data[1].response + } + const channelMetaData = channelPageDataResponse.metadata.channelMetadataRenderer const channelName = channelMetaData.title const channelInfo = { @@ -334,13 +342,13 @@ class YoutubeGrabber { channelUrl: `https://www.youtube.com/channel/${channelId}` } - const searchTab = channelPageResponse.data.response.contents.twoColumnBrowseResultsRenderer.tabs.findIndex((tab) => { + const searchTab = channelPageDataResponse.contents.twoColumnBrowseResultsRenderer.tabs.findIndex((tab) => { if (typeof (tab.expandableTabRenderer) !== 'undefined') { return true } }) - const searchResults = channelPageResponse.data.response.contents.twoColumnBrowseResultsRenderer.tabs[searchTab].expandableTabRenderer.content.sectionListRenderer + const searchResults = channelPageDataResponse.contents.twoColumnBrowseResultsRenderer.tabs[searchTab].expandableTabRenderer.content.sectionListRenderer let continuation = null @@ -393,7 +401,6 @@ class YoutubeGrabber { let nextContinuation = null const continuationData = channelPageResponse.data.onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems - const continuationItem = continuationData.filter((item) => { return typeof (item.continuationItemRenderer) !== 'undefined' }) @@ -451,7 +458,12 @@ class YoutubeGrabber { const ytGrabHelp = YoutubeGrabberHelper.create(httpAgent) const decideResponse = await ytGrabHelp.decideUrlRequestType(channelId, 'about?flow=grid&view=0&pbj=1', channelIdType) const channelPageResponse = decideResponse.response - const headerTabs = channelPageResponse.data.response.contents.twoColumnBrowseResultsRenderer.tabs + let headerTabs + if (channelPageResponse.data.response) { + headerTabs = channelPageResponse.data.response.contents.twoColumnBrowseResultsRenderer.tabs + } else { + headerTabs = channelPageResponse.data[1].response.contents.twoColumnBrowseResultsRenderer.tabs + } const aboutTab = headerTabs.filter((data) => { if (typeof data.tabRenderer !== 'undefined') { return data.tabRenderer.title === 'About' @@ -460,8 +472,16 @@ class YoutubeGrabber { })[0] const contents = aboutTab.tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0] const joined = Date.parse(contents.channelAboutFullMetadataRenderer.joinedDateText.runs[1].text) - const views = contents.channelAboutFullMetadataRenderer.viewCountText.simpleText.replace(/\D/g, '') - const location = contents.channelAboutFullMetadataRenderer.country.simpleText + let views = '0' + let location = 'unknown' + if ('viewCountText' in contents.channelAboutFullMetadataRenderer) { + views = contents.channelAboutFullMetadataRenderer.viewCountText.simpleText.replace(/\D/g, '') + } + + if ('country' in contents.channelAboutFullMetadataRenderer) { + location = contents.channelAboutFullMetadataRenderer.country.simpleText + } + return { joinedDate: joined, viewCount: parseInt(views), @@ -477,11 +497,21 @@ class YoutubeGrabber { const ytGrabHelp = YoutubeGrabberHelper.create(httpAgent) const decideResponse = await ytGrabHelp.decideUrlRequestType(channelId, 'home?flow=grid&view=0&pbj=1', channelIdType) const channelPageResponse = decideResponse.response - const headerTabs = channelPageResponse.data.response.contents.twoColumnBrowseResultsRenderer.tabs - - const channelMetaData = channelPageResponse.data.response.metadata.channelMetadataRenderer - const channelName = channelMetaData.title - const channelUrl = channelMetaData.vanityChannelUrl + let channelPageDataResponse = channelPageResponse.data.response + if (typeof channelPageDataResponse === 'undefined') { + channelPageDataResponse = channelPageResponse.data[1].response + } + const headerTabs = channelPageDataResponse.contents.twoColumnBrowseResultsRenderer.tabs + let channelName + let channelUrl + if ('metadata' in channelPageDataResponse) { + channelName = channelPageDataResponse.metadata.channelMetadataRenderer.title + channelUrl = channelPageDataResponse.metadata.channelMetadataRenderer.vanityChannelUrl + } else { + const channelDetails = channelPageResponse.data[1].response.header.carouselHeaderRenderer.contents[1].topicChannelDetailsRenderer + channelName = channelDetails.title + channelUrl = channelDetails.navigationEndpoint.browseEndpoint.canonicalBaseUrl + } const channelInfo = { channelId: channelId, @@ -496,14 +526,19 @@ class YoutubeGrabber { return false })[0] let featuredVideo = null - let homeItems = homeTab.tabRenderer.content.sectionListRenderer.contents.filter(x => { - if ('shelfRenderer' in x.itemSectionRenderer.contents[0]) { - return true - } else if ('channelVideoPlayerRenderer' in x.itemSectionRenderer.contents[0]) { - featuredVideo = ytGrabHelp.parseVideo(x.itemSectionRenderer.contents[0], channelInfo) - } - return false - }) + let homeItems + if (homeTab !== undefined) { + homeItems = homeTab.tabRenderer.content.sectionListRenderer.contents.filter(x => { + if ('shelfRenderer' in x.itemSectionRenderer.contents[0]) { + return true + } else if ('channelVideoPlayerRenderer' in x.itemSectionRenderer.contents[0]) { + featuredVideo = ytGrabHelp.parseVideo(x.itemSectionRenderer.contents[0], channelInfo) + } + return false + }) + } else { + homeItems = headerTabs[0].tabRenderer.content.richGridRenderer.contents + } homeItems = homeItems.map(x => { const shelf = x.itemSectionRenderer.contents[0].shelfRenderer const title = shelf.title.runs[0] @@ -521,9 +556,15 @@ class YoutubeGrabber { }) } else if (shelfUrl.match(/\?list=/)) { type = 'playlist' // similar to videos but links to a playlist url - items = shelf.content.horizontalListRenderer.items.map(video => { - return ytGrabHelp.parseVideo(video, channelInfo) - }) + if ('horizontalListRenderer' in shelf.content) { + items = shelf.content.horizontalListRenderer.items.map(video => { + return ytGrabHelp.parseVideo(video, channelInfo) + }) + } else { + items = shelf.content.expandedShelfContentsRenderer.items.map(video => { + return ytGrabHelp.parseVideo(video, channelInfo) + }) + } } else if (shelfUrl.match(/\/channels/)) { type = 'channels' items = shelf.content.horizontalListRenderer.items.map(channel => {