import re import requests from urllib.parse import urlparse from ..common.common import video_metadata from ..common.innertube import prepare_infocards, prepare_endcards, G def prepare_metadata(metadata): meta = metadata['videoDetails'] # the actual video streams have exact information: try: sd = metadata['streamingData'] some_stream = (sd.get('adaptiveFormats',[]) + sd.get('formats',[]))[0] aspect_ratio = some_stream['width'] / some_stream['height'] # if that's unavailable (e.g. on livestreams), fall back to 16:9 except: aspect_ratio = 16/9 # Note: we could get subtitles in multiple formats directly by querying # https://video.google.com/timedtext?hl=en&type=list&v= followed by # https://www.youtube.com/api/timedtext?lang=&v=&fmt={srv1|srv2|srv3|ttml|vtt}, # but that won't give us autogenerated subtitles (and is an extra request). # we can still add &fmt= to the extracted URLs below (first one takes precedence). try: # find the native language captions (assuming there is only 1 audioTrack) (any level might not exist): default_track = metadata.get('captions',{}).get('playerCaptionsTracklistRenderer',{}).get('defaultAudioTrackIndex', 0) main_subtitle = metadata['captions']['playerCaptionsTracklistRenderer']['audioTracks'][default_track]['captionTrackIndices'] except: main_subtitle = -1 subtitles = sorted([ {'url':cc['baseUrl'], 'code':cc['languageCode'], 'autogenerated':cc.get('kind')=="asr", 'name':cc['name']|G.text, 'default':i==main_subtitle, 'query':"fmt=vtt&"+urlparse(cc['baseUrl']).query} # for our internal proxy for i,cc in enumerate(metadata|G('captions') |G('playerCaptionsTracklistRenderer') |G('captionTracks') or []) # sort order: default lang gets weight 0 (first), other manually translated weight 1, autogenerated weight 2: ], key=lambda cc: (not cc['default']) + cc['autogenerated']) endcards = prepare_endcards(metadata) # the rating goes from 1 to 5, and is the ratio of up- to down votes, plus 1 if meta.get('averageRating', 0) != 0: thumbs_up = 100 * (meta['averageRating']-1) / 4 # reconstructed ratio thumbs_dn = 100 - thumbs_up else: # no thumbs given thumbs_up = 0 thumbs_dn = 0 thumbs = meta['thumbnail']['thumbnails'] poster = sorted(thumbs, key=lambda t: t['width'], reverse=True)[0]['url'] return { **video_metadata(metadata), 'description': meta['shortDescription'], 'rating': meta.get('averageRating', 0), 'thumbs_up': thumbs_up, 'thumbs_dn': thumbs_dn, 'aspectr': aspect_ratio, 'unlisted': not meta['isCrawlable'], 'poster': poster, 'endcards': endcards, 'all_cards': endcards, 'subtitles': subtitles, } def channel_exists(feed_id): feed_type = "channel_id" if re.match(r"^UC[A-Za-z0-9_-]{22}$", feed_id) else "user" r = requests.head("https://www.youtube.com/feeds/videos.xml", params={ feed_type: feed_id, }) return r.ok