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