mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-11-04 08:35:12 +00:00 
			
		
		
		
	[Youtube] Fix private feeds/playlists on multi-channel accounts (#143)
Authored by: colethedj
This commit is contained in:
		@@ -301,7 +301,6 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
 | 
				
			|||||||
        auth = self._generate_sapisidhash_header()
 | 
					        auth = self._generate_sapisidhash_header()
 | 
				
			||||||
        if auth is not None:
 | 
					        if auth is not None:
 | 
				
			||||||
            headers.update({'Authorization': auth, 'X-Origin': 'https://www.youtube.com'})
 | 
					            headers.update({'Authorization': auth, 'X-Origin': 'https://www.youtube.com'})
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return self._download_json(
 | 
					        return self._download_json(
 | 
				
			||||||
            'https://www.youtube.com/youtubei/v1/%s' % ep,
 | 
					            'https://www.youtube.com/youtubei/v1/%s' % ep,
 | 
				
			||||||
            video_id=video_id, fatal=fatal, note=note, errnote=errnote,
 | 
					            video_id=video_id, fatal=fatal, note=note, errnote=errnote,
 | 
				
			||||||
@@ -2704,7 +2703,7 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
 | 
				
			|||||||
            ctp = continuation_ep.get('clickTrackingParams')
 | 
					            ctp = continuation_ep.get('clickTrackingParams')
 | 
				
			||||||
            return YoutubeTabIE._build_continuation_query(continuation, ctp)
 | 
					            return YoutubeTabIE._build_continuation_query(continuation, ctp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _entries(self, tab, identity_token, item_id):
 | 
					    def _entries(self, tab, item_id, identity_token, account_syncid):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def extract_entries(parent_renderer):  # this needs to called again for continuation to work with feeds
 | 
					        def extract_entries(parent_renderer):  # this needs to called again for continuation to work with feeds
 | 
				
			||||||
            contents = try_get(parent_renderer, lambda x: x['contents'], list) or []
 | 
					            contents = try_get(parent_renderer, lambda x: x['contents'], list) or []
 | 
				
			||||||
@@ -2764,6 +2763,10 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
 | 
				
			|||||||
        if identity_token:
 | 
					        if identity_token:
 | 
				
			||||||
            headers['x-youtube-identity-token'] = identity_token
 | 
					            headers['x-youtube-identity-token'] = identity_token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if account_syncid:
 | 
				
			||||||
 | 
					            headers['X-Goog-PageId'] = account_syncid
 | 
				
			||||||
 | 
					            headers['X-Goog-AuthUser'] = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for page_num in itertools.count(1):
 | 
					        for page_num in itertools.count(1):
 | 
				
			||||||
            if not continuation:
 | 
					            if not continuation:
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
@@ -2883,7 +2886,7 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
 | 
				
			|||||||
                        try_get(owner, lambda x: x['navigationEndpoint']['browseEndpoint']['canonicalBaseUrl'], compat_str))
 | 
					                        try_get(owner, lambda x: x['navigationEndpoint']['browseEndpoint']['canonicalBaseUrl'], compat_str))
 | 
				
			||||||
        return {k: v for k, v in uploader.items() if v is not None}
 | 
					        return {k: v for k, v in uploader.items() if v is not None}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _extract_from_tabs(self, item_id, webpage, data, tabs, identity_token):
 | 
					    def _extract_from_tabs(self, item_id, webpage, data, tabs):
 | 
				
			||||||
        playlist_id = title = description = channel_url = channel_name = channel_id = None
 | 
					        playlist_id = title = description = channel_url = channel_name = channel_id = None
 | 
				
			||||||
        thumbnails_list = tags = []
 | 
					        thumbnails_list = tags = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2947,7 +2950,10 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
 | 
				
			|||||||
            'channel_id': metadata['uploader_id'],
 | 
					            'channel_id': metadata['uploader_id'],
 | 
				
			||||||
            'channel_url': metadata['uploader_url']})
 | 
					            'channel_url': metadata['uploader_url']})
 | 
				
			||||||
        return self.playlist_result(
 | 
					        return self.playlist_result(
 | 
				
			||||||
            self._entries(selected_tab, identity_token, playlist_id),
 | 
					            self._entries(
 | 
				
			||||||
 | 
					                selected_tab, playlist_id,
 | 
				
			||||||
 | 
					                self._extract_identity_token(webpage, item_id),
 | 
				
			||||||
 | 
					                self._extract_account_syncid(data)),
 | 
				
			||||||
            **metadata)
 | 
					            **metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _extract_mix_playlist(self, playlist, playlist_id):
 | 
					    def _extract_mix_playlist(self, playlist, playlist_id):
 | 
				
			||||||
@@ -3026,6 +3032,17 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
 | 
				
			|||||||
            r'\bID_TOKEN["\']\s*:\s*["\'](.+?)["\']', webpage,
 | 
					            r'\bID_TOKEN["\']\s*:\s*["\'](.+?)["\']', webpage,
 | 
				
			||||||
            'identity token', default=None)
 | 
					            'identity token', default=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def _extract_account_syncid(data):
 | 
				
			||||||
 | 
					        """Extract syncId required to download private playlists of secondary channels"""
 | 
				
			||||||
 | 
					        sync_ids = (
 | 
				
			||||||
 | 
					            try_get(data, lambda x: x['responseContext']['mainAppWebResponseContext']['datasyncId'], compat_str)
 | 
				
			||||||
 | 
					            or '').split("||")
 | 
				
			||||||
 | 
					        if len(sync_ids) >= 2 and sync_ids[1]:
 | 
				
			||||||
 | 
					            # datasyncid is of the form "channel_syncid||user_syncid" for secondary channel
 | 
				
			||||||
 | 
					            # and just "user_syncid||" for primary channel. We only want the channel_syncid
 | 
				
			||||||
 | 
					            return sync_ids[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _extract_webpage(self, url, item_id):
 | 
					    def _extract_webpage(self, url, item_id):
 | 
				
			||||||
        retries = self._downloader.params.get('extractor_retries', 3)
 | 
					        retries = self._downloader.params.get('extractor_retries', 3)
 | 
				
			||||||
        count = -1
 | 
					        count = -1
 | 
				
			||||||
@@ -3085,8 +3102,7 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
 | 
				
			|||||||
        tabs = try_get(
 | 
					        tabs = try_get(
 | 
				
			||||||
            data, lambda x: x['contents']['twoColumnBrowseResultsRenderer']['tabs'], list)
 | 
					            data, lambda x: x['contents']['twoColumnBrowseResultsRenderer']['tabs'], list)
 | 
				
			||||||
        if tabs:
 | 
					        if tabs:
 | 
				
			||||||
            identity_token = self._extract_identity_token(webpage, item_id)
 | 
					            return self._extract_from_tabs(item_id, webpage, data, tabs)
 | 
				
			||||||
            return self._extract_from_tabs(item_id, webpage, data, tabs, identity_token)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        playlist = try_get(
 | 
					        playlist = try_get(
 | 
				
			||||||
            data, lambda x: x['contents']['twoColumnWatchNextResults']['playlist']['playlist'], dict)
 | 
					            data, lambda x: x['contents']['twoColumnWatchNextResults']['playlist']['playlist'], dict)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user