def __init__(self, parent, title, text, ok_text, alignment=DEFAULT_ALIGNMENT, flags=Qt.WindowFlags): super(SaneDialog, self).__init__(parent, flags()) self.logger = create_logger(__name__) self.sane_parent = parent if not title: self.title = TITLE else: self.title = title if not text: self.text = TEXT else: self.text = text if not ok_text: self.ok_text = OK else: self.ok_text = ok_text self.alignment = alignment self.ok_button = QPushButton(self) self.init_ui()
def __init__(self, model): super().__init__(model) self.model = model self.widget_id = SUBFEED_VIEW_ID self.name = 'SubfeedGridViewListener' self.logger = create_logger(__name__ + '.' + self.name) self.videos_limit = model.videos_limit # Connect shared listeners self.tileWatched.connect(self.tile_watched) self.tileUnwatched.connect(self.tile_unwatched) self.tileDiscarded.connect(self.tile_discarded) self.tileUndiscarded.connect(self.tile_undiscarded) # Connect own listeners self.tileMarkedPremiere.connect(self.tile_marked_premiere) self.tileUnmarkedPremiere.connect(self.tile_unmarked_premiere) self.tileMarkedLivestreamUpcoming.connect( self.tile_marked_livestream_upcoming) self.tileUnmarkedLivestreamUpcoming.connect( self.tile_unmarked_livestream_upcoming) self.tileMarkedLivestream.connect(self.tile_marked_livestream) self.tileUnmarkedLivestream.connect(self.tile_unmarked_livestream) self.updateFromDb.connect(self.update_from_db) self.thumbnailDownload.connect(self.thumbnail_download) self.scrollReachedEnd.connect(self.scroll_reached_end)
def run(self, info): self.logger = create_logger(__name__) filename = info['filepath'] temp_filename = prepend_extension(filename, 'sanetemp') remux = ['-c', 'copy', '-map', '0:v:0', '-map', '1:a:0'] args = [remux] if info.get('audio_codec') is not None: encode_audio = ['-c', 'copy', '-map', '0:v:0', '-map', '1:a:0', '-c:1:a:0', info.get('audio_codec')] args.append(encode_audio) if info.get('video_codec') is not None: encode_video = ['-c', 'copy', '-c:0:v:0', info.get('video_codec'), '-map', '0:v:0', '-map', '1:a:0', '-c:1:a:0'] args.append(encode_video) if info.get('video_codec') is not None and info.get('audio_codec') is not None: encode_both = ['-c', 'copy', '-c:0:v:0', info.get('video_codec'), '-map', '0:v:0', '-map', '1:a:0', '-c:1:a:0', info.get('audio_codec')] args.append(encode_both) if info.get('no_remux') is not None: args.pop(0) self.logger.info('[ffmpeg] Merging formats into "%s"' % filename) self.attempt_ffmpeg(info, temp_filename, args) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) return info['__files_to_merge'], info
def __init__(self, model): super().__init__(model) self.model = model self.widget_id = PLAYBACK_VIEW_ID self.name = 'PlaybackGridViewListener' self.logger = create_logger(__name__ + '.' + self.name) self.videos_limit = model.playview_videos_limit GridViewListener.static_self = self # Assign myself to the static listener in order to communicate with DownloadView. static_grid_view_listener.STATIC_GRID_VIEW_LISTENER = self # Connect shared listeners self.tileWatched.connect(self.tile_watched) self.tileUnwatched.connect(self.tile_unwatched) self.tileDiscarded.connect(self.tile_discarded) self.tileUndiscarded.connect(self.tile_undiscarded) self.tileDeleteDownloadedData.connect(self.tile_delete_downloaded_data) # Connect own listeners self.tileDownloaded.connect(self.tile_downloaded) self.downloadFinished.connect(self.download_finished) self.downloadedVideosChangedinDB.connect(self.download_finished_in_db) self.decreaseWatchPrio.connect(self.decrease_watch_prio) self.increaseWatchPrio.connect(self.increase_watch_prio) self.updateFromDb.connect(self.update_from_db) self.thumbnailDownload.connect(self.thumbnail_download) self.scrollReachedEnd.connect(self.scroll_reached_end)
def __init__(self, parent, actions, title, text, ok_text, cancel_text, cancel_actions=None, caller=None, flags=Qt.WindowFlags): """ Prompts user for a Yes/No Confirmation where Yes results in a call for each action in actions :param parent: :param text: Text to display in dialog body. :param actions: A function, or a list of functions to be called :param caller: (If given) applies action to the caller function e.g. action(caller) :param title: Title of dialog window. :param ok_text: Text to display on OK button. :param cancel_text: Text to display on Cancel button. :param cancel_actions: Actions to perform on cancel/no, if None it will bind to reject. :param flags: """ super(SaneConfirmationDialog, self).__init__(parent, flags()) self.logger = create_logger(__name__) self.sane_parent = parent if not title: self.title = TITLE else: self.title = title if not text: self.text = TEXT else: self.text = text if not ok_text: self.ok_text = OK else: self.ok_text = ok_text if not cancel_text: self.cancel_text = CANCEL else: self.cancel_text = cancel_text if type(actions) is not list: self.action = actions else: self.action = None self.actions = actions if cancel_actions: if type(cancel_actions) is not list: self.cancel_action = cancel_actions else: self.cancel_action = None self.cancel_actions = cancel_actions # Required since cancel action is not guaranteed to exist else: self.cancel_action = None self.cancel_actions = None self.caller = caller self.cancel_button = QPushButton(self) self.ok_button = QPushButton(self) self.init_ui()
def __init__(self, app_ref=None, use_list=True, display=True): super(SaneExceptionHandler, self).__init__() self.app_ref = app_ref self.use_list = use_list self.logger = create_logger(__name__) # Back up the reference to the exception hook sys._excepthook = sys.excepthook # noinspection PyProtectedMember self.original_excepthook = sys._excepthook
def __init__(self, main_model): super(DownloadViewListener, self).__init__() DownloadViewListener.static_self = self self.logger = create_logger(__name__ + ".DownloadViewListener") self.main_model = main_model self.loadDBDownloadTiles.connect(self.load_db_download_tiles) self.newDownloadTile.connect(self.new_download_tile) self.updateDownloadTileEvent.connect(self.update_download_tile_event) self.updateDownloadTile.connect(self.update_download_tile)
def __init__(self, parent, skip_failed=True): """ Sane History :param parent: MainWindow :param skip_failed: Skip failed entries (remove from history regardless action succeeds or not) """ QObject.__init__(self, parent=parent) self.logger = create_logger(__name__) self.root = parent self.parent = parent self.items = [] self.skip_failed = skip_failed
def __init__(self, main_window: QMainWindow, popup_dialog=None): super(SaneThemeHandler, self).__init__() self.logger = create_logger(__name__) self.main_window = main_window self.popup_dialog = popup_dialog self.themes = None # Theme lookup where key is the absolute path to variant filename and value is the corresponding SaneTheme. self.themes_by_variant_absolute_path = {} self.styles = None self.current_theme = None self.current_theme_idx = 0 self.current_style = None # Generate available themes and styles self.generate_themes() self.generate_styles() # Set the last used theme. variant_absolute_path = read_config('Theme', 'last_theme', literal_eval=False) if variant_absolute_path: self.logger.info( "Using 'last used' theme: {}".format(variant_absolute_path)) # Retrieve the respective theme dict. theme = self.get_theme_by_variant_absolute_path( variant_absolute_path) if theme is not None: # Set the theme self.set_theme(variant_absolute_path) else: self.logger.error( "Unable to restore last theme (INVALID: NoneType): {}". format(variant_absolute_path)) # Set the last used style. last_style = read_config('Theme', 'last_style', literal_eval=False) if last_style: self.logger.info("Using 'last used' style: {}".format(last_style)) self.set_style(last_style) # Apply custom user theme mod overrides self.apply_user_overrides()
def __init__(self, parent, action, title=TITLE, text=TEXT, ok_text=OK, cancel_text=CANCEL, validator=None, flags=Qt.WindowFlags, placeholder=PLACEHOLDER, use_placeholder=True, init_ui=True): """ Text input dialog box that does an action on the text input. :param parent: Parent object to bind to. :param action: Action to perform on OK/Enter. :param title: Dialog title. :param text: Dialog message body. :param ok_text: Text on OK button. :param cancel_text: Text on Cancel button. :param validator: If set, use a validator on the input field. :param flags: Qt WindowFlags. :param placeholder: Placeholder text in text input field. :param use_placeholder: Whether or not to use a placeholder text. :param init_ui: Whether or not to init ui (Usually only False if inherited by a class with its own init_ui(). """ super(SaneInputDialog, self).__init__(parent, flags()) self.logger = create_logger(__name__) self.sane_parent = parent self.text = text self.placeholder = placeholder self.use_placeholder = use_placeholder self.ok_text = ok_text self.cancel_text = cancel_text self.action = action self.title = title self.input_box = QLineEdit() self.validator = validator self.cancel_button = QPushButton(self) self.ok_button = QPushButton(self) # Store relevant original values self.original_background_color = self.palette().color( QPalette.Background) if init_ui: self.init_ui()
def __init__(self, parent, icon: QIcon = None, enforce_position=True): """ Config View Tabs :param parent: :param icon: :param enforce_position: If True (default) insert tabs at their predetermined position, else add them at the end of the row/list. """ super(ConfigViewTabs, self).__init__() self.sane_parent = parent self.logger = create_logger(__name__) if icon is not None: self.setWindowIcon(icon) self.setWindowTitle('Preferences') self.tabs = {} self.enforce_position = enforce_position self.add_tabs(CONFIG_TABS)
def __init__(self, youtube_response, playlist_id, channel_list_response=False): self.logger = create_logger(__name__) if channel_list_response: self.id = youtube_response[ 'id'] # channelList response id is outside of snippet section youtube_response = youtube_response[ 'snippet'] # Readjust to same level as subscriptionList response else: self.id = youtube_response['resourceId']['channelId'] try: self.title = youtube_response['title'] except KeyError as ke_exc_title: self.logger.error( "DB <--: Failed getting title key from YouTube response! Setting value to: None", exc_info=ke_exc_title) self.logger.debug6(youtube_response) # raise ke_exc_title self.title = None try: self.description = youtube_response['description'] except KeyError as ke_exc_desc: self.logger.error( "DB <--: failed getting description key from YouTube response!! Setting value to: None", exc_info=ke_exc_desc) self.logger.debug6(youtube_response) # raise ke_exc_desc self.description = None try: self.thumbnails = youtube_response['thumbnails'] except KeyError as ke_exc_thumbs: self.logger.error( "DB <--: failed getting thumbnails key from YouTube response!! Setting value to: None", exc_info=ke_exc_thumbs) self.logger.debug6(youtube_response) # raise ke_exc_thumbs self.thumbnails = None self.snippet = youtube_response self.playlist_id = playlist_id self.subscribed = True self.subscribed_override = False
def __init__(self, model): super().__init__() self.model = model self.refreshVideos.connect(self.refresh_videos) self.logger = create_logger(__name__ + '.DatabaseListener') self.reading_threads = [] self.writing_threads = [] self.db_state = self.DB_STATE_IDLE self.startWrite.connect(self.start_write) self.finishWrite.connect(self.finish_write) self.startRead.connect(self.start_read) self.finishRead.connect(self.finish_read) DatabaseListener.static_instance = self
def get_new_and_updated_videos(vid_paths): """ :param vid_paths: dict(video_id, vid_path) :return: """ logger = create_logger(__name__ + ".new_and_updated") db_videos = get_videos_by_ids(vid_paths.keys()) return_videos = [] for video in db_videos: if not video.vid_path or not video.downloaded: video_d = Video.to_video_d(video) video_d.vid_path = vid_paths[video.video_id] video_d.downloaded = True video_d.date_downloaded = datetime.datetime.utcnow() logger.info("Found video needing update: {} - {}".format( video.video_id, video.title)) return_videos.append(video_d) vid_paths.pop(video.video_id, None) new_videos = [] if len(vid_paths) > 0: youtube_keys = load_keys(1) logger.info( "Grabbing new video(s) information from youtube for: {}".format( vid_paths.keys())) response_videos = list_uploaded_videos_videos(youtube_keys[0], vid_paths.keys(), 30) for video in response_videos: video.vid_path = vid_paths[video.video_id] video.downloaded = True video.watched = False video.date_downloaded = datetime.datetime.utcnow() logger.info("Found new video: {} - {}".format( video.video_id, video.title)) new_videos.append(video) return_videos.extend(new_videos) logger.info("Downloading thumbnails for: {}".format(return_videos)) download_thumbnails_threaded(return_videos) return return_videos
def manual_youtube_folder_check(input_path): # input_path = os.path.join(OS_PATH, input_folder) logger = create_logger(__name__ + ".manual") logger.info("Searching directory: {}".format(input_path)) vid_paths = {} # start_time = timeit.default_timer() split_string = "_v-id-" for name in os.listdir(input_path): if split_string in name: logger.spam("Found: {}".format(name)) filename = name.split(".") for s in filename: if split_string in s: filename = str(s) id = str(filename.split(split_string)[-1]) vid_paths[id] = os.path.join(input_path, name) return_videos = get_new_and_updated_videos(vid_paths) return return_videos
def __init__(self, parent, cfg_section, cfg_option, tooltip=None, actions=None, actions_kwargs=None): """ A custom QPushButton that launches a QFontDialog. :param parent: Parent ptr. :param cfg_section: Config section. :param cfg_option: Config Option. :param tooltip: String to show on tooltip. :param actions: Function to call when font is selected. :param actions_kwargs: Keyword arguments (dict) to send in checked action calls. """ super(QPushButton, self).__init__(parent=parent) self.parent = parent self.logger = create_logger(__name__) self.tooltip = tooltip self.cfg_section = cfg_section self.cfg_option = cfg_option self.current_font = QFont() self.current_font.fromString(read_config(self.cfg_section, self.cfg_option, literal_eval=False)) self.update_info() self.clicked.connect(self.open_font_dialog)
def __init__(self, parent, video, action=None, anti_action=None, inactive=False): """ Sane History Item :param parent: SaneHistory :param video: :param action: :param anti_action: """ QObject.__init__(self, parent=parent) self.logger = create_logger(__name__) self.root = parent.root self.parent = parent self.video = video self.action = action self.anti_action = anti_action # If an item is inactive it is only there for informational purposes and will be skipped in undo. self.inactive = inactive
def __init__(self, downloader=None): PostProcessor.__init__(self, downloader) self.logger = create_logger(__name__) self._determine_executables()
def run(self, info, custom_map=None): self.logger = create_logger(__name__) metadata = {} def add(meta_list, info_list=None): if not info_list: info_list = meta_list if not isinstance(meta_list, (list, tuple)): meta_list = (meta_list, ) if not isinstance(info_list, (list, tuple)): info_list = (info_list, ) for info_f in info_list: if info.get(info_f) is not None: for meta_f in meta_list: metadata[meta_f] = info[info_f] break # if custom_map is None: # add('title', ('track', 'title')) # add('date', 'upload_date') # add(('description', 'comment'), 'description') # add('purl', 'webpage_url') # add('track', 'track_number') # add('artist', ('artist', 'creator', 'uploader', 'uploader_id')) # add('genre') # add('album') # add('album_artist') # add('disc', 'disc_number') # else: for entry in info: if entry not in info['not_a_tag']: add(entry) if not metadata: self.logger.warning('[ffmpeg] There isn\'t any metadata to add') return [], info filename = info['filepath'] temp_filename = prepend_extension(filename, 'temp') in_filenames = [filename] options = [] if info['ext'] == 'm4a': options.extend(['-vn', '-acodec', 'copy']) else: options.extend(['-c', 'copy']) for (name, value) in metadata.items(): options.extend(['-metadata', '%s=%s' % (name, value)]) chapters = info.get('chapters', []) if chapters: metadata_filename = replace_extension(filename, 'meta') with io.open(metadata_filename, 'wt', encoding='utf-8') as f: def ffmpeg_escape(text): return re.sub(r'(=|;|#|\\|\n)', r'\\\1', text) metadata_file_content = ';FFMETADATA1\n' for chapter in chapters: metadata_file_content += '[CHAPTER]\nTIMEBASE=1/1000\n' metadata_file_content += 'START=%d\n' % ( chapter['start_time'] * 1000) metadata_file_content += 'END=%d\n' % ( chapter['end_time'] * 1000) chapter_title = chapter.get('title') if chapter_title: metadata_file_content += 'title=%s\n' % ffmpeg_escape( chapter_title) f.write(metadata_file_content) in_filenames.append(metadata_filename) options.extend(['-map_metadata', '1']) self.logger.info('[ffmpeg] Adding metadata to \'%s\'' % filename) self.run_ffmpeg_multiple_files(in_filenames, temp_filename, options) if chapters: os.remove(metadata_filename) os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) return [], info
def __init__(self, search_item, grab_methods=None, kind=VIDEO_KIND_VOD): """ Creates a Video object from a YouTube playlist_item :param search_item: A raw YouTube API search()/list() JSON Snippet """ self.logger = create_logger(__name__) self.grab_methods = [] self.vid_path = "" self.date_downloaded = None self.duration = None self.has_caption = False self.downloaded = False self.dimension = "" self.definition = "" self.projection = "" self.region_restriction_allowed = [] self.region_restriction_blocked = [] self.kind = kind # Assume VOD by default. # Put liveBroadcastContent at start of feed to avoid it being buried. if self.kind == VIDEO_KIND_LIVE: self.watch_prio = 0 elif self.kind == VIDEO_KIND_LIVE_SCHEDULED: self.watch_prio = 1 elif self.kind == VIDEO_KIND_PREMIERE: self.watch_prio = 2 if grab_methods is None: grab_methods = [] # TODO: add to Video if grab_methods: self.grab_methods = grab_methods if not search_item: self.video_id = "" self.channel_title = "" self.title = "" self.date_published = datetime.datetime.utcnow() self.description = "" self.channel_id = "" self.url_video = "" self.thumbnails = "" self.search_item = "" self.watched = False return try: self.video_id = search_item['id']['videoId'] except Exception as anomaly: self.logger.exception("Exception while creating VideoD obj!", exc_info=anomaly) self.logger.info(search_item) raise self.channel_title = search_item['snippet']['channelTitle'] self.title = search_item['snippet']['title'] str_date = search_item['snippet']['publishedAt'] self.set_date_published(str_date) self.description = search_item['snippet']['description'] self.channel_id = search_item['snippet']['channelId'] self.url_video = YOUTUBE_URL_BASE + YOUTUBE_URL_PART_VIDEO + self.video_id if 'thumbnails' in search_item['snippet']: self.thumbnails = search_item['snippet']['thumbnails'] else: self.logger.critical("No thumbnails key in snippet!") self.logger.error(search_item) self.thumbnails = None self.search_item = search_item self.watched = False
def __init__(self, theme_dir_absolute_path, theme_handler=None, compile_qrc=False): """ Creates a custom theme from relevant files and returns it as a dict metadata.json specification: { "name": "<name>", "version": "<version>", "author": "<author>", "description": "<description>", "variants": [ { "name": "<name of variant 1>", "filename": "<filename of variant 1>.qss" "platform_whitelist": <null or list containing linux|win32|cygwin|darwin> }, { "name": "<name of variant 2>", "filename": "<filename of variant 2>.qss" "platform_whitelist": <null or list containing linux|win32|cygwin|darwin> }, .... ] } :param: theme_dir_absolute_path: Full path to the theme directory (in the themes directory). :param: theme_handler: Pointer to theme handler in order to send things up the chain. :param: compile_qrc: Whether to compile a QRC file into a module upon theme creation. :return: """ super(SaneTheme).__init__() self.logger = create_logger(__name__) self.theme_handler = theme_handler self.name = None self.version = None self.author = None self.description = None self.theme_dir_absolute_path = theme_dir_absolute_path # Pesky pesky QRC shenanigans. self.compile_qrc = compile_qrc self.qrc_filename = None self.compiled_qrc_filename = None self.compiled_qrc_modulename = None # A list of files in the theme directory self.files = [] # List of dict per variant. self.variants = [] # Contains any additional data that isn't pertinent to a theme. self.extraneous_data = None # Locate and store a list of all the files in the theme directory. self.locate_files() # Create self given absolute path to its theme directory. self.create()
def __init__(self, input_path, db_listeners=None): threading.Thread.__init__(self) self.logger = create_logger(__name__ + ".CheckYoutubeFolderForNew") self.input_path = input_path self.db_listeners = db_listeners
def __init__(self, listener): super().__init__() self.listener = listener self.logger = create_logger(__name__ + "VidEventHandler")
def __init__(self, parent, actions, title, text, ok_text, cancel_text=CANCEL, cancel_actions=None, flags=Qt.WindowFlags, caller=None): """ Builds a OAuth2 client secret JSON based on user input and known defaults, then calls action with the resulting json/dict as argument. :param parent: Parent object to bind to. :param actions: A function, or a list of functions to be called :param title: Dialog title. :param text: Dialog message body. :param ok_text: Text on OK button. :param cancel_text: Text on Cancel button. :param flags: Qt WindowFlags. """ super(SaneOAuth2BuilderDialog, self).__init__(parent, flags()) self.logger = create_logger(__name__) self.sane_parent = parent if not title: self.title = TITLE else: self.title = title if not text: self.text = TEXT else: self.text = text if not ok_text: self.ok_text = OK else: self.ok_text = ok_text if type(actions) is not list: self.action = actions else: self.action = None self.actions = actions self.caller = caller self.ok_text = ok_text self.cancel_text = cancel_text if cancel_actions: if type(cancel_actions) is not list: self.cancel_action = cancel_actions else: self.cancel_action = None self.cancel_actions = cancel_actions # Required since cancel action is not guaranteed to exist else: self.cancel_action = None self.cancel_actions = None self.input_box_client_id = QLineEdit() self.input_box_project_id = QLineEdit() self.input_box_auth_uri = QLineEdit() self.input_box_token_uri = QLineEdit() self.input_box_auth_provider_url = QLineEdit() self.input_box_client_secret = QLineEdit() self.input_box_redirect_uris = QLineEdit() self.input_boxes = [ self.input_box_client_id, self.input_box_project_id, self.input_box_auth_uri, self.input_box_token_uri, self.input_box_auth_provider_url, self.input_box_client_secret, self.input_box_redirect_uris ] self.cancel_button = QPushButton(self) self.ok_button = QPushButton(self) self.init_ui()
from __future__ import unicode_literals import datetime import os import threading from sane_yt_subfeed.handlers.config_handler import read_config, get_options, get_valid_options from sane_yt_subfeed import create_logger # FIXME: module level logger not suggested: https://fangpenlin.com/posts/2012/08/26/good-logging-practice-in-python/ logger = create_logger(__name__) try: YOUTUBE_DL_AVAILABLE = True from youtube_dl import YoutubeDL from youtube_dl.utils import DownloadError from sane_yt_subfeed.postprocessor.ffmpeg import \ SaneFFmpegPostProcessor, SaneFFmpegMetadataPP, SaneFFmpegMergerPP, SaneFFmpegPostProcessorError except ModuleNotFoundError as module_exc: YOUTUBE_DL_AVAILABLE = False VIDEO_FORMATS = ['mp4', 'flv', 'ogg', 'webm', 'mkv', 'avi', 'ts'] AUDIO_MERGE_FAILS = [ "Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument", "ERROR: Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument" ] IO_ERROR = ["ERROR: unable to write data: [Errno 5] Input/output error"] WRITE_DENIED_ERROR = [ "ERROR: unable to open for writing: [Errno 13] Permission denied" ] VIDEO_IS_GEOBLOCKED_ERRORS = [ "The uploader has not made this video available in your country.",