From eef05cc76938a3033988f62e8989bfbef6440591 Mon Sep 17 00:00:00 2001 From: pluja Date: Sat, 10 Oct 2020 20:35:03 +0200 Subject: [PATCH] Improve channels page --- app/routes.py | 64 ++++++++------ app/templates/channel.html | 116 +++++++++++++++++--------- youtube/channel.py | 43 +++------- {youtube_data => youtube}/channels.py | 2 +- 4 files changed, 126 insertions(+), 99 deletions(-) rename {youtube_data => youtube}/channels.py (99%) diff --git a/app/routes.py b/app/routes.py index 8f8291a..1ded586 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,4 +1,3 @@ - import datetime import glob import json @@ -28,8 +27,9 @@ from youtube_search import YoutubeSearch from app import app, db from app.forms import LoginForm, RegistrationForm, EmptyForm, SearchForm, ChannelForm from app.models import User, twitterPost, ytPost, Post, youtubeFollow, twitterFollow -from youtube import comments, utils, search as yts +from youtube import comments, utils, channel as ytch, search as yts from youtube import watch as ytwatch + ######################################### ######################################### @@ -269,6 +269,7 @@ def u(username): return render_template('user.html', posts=posts, user=user, form=form, config=config) + ######################### #### Youtube Logic ###### ######################### @@ -332,7 +333,8 @@ def ytsearch(): if config['nginxVideoStream']: channel['thumbnail'] = channel['thumbnail'].replace("~", "/") hostName = urllib.parse.urlparse(channel['thumbnail']).netloc - channel['thumbnail'] = channel['thumbnail'].replace("https://{}".format(hostName),"") + "?host=" + hostName + channel['thumbnail'] = channel['thumbnail'].replace("https://{}".format(hostName), + "") + "?host=" + hostName return render_template('ytsearch.html', form=form, btform=button_form, results=results, restricted=config['restrictPublicUsage'], config=config, npage=next_page, ppage=prev_page) @@ -343,9 +345,7 @@ def ytsearch(): @app.route('/ytfollow/', methods=['POST']) @login_required def ytfollow(channelId): - form = EmptyForm() - if form.validate_on_submit(): - r = followYoutubeChannel(channelId) + r = followYoutubeChannel(channelId) return redirect(request.referrer) @@ -377,9 +377,7 @@ def followYoutubeChannel(channelId): @app.route('/ytunfollow/', methods=['POST']) @login_required def ytunfollow(channelId): - form = EmptyForm() - if form.validate_on_submit(): - unfollowYoutubeChannel(channelId) + unfollowYoutubeChannel(channelId) return redirect(request.referrer) @@ -405,27 +403,38 @@ def unfollowYoutubeChannel(channelId): def channel(id): form = ChannelForm() button_form = EmptyForm() - data = requests.get('https://www.youtube.com/feeds/videos.xml?channel_id={id}'.format(id=id)) - data = feedparser.parse(data.content) - channelData = YoutubeSearch.channelInfo(id) + page = request.args.get('p', None) + sort = request.args.get('s', None) + if page is None: + page = 1 + if sort is None: + sort = 3 - for video in channelData[1]: + data = ytch.get_channel_tab_info(id, page, sort) + + for video in data['items']: if config['nginxVideoStream']: - hostName = urllib.parse.urlparse(video['videoThumb']).netloc - video['videoThumb'] = video['videoThumb'].replace("https://{}".format(hostName), "").replace("hqdefault", - "mqdefault") + "&host=" + hostName + hostName = urllib.parse.urlparse(video['thumbnail'][1:]).netloc + video['thumbnail'] = video['thumbnail'].replace("https://{}".format(hostName), "")[1:].replace("hqdefault", + "mqdefault") + "&host=" + hostName else: - video['videoThumb'] = video['videoThumb'].replace('/', '~') - if config['nginxVideoStream']: - hostName = urllib.parse.urlparse(channelData[0]['avatar']).netloc - channelData[0]['avatar'] = channelData[0]['avatar'].replace("https://{}".format(hostName), - "") + "?host=" + hostName - else: - channelData[0]['avatar'] = channelData[0]['avatar'].replace('/', '~') + video['thumbnail'] = video['thumbnail'].replace('/', '~') - return render_template('channel.html', form=form, btform=button_form, channel=channelData[0], videos=channelData[1], - restricted=config['restrictPublicUsage'], config=config) + if config['nginxVideoStream']: + hostName = urllib.parse.urlparse(data['avatar'][1:]).netloc + data['avatar'] = data['avatar'].replace("https://{}".format(hostName), "")[1:] + "?host=" + hostName + else: + data['avatar'] = data['avatar'].replace('/', '~') + + next_page = "/channel/{q}?s={s}&p={p}".format(q=id, s=sort, p=int(page) + 1) + if int(page) == 1: + prev_page = "/channel/{q}?s={s}&p={p}".format(q=id, s=sort, p=1) + else: + prev_page = "/channel/{q}?s={s}&p={p}".format(q=id, s=sort, p=int(page) - 1) + + return render_template('channel.html', form=form, btform=button_form, data=data, + restricted=config['restrictPublicUsage'], config=config, next_page=next_page, prev_page=prev_page) def get_best_urls(urls): @@ -474,8 +483,9 @@ def watch(): if videocomments is not None: videocomments.sort(key=lambda x: x['likes'], reverse=True) - info['rating'] = str((info['like_count']/(info['like_count']+info['dislike_count']))*100)[0:4] - return render_template("video.html", info=info, title='{}'.format(info['title']), config=config, videocomments=videocomments) + info['rating'] = str((info['like_count'] / (info['like_count'] + info['dislike_count'])) * 100)[0:4] + return render_template("video.html", info=info, title='{}'.format(info['title']), config=config, + videocomments=videocomments) def markupString(string): diff --git a/app/templates/channel.html b/app/templates/channel.html index a9c3272..ac8f133 100644 --- a/app/templates/channel.html +++ b/app/templates/channel.html @@ -1,58 +1,94 @@ {% extends "base.html" %} + {% block content %} -
-
-
- {%if config.nginxVideoStream%} - Thumbnail +
+
+

+ + {{data.channel_name}} +

+
+
+

{{data.short_description}}

+
+
+
+
+ {%if data.approx_suscriber_count == None%} + ? {%else%} - Thumbnail + {{data.approx_subscriber_count}} {%endif%}
- -
-
-
- {{channel.subCount}} -
-
- Followers -
-
+
+ Followers
-
- {% if restricted or current_user.is_authenticated %} -
- {% if not current_user.is_following_yt(channel.id) %} -

-

- {{ btform.hidden_tag() }} - {{ btform.submit(value='Follow') }} -
-

- {% else %} -

-

- {{ btform.hidden_tag() }} - {{ btform.submit(value='Unfollow') }} -
-

- {% endif %} -
- {% endif %} -
+
+{% if restricted or current_user.is_authenticated %} + {% if not current_user.is_following_yt(data.channel_id) %} +
+ +
+ {% else %} +
+ +
+ {%endif%} +{%endif%}
+


- {% if not videos %} + {% if data['error'] != None %} {% include '_empty_feed.html' %} {% else %}
- {% for video in videos %} - {% include '_video_item.html' %} + {% for video in data['items'] %} +
+ + + + +
+ + + {%if video.duration == "PREMIERING NOW" or video.duration == "LIVE"%} + + + LIVE + + {%else%} + + + {{video.time_published}} + + {%endif%} +
+
{% endfor %}
{% endif %} + +
+
+ + +
+
{% endblock %} \ No newline at end of file diff --git a/youtube/channel.py b/youtube/channel.py index e9cc87b..b1d991d 100644 --- a/youtube/channel.py +++ b/youtube/channel.py @@ -1,20 +1,16 @@ import base64 -from youtube import util, yt_data_extract, local_playlist, subscriptions -from youtube import yt_app - -import urllib import json -from string import Template -import youtube.proto as proto -import html import math -import gevent import re -import cachetools.func import traceback +import urllib +import cachetools.func import flask -from flask import request +import gevent + +import youtube.proto as proto +from youtube import util, yt_data_extract headers_desktop = ( ('Accept', '*/*'), @@ -109,7 +105,7 @@ def channel_ctoken_v1(channel_id, page, sort, tab, view=1): return base64.urlsafe_b64encode(pointless_nest).decode('ascii') -def get_channel_tab(channel_id, page="1", sort=3, tab='videos', view=1, print_status=True): +def get_channel_tab_info(channel_id, page="1", sort=3, tab='videos', view=1, print_status=True): message = 'Got channel tab' if print_status else None if int(sort) == 2 and int(page) > 1: @@ -128,7 +124,11 @@ def get_channel_tab(channel_id, page="1", sort=3, tab='videos', view=1, print_st headers_desktop + generic_cookie, debug_name='channel_tab', report_text=message) - return content + info = yt_data_extract.extract_channel_info(json.loads(content), tab) + if info['error'] is not None: + return False + post_process_channel_info(info) + return info # cache entries expire after 30 minutes @cachetools.func.ttl_cache(maxsize=128, ttl=30*60) @@ -259,23 +259,4 @@ def get_channel_page_general_url(base_url, tab, request, channel_id=None): **info ) -@yt_app.route('/channel//') -@yt_app.route('/channel//') -def get_channel_page(channel_id, tab='videos'): - return get_channel_page_general_url('https://www.youtube.com/channel/' + channel_id, tab, request, channel_id) - -@yt_app.route('/user//') -@yt_app.route('/user//') -def get_user_page(username, tab='videos'): - return get_channel_page_general_url('https://www.youtube.com/user/' + username, tab, request) - -@yt_app.route('/c//') -@yt_app.route('/c//') -def get_custom_c_page(custom, tab='videos'): - return get_channel_page_general_url('https://www.youtube.com/c/' + custom, tab, request) - -@yt_app.route('/') -@yt_app.route('//') -def get_toplevel_custom_page(custom, tab='videos'): - return get_channel_page_general_url('https://www.youtube.com/' + custom, tab, request) diff --git a/youtube_data/channels.py b/youtube/channels.py similarity index 99% rename from youtube_data/channels.py rename to youtube/channels.py index c6bb2d7..5c45ef7 100644 --- a/youtube_data/channels.py +++ b/youtube/channels.py @@ -1,4 +1,4 @@ -from youtube_data import proto +from youtube import proto from flask import Markup as mk import requests import base64