예제 #1
0
def get_playlist_page():
    if 'list' not in request.args:
        abort(400)

    playlist_id = request.args.get('list')
    page = request.args.get('page', '1')

    if page == '1':
        first_page_json = playlist_first_page(playlist_id)
        this_page_json = first_page_json
    else:
        tasks = (gevent.spawn(playlist_first_page,
                              playlist_id,
                              report_text="Retrieved playlist info"),
                 gevent.spawn(get_videos, playlist_id, page))
        gevent.joinall(tasks)
        util.check_gevent_exceptions(*tasks)
        first_page_json, this_page_json = tasks[0].value, tasks[1].value

    info = yt_data_extract.extract_playlist_info(this_page_json)
    if info['error']:
        return flask.render_template('error.html', error_message=info['error'])

    if page != '1':
        info['metadata'] = yt_data_extract.extract_playlist_metadata(
            first_page_json)

    util.prefix_urls(info['metadata'])
    for item in info.get('items', ()):
        util.prefix_urls(item)
        util.add_extra_html_info(item)
        if 'id' in item:
            item[
                'thumbnail'] = settings.img_prefix + 'https://i.ytimg.com/vi/' + item[
                    'id'] + '/default.jpg'

        item['url'] += '&list=' + playlist_id
        if item['index']:
            item['url'] += '&index=' + str(item['index'])

    video_count = yt_data_extract.deep_get(info, 'metadata', 'video_count')
    if video_count is None:
        video_count = 40

    return flask.render_template(
        'playlist.html',
        header_playlist_names=local_playlist.get_playlist_names(),
        video_list=info.get('items', []),
        num_pages=math.ceil(video_count / 20),
        parameters_dictionary=request.args,
        **info['metadata']).encode('utf-8')
예제 #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,
    )
예제 #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'],
        })