def save_video(self) -> Union[None, str]: """save the Video instance to the database Return: None: success str: test mode message """ if self._mode == 'test': return 'Data cannot be saved in test mode' dct = self.__dict__ doc = {} # pop the private modifier of the attribute name before saving for k, v in dct.items(): if k == '_video_id': doc['_id'] = v elif k == '_processed': doc['Processed'] = v else: dk = k.lstrip('_') doc[dk] = v COLLECTION.replace_one({'_id': self._video_id}, doc) logger.info('%s updated', self._video_id) return None
def get_video(self) -> None: """ Download video and write output to folder """ self.create_path() output_folder = self.get_path()['path'] COLLECTION.update_one({'_id': self.video_id}, {'$set': { 'path': output_folder }}) y = YoutubeDl(output_folder=output_folder, mode=self.mode) ydl = y.ydl """ Update the processing attribute to True whilst the video is being downloaded so that other processes don't attempt to download the video whilst it's in progress. Finally update the processed Flag to true and processing Flag to False """ try: with ydl: self.vid.processing = True info_dict = ydl.extract_info(self.video_id) ydl.process_info(info_dict) self.vid.processing = False self.vid.processed = True self.vid.file_path = output_folder self.vid.save_video() except (DownloadError, SameFileError) as e: logger.error(e) logger.info('failed to download video %s', self.video_id)
def setUpClass(cls): # update query to file objects with different field states query = COLLECTION.find_one() query['_id'] = 'testing' COLLECTION.insert_one(query) copy = COLLECTION.find_one({'_id': 'testing'}) cls.videoObj = VideoSearch.query_result_to_video(copy)
def write_nfo(self) -> None: """ Create an nfo file and write to path""" i = COLLECTION.find_one({'_id': self.video_id}, { '_id': True, 'uploader': True, 'title': True, 'description': True, 'upload_date': True }) try: with open(os.path.join(APP_PATH, 'template.nfo'), 'r') as fl: template = Template(fl.read()) except FileNotFoundError as e: logger.error(e) out_template = template.substitute(unique_id=i['_id'], studio=i['uploader'], title=i['title'], plot=i['description'], date_prem=i['upload_date']) path = os.path.join(self.get_path()['path'], 'tvshow.nfo') logger.info('writing nfo to path %s', path) with open(path, 'w') as fl: fl.write(out_template)
def delete_video(self, check: bool = False) -> Union[Tuple, str]: """Remove the current object from the database along with any files or folders Args: check (bool): a value to be explicitly set in order for the video to be deleted Returns: tuple: (int: 0, str: '') - the document was not found tuple: (int: 1, str: '') - the document was found and deleted but had not path key tuple: (int: 2, str: '') - the document and path were found both were deleted str: check did not pass """ if self._mode == 'test': return 'Data cannot be deleted in test mode' if not check: return '' result = COLLECTION.find_one({'_id': self.video_id}, { '_id': True, 'path': True }) if not result: res = (0, '') elif 'path' not in result.keys(): COLLECTION.delete_one({'_id': self.video_id}) res = (1, '') logger.info('video_id %s deleted - no folder found', self.video_id) else: COLLECTION.delete_one({'_id': self.video_id}) path: str = result['path'] try: shutil.rmtree(os.path.split(path)[0], ignore_errors=True) except FileNotFoundError as e: logger.error(e) res = (2, path) logger.info('video_id %s deleted - folder %s deleted', self.video_id, path) return res
def get_thumbnail(self) -> None: # TODO fix path to use same as video_downloader """Download thumbnail to path """ url = COLLECTION.find_one({'_id': self.video_id}, {'thumbnail': True})['thumbnail'] data = requests.get(url, stream=True) image_data = Image.open(io.BytesIO(data.content)) path = self.get_path()['path'] logger.info('writing thumbnail to path %s', path) image_data.save(os.path.join(path, 'thumbnail.jpg'), 'jpeg')
def add_queue(video_id: str, tags: list = None) -> bool: """If the video_id does not exist, insert an entry into the database from the information provided by get_video_info Args: video_id (str): The id of a youtube video tags (list): A list of tags Returns: True: item was added False: exception occurred Raises: TypeError: invalid video_id type """ ret = False vid_info = None if tags is None: tags = ['undefined'] if not isinstance(video_id, str): raise TypeError(f'{video_id} should be str not {type(video_id)}') if check_db(video_id) is True: logger.info('video_id %s already exists in database', video_id) ret = False else: vid_info = get_video_info(video_id, tags) if vid_info is not None: COLLECTION.insert_one(get_video_info(video_id, tags)) logger.info('%s successfully inserted', video_id) ret = True return ret
def get_next_unprocessed(mode: str = '') -> Union[None, Video]: """Find the next unprocessed video Args: mode (str): mode flag to pass to Video Returns: Video: A Video instance None: no value found """ res = COLLECTION.find_one({'Processed': False}) if res: ret = VideoSearch.query_result_to_video(res, mode) else: ret = None return ret
def exact_find_video(**kwarg: str) -> Union[Video, None]: """ match a value exactly Args: kwarg (dict): kv corresponding to value in db Return: Video: an instance of Video None: no value found """ search = COLLECTION.find_one(kwarg) if search: ret = VideoSearch.query_result_to_video(search) else: ret = None return ret
def get_path(self) -> dict: """ Build a path from the VIDEO_DIR, tags and title Returns: dict: {path: True || False} if path does or doesn't exist Raises: TypeError: supplied tags not type str or list """ tags: list = COLLECTION.find_one({'_id': self.video_id}, {'tags': True})['tags'] sub_dir = '/'.join(tags) path: str = os.path.join(VIDEO_DIR, sub_dir, self.video_folder_name()) if os.path.exists(path): result = {'path': path, 'exists': True} else: result = {'path': path, 'exists': False} return result
def check_db(video_id: str) -> bool: """Query the database and assert if the value exists. Args: video_id (str): The id of a youtube video Returns: False: if video does not exist True: if video does exist Raises: TypeError: invalid video_id type """ if not isinstance(video_id, str): raise TypeError(f'{video_id} should be str not {type(video_id)}') result = [i['_id'] for i in COLLECTION.find({'_id': video_id})] if not result: ret = False else: ret = True return ret
#!/usr/bin/env python3 # pylint: disable=all from app.database import COLLECTION videos = [(i['title'], i['_id']) for i in COLLECTION.find({}, { 'title': True, '_id': True })] for i in videos: print(i[1] + '~$' + i[0])
def tearDownClass(cls): COLLECTION.delete_many({'_id': 'testing'})