Rename ycdl.py -> ycdldb.py; add exceptions.py.
This commit is contained in:
parent
3d3da805b6
commit
b48c2fc37c
4 changed files with 94 additions and 44 deletions
|
@ -29,7 +29,7 @@ STATIC_DIR = root_dir.with_child('static')
|
||||||
FAVICON_PATH = STATIC_DIR.with_child('favicon.png')
|
FAVICON_PATH = STATIC_DIR.with_child('favicon.png')
|
||||||
|
|
||||||
youtube_core = ycdl.ytapi.Youtube(bot.get_youtube_key())
|
youtube_core = ycdl.ytapi.Youtube(bot.get_youtube_key())
|
||||||
youtube = ycdl.YCDL(youtube_core)
|
ycdldb = ycdl.ycdldb.YCDLDB(youtube_core)
|
||||||
|
|
||||||
site = flask.Flask(
|
site = flask.Flask(
|
||||||
__name__,
|
__name__,
|
||||||
|
@ -136,9 +136,9 @@ def favicon():
|
||||||
|
|
||||||
@site.route('/channels')
|
@site.route('/channels')
|
||||||
def get_channels():
|
def get_channels():
|
||||||
channels = youtube.get_channels()
|
channels = ycdldb.get_channels()
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
channel['has_pending'] = youtube.channel_has_pending(channel['id'])
|
channel['has_pending'] = ycdldb.channel_has_pending(channel['id'])
|
||||||
return flask.render_template('channels.html', channels=channels)
|
return flask.render_template('channels.html', channels=channels)
|
||||||
|
|
||||||
@site.route('/videos')
|
@site.route('/videos')
|
||||||
|
@ -149,14 +149,14 @@ def get_channels():
|
||||||
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:
|
try:
|
||||||
youtube.add_channel(channel_id)
|
ycdldb.add_channel(channel_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
channel = youtube.get_channel(channel_id)
|
channel = ycdldb.get_channel(channel_id)
|
||||||
else:
|
else:
|
||||||
channel = None
|
channel = None
|
||||||
|
|
||||||
videos = youtube.get_videos(channel_id=channel_id, download_filter=download_filter)
|
videos = ycdldb.get_videos(channel_id=channel_id, download_filter=download_filter)
|
||||||
|
|
||||||
search_terms = request.args.get('q', '').lower().strip().replace('+', ' ').split()
|
search_terms = request.args.get('q', '').lower().strip().replace('+', ' ').split()
|
||||||
if search_terms:
|
if search_terms:
|
||||||
|
@ -164,8 +164,8 @@ def get_channel(channel_id=None, download_filter=None):
|
||||||
|
|
||||||
video_id = request.args.get('v', '')
|
video_id = request.args.get('v', '')
|
||||||
if video_id:
|
if video_id:
|
||||||
youtube.insert_video(video_id)
|
ycdldb.insert_video(video_id)
|
||||||
videos = [youtube.get_video(video_id)]
|
videos = [ycdldb.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:
|
||||||
|
@ -184,8 +184,8 @@ def get_channel(channel_id=None, download_filter=None):
|
||||||
'channel.html',
|
'channel.html',
|
||||||
channel=channel,
|
channel=channel,
|
||||||
download_filter=download_filter,
|
download_filter=download_filter,
|
||||||
videos=videos,
|
|
||||||
query_string='?' + request.query_string.decode('utf-8'),
|
query_string='?' + request.query_string.decode('utf-8'),
|
||||||
|
videos=videos,
|
||||||
)
|
)
|
||||||
|
|
||||||
@site.route('/mark_video_state', methods=['POST'])
|
@site.route('/mark_video_state', methods=['POST'])
|
||||||
|
@ -197,16 +197,16 @@ def post_mark_video_state():
|
||||||
try:
|
try:
|
||||||
video_ids = video_ids.split(',')
|
video_ids = video_ids.split(',')
|
||||||
for video_id in video_ids:
|
for video_id in video_ids:
|
||||||
youtube.mark_video_state(video_id, state, commit=False)
|
ycdldb.mark_video_state(video_id, state, commit=False)
|
||||||
youtube.sql.commit()
|
ycdldb.sql.commit()
|
||||||
|
|
||||||
except ycdl.NoSuchVideo:
|
except ycdl.exceptions.NoSuchVideo:
|
||||||
youtube.rollback()
|
ycdldb.rollback()
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
except ycdl.InvalidVideoState:
|
except ycdl.exceptions.InvalidVideoState:
|
||||||
youtube.rollback()
|
ycdldb.rollback()
|
||||||
flask.abort(400)
|
flask.abort(400)
|
||||||
|
|
||||||
return make_json_response({'video_ids': video_ids, 'state': state})
|
return make_json_response({'video_ids': video_ids, 'state': state})
|
||||||
|
@ -215,7 +215,7 @@ def post_mark_video_state():
|
||||||
def post_refresh_all_channels():
|
def post_refresh_all_channels():
|
||||||
force = request.form.get('force', False)
|
force = request.form.get('force', False)
|
||||||
force = ycdl.helpers.truthystring(force)
|
force = ycdl.helpers.truthystring(force)
|
||||||
youtube.refresh_all_channels(force=force)
|
ycdldb.refresh_all_channels(force=force)
|
||||||
return make_json_response({})
|
return make_json_response({})
|
||||||
|
|
||||||
@site.route('/refresh_channel', methods=['POST'])
|
@site.route('/refresh_channel', methods=['POST'])
|
||||||
|
@ -229,14 +229,14 @@ def post_refresh_channel():
|
||||||
if not (len(channel_id) == 24 and channel_id.startswith('UC')):
|
if not (len(channel_id) == 24 and channel_id.startswith('UC')):
|
||||||
# It seems they have given us a username instead.
|
# It seems they have given us a username instead.
|
||||||
try:
|
try:
|
||||||
channel_id = youtube.youtube.get_user_id(username=channel_id)
|
channel_id = ycdldb.youtube.get_user_id(username=channel_id)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
force = request.form.get('force', False)
|
force = request.form.get('force', False)
|
||||||
force = ycdl.helpers.truthystring(force)
|
force = ycdl.helpers.truthystring(force)
|
||||||
youtube.add_channel(channel_id, commit=False)
|
ycdldb.add_channel(channel_id, commit=False)
|
||||||
youtube.refresh_channel(channel_id, force=force)
|
ycdldb.refresh_channel(channel_id, force=force)
|
||||||
return make_json_response({})
|
return make_json_response({})
|
||||||
|
|
||||||
@site.route('/start_download', methods=['POST'])
|
@site.route('/start_download', methods=['POST'])
|
||||||
|
@ -247,11 +247,11 @@ def post_start_download():
|
||||||
try:
|
try:
|
||||||
video_ids = video_ids.split(',')
|
video_ids = video_ids.split(',')
|
||||||
for video_id in video_ids:
|
for video_id in video_ids:
|
||||||
youtube.download_video(video_id, commit=False)
|
ycdldb.download_video(video_id, commit=False)
|
||||||
youtube.sql.commit()
|
ycdldb.sql.commit()
|
||||||
|
|
||||||
except ycdl.ytapi.VideoNotFound:
|
except ycdl.ytapi.VideoNotFound:
|
||||||
youtube.rollback()
|
ycdldb.rollback()
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
return make_json_response({'video_ids': video_ids, 'state': 'downloaded'})
|
return make_json_response({'video_ids': video_ids, 'state': 'downloaded'})
|
||||||
|
@ -266,7 +266,7 @@ def refresher_thread():
|
||||||
time.sleep(60 * 60 * 6)
|
time.sleep(60 * 60 * 6)
|
||||||
print('Starting refresh job.')
|
print('Starting refresh job.')
|
||||||
thread_kwargs = {'force': False, 'skip_failures': True}
|
thread_kwargs = {'force': False, 'skip_failures': True}
|
||||||
refresh_job = threading.Thread(target=youtube.refresh_all_channels, kwargs=thread_kwargs, daemon=True)
|
refresh_job = threading.Thread(target=ycdldb.refresh_all_channels, kwargs=thread_kwargs, daemon=True)
|
||||||
refresh_job.start()
|
refresh_job.start()
|
||||||
|
|
||||||
refresher = threading.Thread(target=refresher_thread, daemon=True)
|
refresher = threading.Thread(target=refresher_thread, daemon=True)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
from . import exceptions
|
||||||
from . import helpers
|
from . import helpers
|
||||||
from . import ycdl
|
from . import ycdldb
|
||||||
from . import ytapi
|
from . import ytapi
|
||||||
|
|
||||||
YCDL = ycdl.YCDL
|
|
||||||
|
|
53
ycdl/exceptions.py
Normal file
53
ycdl/exceptions.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import re
|
||||||
|
|
||||||
|
def pascal_to_loudsnakes(text):
|
||||||
|
'''
|
||||||
|
NoSuchPhoto -> NO_SUCH_PHOTO
|
||||||
|
'''
|
||||||
|
match = re.findall(r'[A-Z][a-z]*', text)
|
||||||
|
text = '_'.join(match)
|
||||||
|
text = text.upper()
|
||||||
|
return text
|
||||||
|
|
||||||
|
class ErrorTypeAdder(type):
|
||||||
|
'''
|
||||||
|
During definition, the Exception class will automatically receive a class
|
||||||
|
attribute called `error_type` which is just the class's name as a string
|
||||||
|
in the loudsnake casing style. NoSuchPhoto -> NO_SUCH_PHOTO.
|
||||||
|
|
||||||
|
This is used for serialization of the exception object and should
|
||||||
|
basically act as a status code when displaying the error to the user.
|
||||||
|
|
||||||
|
Thanks Unutbu
|
||||||
|
http://stackoverflow.com/a/18126678
|
||||||
|
'''
|
||||||
|
def __init__(cls, name, bases, clsdict):
|
||||||
|
type.__init__(cls, name, bases, clsdict)
|
||||||
|
cls.error_type = pascal_to_loudsnakes(name)
|
||||||
|
|
||||||
|
class YCDLException(Exception, metaclass=ErrorTypeAdder):
|
||||||
|
'''
|
||||||
|
Subtypes should have a class attribute `error_message`. The error message
|
||||||
|
may contain {format} strings which will be formatted using the
|
||||||
|
Exception's constructor arguments.
|
||||||
|
'''
|
||||||
|
error_message = ''
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__()
|
||||||
|
self.given_args = args
|
||||||
|
self.given_kwargs = kwargs
|
||||||
|
self.error_message = self.error_message.format(*args, **kwargs)
|
||||||
|
self.args = (self.error_message, args, kwargs)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.error_type + '\n' + self.error_message
|
||||||
|
|
||||||
|
class InvalidVideoState(YCDLException):
|
||||||
|
error_message = '{} is not a valid state.'
|
||||||
|
|
||||||
|
class NoSuchVideo(YCDLException):
|
||||||
|
error_message = 'Video {} does not exist.'
|
||||||
|
|
||||||
|
class DatabaseOutOfDate(YCDLException):
|
||||||
|
error_message = 'Database is out-of-date. {current} should be {new}.'
|
|
@ -3,6 +3,7 @@ import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from . import exceptions
|
||||||
from . import helpers
|
from . import helpers
|
||||||
from . import ytapi
|
from . import ytapi
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ from voussoirkit import sqlhelpers
|
||||||
|
|
||||||
|
|
||||||
def YOUTUBE_DL_COMMAND(video_id):
|
def YOUTUBE_DL_COMMAND(video_id):
|
||||||
path = 'D:\\Incoming\\ytqueue\\{id}.ytqueue'.format(id=video_id)
|
path = f'D:\\Incoming\\ytqueue\\{video_id}.ytqueue'
|
||||||
open(path, 'w')
|
open(path, 'w')
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
@ -74,9 +75,6 @@ SQL_VIDEO = {key:index for (index, key) in enumerate(SQL_VIDEO_COLUMNS)}
|
||||||
|
|
||||||
DEFAULT_DBNAME = 'ycdl.db'
|
DEFAULT_DBNAME = 'ycdl.db'
|
||||||
|
|
||||||
ERROR_DATABASE_OUTOFDATE = 'Database is out-of-date. {current} should be {new}.'
|
|
||||||
|
|
||||||
|
|
||||||
def assert_is_abspath(path):
|
def assert_is_abspath(path):
|
||||||
'''
|
'''
|
||||||
TO DO: Determine whether this is actually correct.
|
TO DO: Determine whether this is actually correct.
|
||||||
|
@ -85,13 +83,7 @@ def assert_is_abspath(path):
|
||||||
raise ValueError('Not an abspath')
|
raise ValueError('Not an abspath')
|
||||||
|
|
||||||
|
|
||||||
class InvalidVideoState(Exception):
|
class YCDLDB:
|
||||||
pass
|
|
||||||
|
|
||||||
class NoSuchVideo(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class YCDL:
|
|
||||||
def __init__(self, youtube, database_filename=None, youtube_dl_function=None):
|
def __init__(self, youtube, database_filename=None, youtube_dl_function=None):
|
||||||
self.youtube = youtube
|
self.youtube = youtube
|
||||||
if database_filename is None:
|
if database_filename is None:
|
||||||
|
@ -105,10 +97,7 @@ class YCDL:
|
||||||
self.cur.execute('PRAGMA user_version')
|
self.cur.execute('PRAGMA user_version')
|
||||||
existing_version = self.cur.fetchone()[0]
|
existing_version = self.cur.fetchone()[0]
|
||||||
if existing_version != DATABASE_VERSION:
|
if existing_version != DATABASE_VERSION:
|
||||||
message = ERROR_DATABASE_OUTOFDATE
|
raise exceptions.DatabaseOutOfDate(current=existing_version, new=DATABASE_VERSION)
|
||||||
message = message.format(current=existing_version, new=DATABASE_VERSION)
|
|
||||||
print(message)
|
|
||||||
raise SystemExit
|
|
||||||
|
|
||||||
if youtube_dl_function:
|
if youtube_dl_function:
|
||||||
self.youtube_dl_function = youtube_dl_function
|
self.youtube_dl_function = youtube_dl_function
|
||||||
|
@ -203,6 +192,15 @@ class YCDL:
|
||||||
if commit:
|
if commit:
|
||||||
self.sql.commit()
|
self.sql.commit()
|
||||||
|
|
||||||
|
def get_all_states(self):
|
||||||
|
query = 'SELECT DISTINCT download FROM videos'
|
||||||
|
self.cur.execute(query)
|
||||||
|
states = self.cur.fetchall()
|
||||||
|
if states is None:
|
||||||
|
return []
|
||||||
|
states = [row[0] for row in states]
|
||||||
|
return sorted(states)
|
||||||
|
|
||||||
def get_channel(self, channel_id):
|
def get_channel(self, channel_id):
|
||||||
self.cur.execute('SELECT * FROM channels WHERE id == ?', [channel_id])
|
self.cur.execute('SELECT * FROM channels WHERE id == ?', [channel_id])
|
||||||
fetch = self.cur.fetchone()
|
fetch = self.cur.fetchone()
|
||||||
|
@ -294,11 +292,11 @@ class YCDL:
|
||||||
'''
|
'''
|
||||||
Mark the video as ignored, pending, or downloaded.
|
Mark the video as ignored, pending, or downloaded.
|
||||||
'''
|
'''
|
||||||
if state not in ['ignored', 'pending', 'downloaded']:
|
if state not in ['ignored', 'pending', 'downloaded', 'coldstorage']:
|
||||||
raise InvalidVideoState(state)
|
raise exceptions.InvalidVideoState(state)
|
||||||
self.cur.execute('SELECT * FROM videos WHERE id == ?', [video_id])
|
self.cur.execute('SELECT * FROM videos WHERE id == ?', [video_id])
|
||||||
if self.cur.fetchone() is None:
|
if self.cur.fetchone() is None:
|
||||||
raise NoSuchVideo(video_id)
|
raise exceptions.NoSuchVideo(video_id)
|
||||||
self.cur.execute('UPDATE videos SET download = ? WHERE id == ?', [state, video_id])
|
self.cur.execute('UPDATE videos SET download = ? WHERE id == ?', [state, video_id])
|
||||||
if commit:
|
if commit:
|
||||||
self.sql.commit()
|
self.sql.commit()
|
Loading…
Reference in a new issue