It's been a while
This commit is contained in:
parent
72e121d585
commit
ba1961349c
19 changed files with 117 additions and 66 deletions
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
@ -77,15 +77,15 @@
|
||||||
onclick="refresh_channel('{{channel['id']}}', false, function(){location.reload()})">Refresh new videos</button></span>
|
onclick="refresh_channel('{{channel['id']}}', false, function(){location.reload()})">Refresh new videos</button></span>
|
||||||
<span><button class="refresh_button"
|
<span><button class="refresh_button"
|
||||||
onclick="refresh_channel('{{channel['id']}}', true, function(){location.reload()})">Refresh everything</button></span>
|
onclick="refresh_channel('{{channel['id']}}', true, function(){location.reload()})">Refresh everything</button></span>
|
||||||
<span><a href="/channel/{{channel['id']}}">All</a></span>
|
<span><a href="/channel/{{channel['id']}}{{query_string}}">All</a></span>
|
||||||
<span><a href="/channel/{{channel['id']}}/pending">Pending</a></span>
|
<span><a href="/channel/{{channel['id']}}/pending{{query_string}}">Pending</a></span>
|
||||||
<span><a href="/channel/{{channel['id']}}/ignored">Ignored</a></span>
|
<span><a href="/channel/{{channel['id']}}/ignored{{query_string}}">Ignored</a></span>
|
||||||
<span><a href="/channel/{{channel['id']}}/downloaded">Downloaded</a></span>
|
<span><a href="/channel/{{channel['id']}}/downloaded{{query_string}}">Downloaded</a></span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span><a href="/videos">All</a></span>
|
<span><a href="/videos{{query_string}}">All</a></span>
|
||||||
<span><a href="/videos/pending">Pending</a></span>
|
<span><a href="/videos/pending{{query_string}}">Pending</a></span>
|
||||||
<span><a href="/videos/ignored">Ignored</a></span>
|
<span><a href="/videos/ignored{{query_string}}">Ignored</a></span>
|
||||||
<span><a href="/videos/downloaded">Downloaded</a></span>
|
<span><a href="/videos/downloaded{{query_string}}">Downloaded</a></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span>{{videos|length}} items</span>
|
<span>{{videos|length}} items</span>
|
||||||
|
|
3
frontends/ycdl_flask/ycdl_flask/__init__.py
Normal file
3
frontends/ycdl_flask/ycdl_flask/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from . import ycdl_flask
|
||||||
|
|
||||||
|
site = ycdl_flask.site
|
|
@ -2,22 +2,36 @@
|
||||||
Do not execute this file directly.
|
Do not execute this file directly.
|
||||||
Use ycdl_launch.py to start the server with gevent.
|
Use ycdl_launch.py to start the server with gevent.
|
||||||
'''
|
'''
|
||||||
|
import logging
|
||||||
|
logging.getLogger('googleapicliet.discovery_cache').setLevel(logging.ERROR)
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import flask
|
import flask
|
||||||
from flask import request
|
from flask import request
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
import bot
|
import bot
|
||||||
import helpers
|
|
||||||
import ycdl
|
import ycdl
|
||||||
import ytapi
|
|
||||||
|
|
||||||
youtube_core = ytapi.Youtube(bot.YOUTUBE_KEY)
|
from voussoirkit import pathclass
|
||||||
|
|
||||||
|
root_dir = pathclass.Path(__file__).parent.parent
|
||||||
|
|
||||||
|
TEMPLATE_DIR = root_dir.with_child('templates')
|
||||||
|
STATIC_DIR = root_dir.with_child('static')
|
||||||
|
FAVICON_PATH = STATIC_DIR.with_child('favicon.png')
|
||||||
|
|
||||||
|
youtube_core = ycdl.ytapi.Youtube(bot.YOUTUBE_KEY)
|
||||||
youtube = ycdl.YCDL(youtube_core)
|
youtube = ycdl.YCDL(youtube_core)
|
||||||
|
|
||||||
site = flask.Flask(__name__)
|
site = flask.Flask(
|
||||||
|
__name__,
|
||||||
|
template_folder=TEMPLATE_DIR.absolute_path,
|
||||||
|
static_folder=STATIC_DIR.absolute_path,
|
||||||
|
)
|
||||||
site.config.update(
|
site.config.update(
|
||||||
SEND_FILE_MAX_AGE_DEFAULT=180,
|
SEND_FILE_MAX_AGE_DEFAULT=180,
|
||||||
TEMPLATES_AUTO_RELOAD=True,
|
TEMPLATES_AUTO_RELOAD=True,
|
||||||
|
@ -91,7 +105,7 @@ def send_file(filepath):
|
||||||
if request.method == 'HEAD':
|
if request.method == 'HEAD':
|
||||||
outgoing_data = bytes()
|
outgoing_data = bytes()
|
||||||
else:
|
else:
|
||||||
outgoing_data = helpers.read_filebytes(filepath, range_min=range_min, range_max=range_max)
|
outgoing_data = ycdl.helpers.read_filebytes(filepath, range_min=range_min, range_max=range_max)
|
||||||
|
|
||||||
response = flask.Response(
|
response = flask.Response(
|
||||||
outgoing_data,
|
outgoing_data,
|
||||||
|
@ -112,8 +126,8 @@ def root():
|
||||||
@site.route('/favicon.ico')
|
@site.route('/favicon.ico')
|
||||||
@site.route('/favicon.png')
|
@site.route('/favicon.png')
|
||||||
def favicon():
|
def favicon():
|
||||||
filename = os.path.join('static', 'favicon.png')
|
return flask.send_file(FAVICON_PATH.absolute_path)
|
||||||
return flask.send_file(filename)
|
|
||||||
|
|
||||||
@site.route('/channels')
|
@site.route('/channels')
|
||||||
def get_channels():
|
def get_channels():
|
||||||
|
@ -123,24 +137,30 @@ def get_channels():
|
||||||
return flask.render_template('channels.html', channels=channels)
|
return flask.render_template('channels.html', channels=channels)
|
||||||
|
|
||||||
@site.route('/videos')
|
@site.route('/videos')
|
||||||
|
@site.route('/watch')
|
||||||
@site.route('/videos/<download_filter>')
|
@site.route('/videos/<download_filter>')
|
||||||
@site.route('/channel/<channel_id>')
|
@site.route('/channel/<channel_id>')
|
||||||
@site.route('/channel/<channel_id>/<download_filter>')
|
@site.route('/channel/<channel_id>/<download_filter>')
|
||||||
def get_channel(channel_id=None, download_filter=None):
|
def get_channel(channel_id=None, download_filter=None):
|
||||||
if channel_id is not None:
|
if channel_id is not None:
|
||||||
|
try:
|
||||||
youtube.add_channel(channel_id)
|
youtube.add_channel(channel_id)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
channel = youtube.get_channel(channel_id)
|
channel = youtube.get_channel(channel_id)
|
||||||
if channel is None:
|
|
||||||
flask.abort(404)
|
|
||||||
else:
|
else:
|
||||||
channel = None
|
channel = None
|
||||||
|
|
||||||
videos = youtube.get_videos(channel_id=channel_id, download_filter=download_filter)
|
videos = youtube.get_videos(channel_id=channel_id, download_filter=download_filter)
|
||||||
|
|
||||||
search_term = request.args.get('q', None)
|
search_terms = request.args.get('q', '').lower().strip().replace('+', ' ').split()
|
||||||
if search_term is not None:
|
if search_terms:
|
||||||
search_term = search_term.lower()
|
videos = [v for v in videos if all(term in v['title'].lower() for term in search_terms)]
|
||||||
videos = [v for v in videos if search_term in v['title'].lower()]
|
|
||||||
|
video_id = request.args.get('v', '')
|
||||||
|
if video_id:
|
||||||
|
youtube.insert_video(video_id)
|
||||||
|
videos = [youtube.get_video(video_id)]
|
||||||
|
|
||||||
limit = request.args.get('limit', None)
|
limit = request.args.get('limit', None)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
|
@ -155,14 +175,12 @@ def get_channel(channel_id=None, download_filter=None):
|
||||||
published = datetime.datetime.utcfromtimestamp(published)
|
published = datetime.datetime.utcfromtimestamp(published)
|
||||||
published = published.strftime('%Y %m %d')
|
published = published.strftime('%Y %m %d')
|
||||||
video['_published_str'] = published
|
video['_published_str'] = published
|
||||||
return flask.render_template('channel.html', channel=channel, videos=videos)
|
return flask.render_template(
|
||||||
|
'channel.html',
|
||||||
@site.route('/static/<filename>')
|
channel=channel,
|
||||||
def get_static(filename):
|
videos=videos,
|
||||||
filename = filename.replace('\\', os.sep)
|
query_string='?' + request.query_string.decode('utf-8'),
|
||||||
filename = filename.replace('/', os.sep)
|
)
|
||||||
filename = os.path.join('static', filename)
|
|
||||||
return flask.send_file(filename)
|
|
||||||
|
|
||||||
@site.route('/mark_video_state', methods=['POST'])
|
@site.route('/mark_video_state', methods=['POST'])
|
||||||
def post_mark_video_state():
|
def post_mark_video_state():
|
||||||
|
@ -184,7 +202,7 @@ def post_mark_video_state():
|
||||||
@site.route('/refresh_all_channels', methods=['POST'])
|
@site.route('/refresh_all_channels', methods=['POST'])
|
||||||
def post_refresh_all_channels():
|
def post_refresh_all_channels():
|
||||||
force = request.form.get('force', False)
|
force = request.form.get('force', False)
|
||||||
force = helpers.truthystring(force)
|
force = ycdl.helpers.truthystring(force)
|
||||||
youtube.refresh_all_channels(force=force)
|
youtube.refresh_all_channels(force=force)
|
||||||
return make_json_response({})
|
return make_json_response({})
|
||||||
|
|
||||||
|
@ -204,7 +222,7 @@ def post_refresh_channel():
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
force = request.form.get('force', False)
|
force = request.form.get('force', False)
|
||||||
force = helpers.truthystring(force)
|
force = ycdl.helpers.truthystring(force)
|
||||||
youtube.refresh_channel(channel_id, force=force)
|
youtube.refresh_channel(channel_id, force=force)
|
||||||
return make_json_response({})
|
return make_json_response({})
|
||||||
|
|
||||||
|
@ -215,7 +233,7 @@ def post_start_download():
|
||||||
video_id = request.form['video_id']
|
video_id = request.form['video_id']
|
||||||
try:
|
try:
|
||||||
youtube.download_video(video_id)
|
youtube.download_video(video_id)
|
||||||
except ytapi.VideoNotFound:
|
except ycdl.ytapi.VideoNotFound:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
return make_json_response({'video_id': video_id, 'state': 'downloaded'})
|
return make_json_response({'video_id': video_id, 'state': 'downloaded'})
|
|
@ -1,3 +1,6 @@
|
||||||
|
import logging
|
||||||
|
logging.getLogger('googleapicliet.discovery_cache').setLevel(logging.ERROR)
|
||||||
|
|
||||||
import gevent.monkey
|
import gevent.monkey
|
||||||
gevent.monkey.patch_all()
|
gevent.monkey.patch_all()
|
||||||
|
|
||||||
|
@ -5,7 +8,7 @@ import gevent.pywsgi
|
||||||
import gevent.wsgi
|
import gevent.wsgi
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import ycdl_site
|
import ycdl_flask
|
||||||
|
|
||||||
if len(sys.argv) == 2:
|
if len(sys.argv) == 2:
|
||||||
port = int(sys.argv[1])
|
port = int(sys.argv[1])
|
||||||
|
@ -15,14 +18,14 @@ else:
|
||||||
if port == 443:
|
if port == 443:
|
||||||
http = gevent.pywsgi.WSGIServer(
|
http = gevent.pywsgi.WSGIServer(
|
||||||
listener=('', port),
|
listener=('', port),
|
||||||
application=ycdl_site.site,
|
application=ycdl_flask.site,
|
||||||
keyfile='https\\flasksite.key',
|
keyfile='https\\flasksite.key',
|
||||||
certfile='https\\flasksite.crt',
|
certfile='https\\flasksite.crt',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
http = gevent.pywsgi.WSGIServer(
|
http = gevent.pywsgi.WSGIServer(
|
||||||
listener=('', port),
|
listener=('0.0.0.0', port),
|
||||||
application=ycdl_site.site,
|
application=ycdl_flask.site,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ session with these variables preloaded.
|
||||||
|
|
||||||
import bot
|
import bot
|
||||||
import ycdl
|
import ycdl
|
||||||
import ytapi
|
|
||||||
|
|
||||||
youtube_core = ytapi.Youtube(bot.YOUTUBE_KEY)
|
youtube_core = ycdl.ytapi.Youtube(bot.YOUTUBE_KEY)
|
||||||
youtube = ycdl.YCDL(youtube_core)
|
youtube = ycdl.YCDL(youtube_core)
|
45
utilities/ytqueue.py
Normal file
45
utilities/ytqueue.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
'''
|
||||||
|
I was having trouble making my Flask server perform the youtube-dl without
|
||||||
|
clogging up the other site activities. So instead I'll just have the server
|
||||||
|
export ytqueue files, which this script will download as a separate process.
|
||||||
|
|
||||||
|
Rather than maintaining a text file or database of IDs to be downloaded,
|
||||||
|
I'm fine with creating each ID as a file and letting the filesystem act
|
||||||
|
as the to-do list.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
YOUTUBE_DL = 'youtube-dlw https://www.youtube.com/watch?v={id}'
|
||||||
|
|
||||||
|
def ytqueue(only_once=False):
|
||||||
|
while True:
|
||||||
|
print(time.strftime('%H:%M:%S'), 'Looking for files.')
|
||||||
|
queue = [f for f in os.listdir() if f.endswith('.ytqueue')]
|
||||||
|
for filename in queue:
|
||||||
|
yt_id = filename.split('.')[0]
|
||||||
|
command = YOUTUBE_DL.format(id=yt_id)
|
||||||
|
exit_code = os.system(command)
|
||||||
|
if exit_code == 0:
|
||||||
|
os.remove(filename)
|
||||||
|
if only_once:
|
||||||
|
break
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
def ytqueue_argparse(args):
|
||||||
|
return ytqueue(only_once=args.once)
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument('--once', dest='once', action='store_true')
|
||||||
|
parser.set_defaults(func=ytqueue_argparse)
|
||||||
|
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
args.func(args)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(sys.argv[1:])
|
|
@ -2,10 +2,11 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
import ytapi
|
from . import helpers
|
||||||
|
from . import ytapi
|
||||||
|
|
||||||
def YOUTUBE_DL_COMMAND(video_id):
|
def YOUTUBE_DL_COMMAND(video_id):
|
||||||
path = 'C:\\Incoming\\ytqueue\\{id}.ytqueue'.format(id=video_id)
|
path = 'D:\\Incoming\\ytqueue\\{id}.ytqueue'.format(id=video_id)
|
||||||
open(path, 'w')
|
open(path, 'w')
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
@ -207,6 +208,12 @@ class YCDL:
|
||||||
channels.sort(key=lambda x: x['name'].lower())
|
channels.sort(key=lambda x: x['name'].lower())
|
||||||
return channels
|
return channels
|
||||||
|
|
||||||
|
def get_video(self, video_id):
|
||||||
|
self.cur.execute('SELECT * FROM videos WHERE id == ?', [video_id])
|
||||||
|
video = self.cur.fetchone()
|
||||||
|
video = {key: video[SQL_VIDEO[key]] for key in SQL_VIDEO}
|
||||||
|
return video
|
||||||
|
|
||||||
def get_videos(self, channel_id=None, download_filter=None):
|
def get_videos(self, channel_id=None, download_filter=None):
|
||||||
wheres = []
|
wheres = []
|
||||||
bindings = []
|
bindings = []
|
|
@ -1,7 +1,7 @@
|
||||||
import apiclient.discovery
|
import apiclient.discovery
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import helpers
|
from . import helpers
|
||||||
|
|
||||||
class VideoNotFound(Exception):
|
class VideoNotFound(Exception):
|
||||||
pass
|
pass
|
24
ytqueue.py
24
ytqueue.py
|
@ -1,24 +0,0 @@
|
||||||
'''
|
|
||||||
I was having trouble making my Flask server perform the youtube-dl without
|
|
||||||
clogging up the other site activities. So instead I'll just have the server
|
|
||||||
export ytqueue files, which this script will download as a separate process.
|
|
||||||
|
|
||||||
Rather than maintaining a text file or database of IDs to be downloaded,
|
|
||||||
I'm fine with creating each ID as a file and letting the filesystem act
|
|
||||||
as the to-do list.
|
|
||||||
'''
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
YOUTUBE_DL = 'youtube-dlw https://www.youtube.com/watch?v={id}'
|
|
||||||
|
|
||||||
while True:
|
|
||||||
print(time.strftime('%H:%M:%S'), 'Looking for files.')
|
|
||||||
queue = [f for f in os.listdir() if f.endswith('.ytqueue')]
|
|
||||||
for filename in queue:
|
|
||||||
yt_id = filename.split('.')[0]
|
|
||||||
command = YOUTUBE_DL.format(id=yt_id)
|
|
||||||
exit_code = os.system(command)
|
|
||||||
if exit_code == 0:
|
|
||||||
os.remove(filename)
|
|
||||||
time.sleep(10)
|
|
Loading…
Reference in a new issue