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')
|
||||
|
||||
youtube_core = ycdl.ytapi.Youtube(bot.get_youtube_key())
|
||||
youtube = ycdl.YCDL(youtube_core)
|
||||
ycdldb = ycdl.ycdldb.YCDLDB(youtube_core)
|
||||
|
||||
site = flask.Flask(
|
||||
__name__,
|
||||
|
@ -136,9 +136,9 @@ def favicon():
|
|||
|
||||
@site.route('/channels')
|
||||
def get_channels():
|
||||
channels = youtube.get_channels()
|
||||
channels = ycdldb.get_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)
|
||||
|
||||
@site.route('/videos')
|
||||
|
@ -149,14 +149,14 @@ def get_channels():
|
|||
def get_channel(channel_id=None, download_filter=None):
|
||||
if channel_id is not None:
|
||||
try:
|
||||
youtube.add_channel(channel_id)
|
||||
ycdldb.add_channel(channel_id)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
channel = youtube.get_channel(channel_id)
|
||||
channel = ycdldb.get_channel(channel_id)
|
||||
else:
|
||||
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()
|
||||
if search_terms:
|
||||
|
@ -164,8 +164,8 @@ def get_channel(channel_id=None, download_filter=None):
|
|||
|
||||
video_id = request.args.get('v', '')
|
||||
if video_id:
|
||||
youtube.insert_video(video_id)
|
||||
videos = [youtube.get_video(video_id)]
|
||||
ycdldb.insert_video(video_id)
|
||||
videos = [ycdldb.get_video(video_id)]
|
||||
|
||||
limit = request.args.get('limit', None)
|
||||
if limit is not None:
|
||||
|
@ -184,8 +184,8 @@ def get_channel(channel_id=None, download_filter=None):
|
|||
'channel.html',
|
||||
channel=channel,
|
||||
download_filter=download_filter,
|
||||
videos=videos,
|
||||
query_string='?' + request.query_string.decode('utf-8'),
|
||||
videos=videos,
|
||||
)
|
||||
|
||||
@site.route('/mark_video_state', methods=['POST'])
|
||||
|
@ -197,16 +197,16 @@ def post_mark_video_state():
|
|||
try:
|
||||
video_ids = video_ids.split(',')
|
||||
for video_id in video_ids:
|
||||
youtube.mark_video_state(video_id, state, commit=False)
|
||||
youtube.sql.commit()
|
||||
ycdldb.mark_video_state(video_id, state, commit=False)
|
||||
ycdldb.sql.commit()
|
||||
|
||||
except ycdl.NoSuchVideo:
|
||||
youtube.rollback()
|
||||
except ycdl.exceptions.NoSuchVideo:
|
||||
ycdldb.rollback()
|
||||
traceback.print_exc()
|
||||
flask.abort(404)
|
||||
|
||||
except ycdl.InvalidVideoState:
|
||||
youtube.rollback()
|
||||
except ycdl.exceptions.InvalidVideoState:
|
||||
ycdldb.rollback()
|
||||
flask.abort(400)
|
||||
|
||||
return make_json_response({'video_ids': video_ids, 'state': state})
|
||||
|
@ -215,7 +215,7 @@ def post_mark_video_state():
|
|||
def post_refresh_all_channels():
|
||||
force = request.form.get('force', False)
|
||||
force = ycdl.helpers.truthystring(force)
|
||||
youtube.refresh_all_channels(force=force)
|
||||
ycdldb.refresh_all_channels(force=force)
|
||||
return make_json_response({})
|
||||
|
||||
@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')):
|
||||
# It seems they have given us a username instead.
|
||||
try:
|
||||
channel_id = youtube.youtube.get_user_id(username=channel_id)
|
||||
channel_id = ycdldb.youtube.get_user_id(username=channel_id)
|
||||
except IndexError:
|
||||
flask.abort(404)
|
||||
|
||||
force = request.form.get('force', False)
|
||||
force = ycdl.helpers.truthystring(force)
|
||||
youtube.add_channel(channel_id, commit=False)
|
||||
youtube.refresh_channel(channel_id, force=force)
|
||||
ycdldb.add_channel(channel_id, commit=False)
|
||||
ycdldb.refresh_channel(channel_id, force=force)
|
||||
return make_json_response({})
|
||||
|
||||
@site.route('/start_download', methods=['POST'])
|
||||
|
@ -247,11 +247,11 @@ def post_start_download():
|
|||
try:
|
||||
video_ids = video_ids.split(',')
|
||||
for video_id in video_ids:
|
||||
youtube.download_video(video_id, commit=False)
|
||||
youtube.sql.commit()
|
||||
ycdldb.download_video(video_id, commit=False)
|
||||
ycdldb.sql.commit()
|
||||
|
||||
except ycdl.ytapi.VideoNotFound:
|
||||
youtube.rollback()
|
||||
ycdldb.rollback()
|
||||
flask.abort(404)
|
||||
|
||||
return make_json_response({'video_ids': video_ids, 'state': 'downloaded'})
|
||||
|
@ -266,7 +266,7 @@ def refresher_thread():
|
|||
time.sleep(60 * 60 * 6)
|
||||
print('Starting refresh job.')
|
||||
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()
|
||||
|
||||
refresher = threading.Thread(target=refresher_thread, daemon=True)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from . import exceptions
|
||||
from . import helpers
|
||||
from . import ycdl
|
||||
from . import ycdldb
|
||||
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 traceback
|
||||
|
||||
from . import exceptions
|
||||
from . import helpers
|
||||
from . import ytapi
|
||||
|
||||
|
@ -10,7 +11,7 @@ from voussoirkit import sqlhelpers
|
|||
|
||||
|
||||
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')
|
||||
|
||||
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'
|
||||
|
||||
ERROR_DATABASE_OUTOFDATE = 'Database is out-of-date. {current} should be {new}.'
|
||||
|
||||
|
||||
def assert_is_abspath(path):
|
||||
'''
|
||||
TO DO: Determine whether this is actually correct.
|
||||
|
@ -85,13 +83,7 @@ def assert_is_abspath(path):
|
|||
raise ValueError('Not an abspath')
|
||||
|
||||
|
||||
class InvalidVideoState(Exception):
|
||||
pass
|
||||
|
||||
class NoSuchVideo(Exception):
|
||||
pass
|
||||
|
||||
class YCDL:
|
||||
class YCDLDB:
|
||||
def __init__(self, youtube, database_filename=None, youtube_dl_function=None):
|
||||
self.youtube = youtube
|
||||
if database_filename is None:
|
||||
|
@ -105,10 +97,7 @@ class YCDL:
|
|||
self.cur.execute('PRAGMA user_version')
|
||||
existing_version = self.cur.fetchone()[0]
|
||||
if existing_version != DATABASE_VERSION:
|
||||
message = ERROR_DATABASE_OUTOFDATE
|
||||
message = message.format(current=existing_version, new=DATABASE_VERSION)
|
||||
print(message)
|
||||
raise SystemExit
|
||||
raise exceptions.DatabaseOutOfDate(current=existing_version, new=DATABASE_VERSION)
|
||||
|
||||
if youtube_dl_function:
|
||||
self.youtube_dl_function = youtube_dl_function
|
||||
|
@ -203,6 +192,15 @@ class YCDL:
|
|||
if 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):
|
||||
self.cur.execute('SELECT * FROM channels WHERE id == ?', [channel_id])
|
||||
fetch = self.cur.fetchone()
|
||||
|
@ -294,11 +292,11 @@ class YCDL:
|
|||
'''
|
||||
Mark the video as ignored, pending, or downloaded.
|
||||
'''
|
||||
if state not in ['ignored', 'pending', 'downloaded']:
|
||||
raise InvalidVideoState(state)
|
||||
if state not in ['ignored', 'pending', 'downloaded', 'coldstorage']:
|
||||
raise exceptions.InvalidVideoState(state)
|
||||
self.cur.execute('SELECT * FROM videos WHERE id == ?', [video_id])
|
||||
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])
|
||||
if commit:
|
||||
self.sql.commit()
|
Loading…
Reference in a new issue