def watch_page_related_video_info(item): result = {key: item[key] for key in ('id', 'title', 'author')} result['duration'] = util.seconds_to_timestamp(item['length_seconds']) try: result['views'] = item['short_view_count_text'] except KeyError: result['views'] = '' result['thumbnail'] = util.get_thumbnail_url(item['id']) return result
def get_watch_page(video_id=None): video_id = request.args.get('v') or video_id if not video_id: return flask.render_template('error.html', error_message='Missing video id'), 404 if len(video_id) < 11: return flask.render_template( 'error.html', error_message='Incomplete video id (too short): ' + video_id), 404 lc = request.args.get('lc', '') playlist_id = request.args.get('list') index = request.args.get('index') use_invidious = bool(int(request.args.get('use_invidious', '1'))) tasks = (gevent.spawn(comments.video_comments, video_id, int(settings.default_comment_sorting), lc=lc), gevent.spawn(extract_info, video_id, use_invidious, playlist_id=playlist_id, index=index)) gevent.joinall(tasks) util.check_gevent_exceptions(tasks[1]) comments_info, info = tasks[0].value, tasks[1].value if info['error']: return flask.render_template('error.html', error_message=info['error']) video_info = { "duration": util.seconds_to_timestamp(info["duration"] or 0), "id": info['id'], "title": info['title'], "author": info['author'], } for item in info['related_videos']: util.prefix_urls(item) util.add_extra_html_info(item) if info['playlist']: playlist_id = info['playlist']['id'] for item in info['playlist']['items']: util.prefix_urls(item) util.add_extra_html_info(item) if playlist_id: item['url'] += '&list=' + playlist_id if item['index']: item['url'] += '&index=' + str(item['index']) info['playlist']['author_url'] = util.prefix_url( info['playlist']['author_url']) if settings.gather_googlevideo_domains: with open(os.path.join(settings.data_dir, 'googlevideo-domains.txt'), 'a+', encoding='utf-8') as f: url = info['formats'][0]['url'] subdomain = url[0:url.find(".googlevideo.com")] f.write(subdomain + "\n") download_formats = [] for format in (info['formats'] + info['hls_formats']): if format['acodec'] and format['vcodec']: codecs_string = format['acodec'] + ', ' + format['vcodec'] else: codecs_string = format['acodec'] or format['vcodec'] or '?' download_formats.append({ 'url': format['url'], 'ext': format['ext'] or '?', 'audio_quality': audio_quality_string(format), 'video_quality': video_quality_string(format), 'file_size': format_bytes(format['file_size']), 'codecs': codecs_string, }) video_sources = get_video_sources(info) video_height = yt_data_extract.deep_get(video_sources, 0, 'height', default=360) video_width = yt_data_extract.deep_get(video_sources, 0, 'width', default=640) # 1 second per pixel, or the actual video width theater_video_target_width = max(640, info['duration'] or 0, video_width) # Check for false determination of disabled comments, which comes from # the watch page. But if we got comments in the separate request for those, # then the determination is wrong. if info['comments_disabled'] and len(comments_info['comments']) != 0: info['comments_disabled'] = False print('Warning: False determination that comments are disabled') print('Comment count:', info['comment_count']) info['comment_count'] = None # hack to make it obvious there's a bug return flask.render_template( 'watch.html', header_playlist_names=local_playlist.get_playlist_names(), uploader_channel_url=('/' + info['author_url']) if info['author_url'] else '', time_published=info['time_published'], view_count=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("view_count", None)), like_count=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("like_count", None)), dislike_count=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get( "dislike_count", None)), download_formats=download_formats, video_info=json.dumps(video_info), video_sources=video_sources, hls_formats=info['hls_formats'], subtitle_sources=get_subtitle_sources(info), related=info['related_videos'], playlist=info['playlist'], music_list=info['music_list'], music_attributes=get_ordered_music_list_attributes(info['music_list']), comments_info=comments_info, comment_count=info['comment_count'], comments_disabled=info['comments_disabled'], theater_mode=settings.theater_mode, related_videos_mode=settings.related_videos_mode, comments_mode=settings.comments_mode, video_height=video_height, theater_video_target_width=theater_video_target_width, title=info['title'], uploader=info['author'], description=info['description'], unlisted=info['unlisted'], limited_state=info['limited_state'], age_restricted=info['age_restricted'], live=info['live'], playability_error=info['playability_error'], allowed_countries=info['allowed_countries'], ip_address=info['ip_address'] if settings.route_tor else None, invidious_used=info['invidious_used'], invidious_reload_button=info['invidious_reload_button'], video_url=util.URL_ORIGIN + '/watch?v=' + video_id, )
def get_watch_page(video_id=None): video_id = request.args.get('v') or video_id if not video_id: return flask.render_template('error.html', error_message='Missing video id'), 404 if len(video_id) < 11: return flask.render_template( 'error.html', error_message='Incomplete video id (too short): ' + video_id), 404 time_start_str = request.args.get('t', '0s') time_start = 0 if re.fullmatch(r'(\d+(h|m|s))+', time_start_str): for match in re.finditer(r'(\d+)(h|m|s)', time_start_str): time_start += int(match.group(1)) * time_table[match.group(2)] elif re.fullmatch(r'\d+', time_start_str): time_start = int(time_start_str) lc = request.args.get('lc', '') playlist_id = request.args.get('list') index = request.args.get('index') use_invidious = bool(int(request.args.get('use_invidious', '1'))) tasks = (gevent.spawn(comments.video_comments, video_id, int(settings.default_comment_sorting), lc=lc), gevent.spawn(extract_info, video_id, use_invidious, playlist_id=playlist_id, index=index)) gevent.joinall(tasks) util.check_gevent_exceptions(tasks[1]) comments_info, info = tasks[0].value, tasks[1].value if info['error']: return flask.render_template('error.html', error_message=info['error']) video_info = { "duration": util.seconds_to_timestamp(info["duration"] or 0), "id": info['id'], "title": info['title'], "author": info['author'], } # prefix urls, and other post-processing not handled by yt_data_extract for item in info['related_videos']: util.prefix_urls(item) util.add_extra_html_info(item) if info['playlist']: playlist_id = info['playlist']['id'] for item in info['playlist']['items']: util.prefix_urls(item) util.add_extra_html_info(item) if playlist_id: item['url'] += '&list=' + playlist_id if item['index']: item['url'] += '&index=' + str(item['index']) info['playlist']['author_url'] = util.prefix_url( info['playlist']['author_url']) # Don't prefix hls_formats for now because the urls inside the manifest # would need to be prefixed as well. for fmt in info['formats']: fmt['url'] = util.prefix_url(fmt['url']) # Add video title to end of url path so it has a filename other than just # "videoplayback" when downloaded title = urllib.parse.quote(util.to_valid_filename(info['title'] or '')) for fmt in info['formats']: filename = title ext = fmt.get('ext') if ext: filename += '.' + ext fmt['url'] = fmt['url'].replace('/videoplayback', '/videoplayback/name/' + filename) if settings.gather_googlevideo_domains: with open(os.path.join(settings.data_dir, 'googlevideo-domains.txt'), 'a+', encoding='utf-8') as f: url = info['formats'][0]['url'] subdomain = url[0:url.find(".googlevideo.com")] f.write(subdomain + "\n") download_formats = [] for format in (info['formats'] + info['hls_formats']): if format['acodec'] and format['vcodec']: codecs_string = format['acodec'] + ', ' + format['vcodec'] else: codecs_string = format['acodec'] or format['vcodec'] or '?' download_formats.append({ 'url': format['url'], 'ext': format['ext'] or '?', 'audio_quality': audio_quality_string(format), 'video_quality': video_quality_string(format), 'file_size': format_bytes(format['file_size']), 'codecs': codecs_string, }) video_sources = get_video_sources(info, tor_bypass=info['tor_bypass_used']) video_height = yt_data_extract.deep_get(video_sources, 0, 'height', default=360) video_width = yt_data_extract.deep_get(video_sources, 0, 'width', default=640) # 1 second per pixel, or the actual video width theater_video_target_width = max(640, info['duration'] or 0, video_width) # Check for false determination of disabled comments, which comes from # the watch page. But if we got comments in the separate request for those, # then the determination is wrong. if info['comments_disabled'] and len(comments_info['comments']) != 0: info['comments_disabled'] = False print('Warning: False determination that comments are disabled') print('Comment count:', info['comment_count']) info['comment_count'] = None # hack to make it obvious there's a bug # captions and transcript subtitle_sources = get_subtitle_sources(info) other_downloads = [] for source in subtitle_sources: best_caption_parse = urllib.parse.urlparse(source['url'].lstrip('/')) transcript_url = (util.URL_ORIGIN + '/watch/transcript' + best_caption_parse.path + '?' + best_caption_parse.query) other_downloads.append({ 'label': 'Video Transcript: ' + source['label'], 'ext': 'txt', 'url': transcript_url }) return flask.render_template( 'watch.html', header_playlist_names=local_playlist.get_playlist_names(), uploader_channel_url=('/' + info['author_url']) if info['author_url'] else '', time_published=info['time_published'], view_count=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("view_count", None)), like_count=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("like_count", None)), dislike_count=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get( "dislike_count", None)), download_formats=download_formats, other_downloads=other_downloads, video_info=json.dumps(video_info), video_sources=video_sources, hls_formats=info['hls_formats'], subtitle_sources=subtitle_sources, related=info['related_videos'], playlist=info['playlist'], music_list=info['music_list'], music_attributes=get_ordered_music_list_attributes(info['music_list']), comments_info=comments_info, comment_count=info['comment_count'], comments_disabled=info['comments_disabled'], video_height=video_height, video_width=video_width, theater_video_target_width=theater_video_target_width, title=info['title'], uploader=info['author'], description=info['description'], unlisted=info['unlisted'], limited_state=info['limited_state'], age_restricted=info['age_restricted'], live=info['live'], playability_error=info['playability_error'], allowed_countries=info['allowed_countries'], ip_address=info['ip_address'] if settings.route_tor else None, invidious_used=info['invidious_used'], invidious_reload_button=info['invidious_reload_button'], video_url=util.URL_ORIGIN + '/watch?v=' + video_id, time_start=time_start, js_data={ 'video_id': video_info['id'], })
def get_watch_page(): video_id = request.args['v'] if len(video_id) < 11: flask.abort(404) flask.abort( flask.Response('Incomplete video id (too short): ' + video_id)) lc = request.args.get('lc', '') if settings.route_tor: proxy = 'socks5://127.0.0.1:9150/' else: proxy = '' yt_dl_downloader = YoutubeDL(params={ 'youtube_include_dash_manifest': False, 'proxy': proxy }) tasks = (gevent.spawn(comments.video_comments, video_id, int(settings.default_comment_sorting), lc=lc), gevent.spawn(extract_info, yt_dl_downloader, "https://www.youtube.com/watch?v=" + video_id, download=False)) gevent.joinall(tasks) comments_info, info = tasks[0].value, tasks[1].value if isinstance(info, str): # youtube error return flask.render_template('error.html', error_message=info) video_info = { "duration": util.seconds_to_timestamp(info["duration"]), "id": info['id'], "title": info['title'], "author": info['uploader'], } upload_year = info["upload_date"][0:4] upload_month = info["upload_date"][4:6] upload_day = info["upload_date"][6:8] upload_date = upload_month + "/" + upload_day + "/" + upload_year if settings.related_videos_mode: related_videos = get_related_items(info) else: related_videos = [] if settings.gather_googlevideo_domains: with open(os.path.join(settings.data_dir, 'googlevideo-domains.txt'), 'a+', encoding='utf-8') as f: url = info['formats'][0]['url'] subdomain = url[0:url.find(".googlevideo.com")] f.write(subdomain + "\n") download_formats = [] for format in info['formats']: download_formats.append({ 'url': format['url'], 'ext': format['ext'], 'resolution': yt_dl_downloader.format_resolution(format), 'note': yt_dl_downloader._format_note(format), }) return flask.render_template( 'watch.html', header_playlist_names=local_playlist.get_playlist_names(), uploader_channel_url='/' + info['uploader_url'], upload_date=upload_date, views=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("view_count", None)), likes=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("like_count", None)), dislikes=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("dislike_count", None)), download_formats=download_formats, video_info=json.dumps(video_info), video_sources=get_video_sources(info), subtitle_sources=get_subtitle_sources(info), related=related_videos, music_list=info['music_list'], music_attributes=get_ordered_music_list_attributes(info['music_list']), comments_info=comments_info, title=info['title'], uploader=info['uploader'], description=info['description'], unlisted=info['unlisted'], )
def get_watch_page(env, start_response): video_id = env['parameters']['v'][0] if len(video_id) < 11: start_response('404 Not Found', [ ('Content-type', 'text/plain'), ]) return b'Incomplete video id (too short): ' + video_id.encode('ascii') start_response('200 OK', [ ('Content-type', 'text/html'), ]) lc = util.default_multi_get(env['parameters'], 'lc', 0, default='') if settings.route_tor: proxy = 'socks5://127.0.0.1:9150/' else: proxy = '' downloader = YoutubeDL(params={ 'youtube_include_dash_manifest': False, 'proxy': proxy }) tasks = (gevent.spawn(comments.video_comments, video_id, int(settings.default_comment_sorting), lc=lc), gevent.spawn(extract_info, downloader, "https://www.youtube.com/watch?v=" + video_id, download=False)) gevent.joinall(tasks) comments_html, info = tasks[0].value, tasks[1].value #comments_html = comments.comments_html(video_id(url)) #info = YoutubeDL().extract_info(url, download=False) #chosen_format = choose_format(info) if isinstance(info, str): # youtube error return html_common.yt_basic_template.substitute( page_title="Error", style="", header=html_common.get_header(), page=html.escape(info), ).encode('utf-8') sorted_formats = sort_formats(info) video_info = { "duration": util.seconds_to_timestamp(info["duration"]), "id": info['id'], "title": info['title'], "author": info['uploader'], } upload_year = info["upload_date"][0:4] upload_month = info["upload_date"][4:6] upload_day = info["upload_date"][6:8] upload_date = upload_month + "/" + upload_day + "/" + upload_year if settings.enable_related_videos: related_videos_html = get_related_items_html(info) else: related_videos_html = '' music_list = info['music_list'] if len(music_list) == 0: music_list_html = '' else: # get the set of attributes which are used by atleast 1 track # so there isn't an empty, extraneous album column which no tracks use, for example used_attributes = set() for track in music_list: used_attributes = used_attributes | track.keys() # now put them in the right order ordered_attributes = [] for attribute in ('Artist', 'Title', 'Album'): if attribute.lower() in used_attributes: ordered_attributes.append(attribute) music_list_html = '''<hr> <table> <caption>Music</caption> <tr> ''' # table headings for attribute in ordered_attributes: music_list_html += "<th>" + attribute + "</th>\n" music_list_html += '''</tr>\n''' for track in music_list: music_list_html += '''<tr>\n''' for attribute in ordered_attributes: try: value = track[attribute.lower()] except KeyError: music_list_html += '''<td></td>''' else: music_list_html += '''<td>''' + html.escape( value) + '''</td>''' music_list_html += '''</tr>\n''' music_list_html += '''</table>\n''' if settings.gather_googlevideo_domains: with open(os.path.join(settings.data_dir, 'googlevideo-domains.txt'), 'a+', encoding='utf-8') as f: url = info['formats'][0]['url'] subdomain = url[0:url.find(".googlevideo.com")] f.write(subdomain + "\n") download_options = '' for format in info['formats']: download_options += download_link_template.substitute( url=html.escape(format['url']), ext=html.escape(format['ext']), resolution=html.escape(downloader.format_resolution(format)), note=html.escape(downloader._format_note(format)), ) page = yt_watch_template.substitute( video_title=html.escape(info["title"]), page_title=html.escape(info["title"]), header=html_common.get_header(), uploader=html.escape(info["uploader"]), uploader_channel_url='/' + info["uploader_url"], upload_date=upload_date, views=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("view_count", None)), likes=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("like_count", None)), dislikes=(lambda x: '{:,}'.format(x) if x is not None else "")(info.get("dislike_count", None)), download_options=download_options, video_info=html.escape(json.dumps(video_info)), description=html.escape(info["description"]), video_sources=formats_html(sorted_formats) + subtitles_html(info), related=related_videos_html, comments=comments_html, music_list=music_list_html, is_unlisted='<span class="is-unlisted">Unlisted</span>' if info['unlisted'] else '', ) return page.encode('utf-8')