Beispiel #1
0
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
Beispiel #2
0
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,
    )
Beispiel #3
0
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'],
        })
Beispiel #4
0
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'],
    )
Beispiel #5
0
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')