Improve channels page
This commit is contained in:
parent
f2badcef55
commit
eef05cc769
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
@ -28,8 +27,9 @@ from youtube_search import YoutubeSearch
|
|||||||
from app import app, db
|
from app import app, db
|
||||||
from app.forms import LoginForm, RegistrationForm, EmptyForm, SearchForm, ChannelForm
|
from app.forms import LoginForm, RegistrationForm, EmptyForm, SearchForm, ChannelForm
|
||||||
from app.models import User, twitterPost, ytPost, Post, youtubeFollow, twitterFollow
|
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
|
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)
|
return render_template('user.html', posts=posts, user=user, form=form, config=config)
|
||||||
|
|
||||||
|
|
||||||
#########################
|
#########################
|
||||||
#### Youtube Logic ######
|
#### Youtube Logic ######
|
||||||
#########################
|
#########################
|
||||||
@ -332,7 +333,8 @@ def ytsearch():
|
|||||||
if config['nginxVideoStream']:
|
if config['nginxVideoStream']:
|
||||||
channel['thumbnail'] = channel['thumbnail'].replace("~", "/")
|
channel['thumbnail'] = channel['thumbnail'].replace("~", "/")
|
||||||
hostName = urllib.parse.urlparse(channel['thumbnail']).netloc
|
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,
|
return render_template('ytsearch.html', form=form, btform=button_form, results=results,
|
||||||
restricted=config['restrictPublicUsage'], config=config, npage=next_page,
|
restricted=config['restrictPublicUsage'], config=config, npage=next_page,
|
||||||
ppage=prev_page)
|
ppage=prev_page)
|
||||||
@ -343,9 +345,7 @@ def ytsearch():
|
|||||||
@app.route('/ytfollow/<channelId>', methods=['POST'])
|
@app.route('/ytfollow/<channelId>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def ytfollow(channelId):
|
def ytfollow(channelId):
|
||||||
form = EmptyForm()
|
r = followYoutubeChannel(channelId)
|
||||||
if form.validate_on_submit():
|
|
||||||
r = followYoutubeChannel(channelId)
|
|
||||||
return redirect(request.referrer)
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
@ -377,9 +377,7 @@ def followYoutubeChannel(channelId):
|
|||||||
@app.route('/ytunfollow/<channelId>', methods=['POST'])
|
@app.route('/ytunfollow/<channelId>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def ytunfollow(channelId):
|
def ytunfollow(channelId):
|
||||||
form = EmptyForm()
|
unfollowYoutubeChannel(channelId)
|
||||||
if form.validate_on_submit():
|
|
||||||
unfollowYoutubeChannel(channelId)
|
|
||||||
return redirect(request.referrer)
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
@ -405,27 +403,38 @@ def unfollowYoutubeChannel(channelId):
|
|||||||
def channel(id):
|
def channel(id):
|
||||||
form = ChannelForm()
|
form = ChannelForm()
|
||||||
button_form = EmptyForm()
|
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']:
|
if config['nginxVideoStream']:
|
||||||
hostName = urllib.parse.urlparse(video['videoThumb']).netloc
|
hostName = urllib.parse.urlparse(video['thumbnail'][1:]).netloc
|
||||||
video['videoThumb'] = video['videoThumb'].replace("https://{}".format(hostName), "").replace("hqdefault",
|
video['thumbnail'] = video['thumbnail'].replace("https://{}".format(hostName), "")[1:].replace("hqdefault",
|
||||||
"mqdefault") + "&host=" + hostName
|
"mqdefault") + "&host=" + hostName
|
||||||
else:
|
else:
|
||||||
video['videoThumb'] = video['videoThumb'].replace('/', '~')
|
video['thumbnail'] = video['thumbnail'].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('/', '~')
|
|
||||||
|
|
||||||
return render_template('channel.html', form=form, btform=button_form, channel=channelData[0], videos=channelData[1],
|
if config['nginxVideoStream']:
|
||||||
restricted=config['restrictPublicUsage'], config=config)
|
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):
|
def get_best_urls(urls):
|
||||||
@ -474,8 +483,9 @@ def watch():
|
|||||||
if videocomments is not None:
|
if videocomments is not None:
|
||||||
videocomments.sort(key=lambda x: x['likes'], reverse=True)
|
videocomments.sort(key=lambda x: x['likes'], reverse=True)
|
||||||
|
|
||||||
info['rating'] = str((info['like_count']/(info['like_count']+info['dislike_count']))*100)[0:4]
|
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)
|
return render_template("video.html", info=info, title='{}'.format(info['title']), config=config,
|
||||||
|
videocomments=videocomments)
|
||||||
|
|
||||||
|
|
||||||
def markupString(string):
|
def markupString(string):
|
||||||
|
@ -1,58 +1,94 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="blue ui centered card">
|
<div class="ui center aligned text container">
|
||||||
<div class="content">
|
<div class="ui centered vertical segment">
|
||||||
<div class="center aligned author">
|
<h2 class="ui header">
|
||||||
{%if config.nginxVideoStream%}
|
<img src="https://yotter.xyz{{data.avatar}}" class="ui circular image">
|
||||||
<img alt="Thumbnail" src="{{channel.avatar}}">
|
{{data.channel_name}}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="ui vertical segment">
|
||||||
|
<p>{{data.short_description}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui vertical segment">
|
||||||
|
<div class="ui tiny statistic">
|
||||||
|
<div class="value">
|
||||||
|
{%if data.approx_suscriber_count == None%}
|
||||||
|
<i class="user icon"></i> ?
|
||||||
{%else%}
|
{%else%}
|
||||||
<img alt="Thumbnail" src="/img/{{channel.avatar.replace('/', '~')}}">
|
<i class="user icon"></i> {{data.approx_subscriber_count}}
|
||||||
{%endif%}
|
{%endif%}
|
||||||
</div>
|
</div>
|
||||||
<div class="center aligned header"><a href="">{{channel.name}}</a></div>
|
<div class="label">
|
||||||
<div class="center aligned description">
|
Followers
|
||||||
<div class="statistic">
|
|
||||||
<div class="value">
|
|
||||||
<i class="users icon"></i>{{channel.subCount}}
|
|
||||||
</div>
|
|
||||||
<div class="label">
|
|
||||||
Followers
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if restricted or current_user.is_authenticated %}
|
{% if restricted or current_user.is_authenticated %}
|
||||||
<div class="center aligned extra content">
|
{% if not current_user.is_following_yt(data.channel_id) %}
|
||||||
{% if not current_user.is_following_yt(channel.id) %}
|
<form action="{{ url_for('ytfollow', channelId=data.channel_id) }}" method="post">
|
||||||
<p>
|
<button type="submit" value="Submit" class="ui red button">
|
||||||
<form action="{{ url_for('ytfollow', channelId=channel.id) }}" method="post">
|
<i class="user icon"></i>
|
||||||
{{ btform.hidden_tag() }}
|
Suscribe
|
||||||
{{ btform.submit(value='Follow') }}
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</p>
|
{% else %}
|
||||||
{% else %}
|
<form action="{{ url_for('ytunfollow', channelId=data.channel_id) }}" method="post">
|
||||||
<p>
|
<button type="submit" value="Submit" class="ui red active button">
|
||||||
<form action="{{ url_for('ytunfollow', channelId=channel.id) }}" method="post">
|
<i class="user icon"></i>
|
||||||
{{ btform.hidden_tag() }}
|
Unsuscribe
|
||||||
{{ btform.submit(value='Unfollow') }}
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</p>
|
{%endif%}
|
||||||
{% endif %}
|
{%endif%}
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
{% if not videos %}
|
{% if data['error'] != None %}
|
||||||
{% include '_empty_feed.html' %}
|
{% include '_empty_feed.html' %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="ui centered cards">
|
<div class="ui centered cards">
|
||||||
{% for video in videos %}
|
{% for video in data['items'] %}
|
||||||
{% include '_video_item.html' %}
|
<div class="ui card">
|
||||||
|
<a class="image" href="{{url_for('watch', v=video.id, _method='GET')}}">
|
||||||
|
<img src="https://yotter.xyz{{video.thumbnail}}">
|
||||||
|
</a>
|
||||||
|
<div class="content">
|
||||||
|
<a class="header" href="{{url_for('watch', v=video.id, _method='GET')}}">{{video.title}}</a>
|
||||||
|
<div class="meta">
|
||||||
|
<a class="break-word" href="{{url_for('channel', id=video.channel_id)}}">{{data.channel_name}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra content">
|
||||||
|
<span class="left floated like">
|
||||||
|
<i class="eye icon"></i>
|
||||||
|
{{video.approx_view_count}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{%if video.duration == "PREMIERING NOW" or video.duration == "LIVE"%}
|
||||||
|
<span class="right floated star">
|
||||||
|
<i class="red circle icon"></i>
|
||||||
|
LIVE
|
||||||
|
</span>
|
||||||
|
{%else%}
|
||||||
|
<span class="right floated star">
|
||||||
|
<i class="clock icon"></i>
|
||||||
|
{{video.time_published}}
|
||||||
|
</span>
|
||||||
|
{%endif%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div class="ui center aligned text container">
|
||||||
|
<a href="{{prev_page}}"> <button class="ui left attached button"><i class="angle red left icon"></i></button> </a>
|
||||||
|
<a href="{{next_page}}"> <button class="right attached ui button"><i class="angle red right icon"></i></button></a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,20 +1,16 @@
|
|||||||
import base64
|
import base64
|
||||||
from youtube import util, yt_data_extract, local_playlist, subscriptions
|
|
||||||
from youtube import yt_app
|
|
||||||
|
|
||||||
import urllib
|
|
||||||
import json
|
import json
|
||||||
from string import Template
|
|
||||||
import youtube.proto as proto
|
|
||||||
import html
|
|
||||||
import math
|
import math
|
||||||
import gevent
|
|
||||||
import re
|
import re
|
||||||
import cachetools.func
|
|
||||||
import traceback
|
import traceback
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
import cachetools.func
|
||||||
import flask
|
import flask
|
||||||
from flask import request
|
import gevent
|
||||||
|
|
||||||
|
import youtube.proto as proto
|
||||||
|
from youtube import util, yt_data_extract
|
||||||
|
|
||||||
headers_desktop = (
|
headers_desktop = (
|
||||||
('Accept', '*/*'),
|
('Accept', '*/*'),
|
||||||
@ -109,7 +105,7 @@ def channel_ctoken_v1(channel_id, page, sort, tab, view=1):
|
|||||||
|
|
||||||
return base64.urlsafe_b64encode(pointless_nest).decode('ascii')
|
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
|
message = 'Got channel tab' if print_status else None
|
||||||
|
|
||||||
if int(sort) == 2 and int(page) > 1:
|
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,
|
headers_desktop + generic_cookie,
|
||||||
debug_name='channel_tab', report_text=message)
|
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
|
# cache entries expire after 30 minutes
|
||||||
@cachetools.func.ttl_cache(maxsize=128, ttl=30*60)
|
@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
|
**info
|
||||||
)
|
)
|
||||||
|
|
||||||
@yt_app.route('/channel/<channel_id>/')
|
|
||||||
@yt_app.route('/channel/<channel_id>/<tab>')
|
|
||||||
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/<username>/')
|
|
||||||
@yt_app.route('/user/<username>/<tab>')
|
|
||||||
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/<custom>/')
|
|
||||||
@yt_app.route('/c/<custom>/<tab>')
|
|
||||||
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('/<custom>')
|
|
||||||
@yt_app.route('/<custom>/<tab>')
|
|
||||||
def get_toplevel_custom_page(custom, tab='videos'):
|
|
||||||
return get_channel_page_general_url('https://www.youtube.com/' + custom, tab, request)
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from youtube_data import proto
|
from youtube import proto
|
||||||
from flask import Markup as mk
|
from flask import Markup as mk
|
||||||
import requests
|
import requests
|
||||||
import base64
|
import base64
|
Reference in New Issue
Block a user