def on_method_call(self, connection, sender, object_path, interface_name, method_name, parameters, invocation): args = list(parameters.unpack()) for i, sig in enumerate(self.method_inargs[method_name]): if sig == "h": msg = invocation.get_message() fd_list = msg.get_unix_fd_list() args[i] = fd_list.get(args[i]) try: result = getattr(self, method_name)(*args) # out_args is atleast (signature1). # We therefore always wrap the result as a tuple. # Refer to https://bugzilla.gnome.org/show_bug.cgi?id=765603 result = (result,) out_args = self.method_outargs[method_name] if out_args and out_args != "()" and result: variant = GLib.Variant(out_args, result) invocation.return_value(variant) else: invocation.return_value(None) except Exception as e: log.error(e) reporter.exception("mpris", e) reporter.error("mrpis", "MPRIS method call failed with method name: {}".format(method_name)) invocation.return_value(None)
def __load_pixbuf_from_cache(book, size): """ """ pixbuf = None query = ArtworkCache.select().where(ArtworkCache.book == book.id) if query.exists(): try: uuid = query.first().uuid except Exception as e: reporter.error( "artwork_cache", "load_pixbuf_from_cache: query exists but query.first().uuid crashed." ) return None else: return None cache_dir = os.path.join(get_cache_dir(), "artwork") cache_dir = os.path.join(cache_dir, uuid) try: if os.path.exists(cache_dir): file_path = os.path.join(cache_dir, str(size) + ".jpg") if os.path.exists(file_path): pixbuf = GdkPixbuf.Pixbuf.new_from_file( os.path.join(cache_dir, str(size) + ".jpg")) else: return None except Exception as e: log.warning(e) return None return pixbuf
def get_album_art_path(self, book, size): query = ArtworkCacheModel.select().where( ArtworkCacheModel.book == book.id) if query.exists(): try: uuid = query.first().uuid except Exception as e: reporter.error( "artwork_cache", "load_pixbuf_from_cache: query exists but query.first().uuid crashed." ) return None else: return None cache_dir = os.path.join(get_cache_dir(), "artwork") cache_dir = os.path.join(cache_dir, uuid) try: if os.path.exists(cache_dir): file_path = os.path.join(cache_dir, str(size) + ".jpg") if os.path.exists(file_path): return file_path else: return None except Exception as e: log.warning(e) return None return None
def play_pause_book(self, book: Book): if not book: log.error("Cannot play book which is None.") reporter.error("player", "Cannot play book which is None.") return self._play_chapter(book, book.current_chapter)
def _forward_in_book(self): if not self._book: log.error( "Forward in book not possible because no book is loaded.") reporter.error( "player", "Forward in book not possible because no book is loaded.") return current_position = self._gst_player.position current_position_relative = max( current_position - self.loaded_chapter.start_position, 0) old_chapter = self._book.current_chapter chapter_number = self._book.chapters.index(self._book.current_chapter) forward_seconds = self._app_settings.forward_duration * self.playback_speed if current_position_relative / NS_TO_SEC + forward_seconds < self._book.current_chapter.length: self._gst_player.position = current_position + (NS_TO_SEC * forward_seconds) elif chapter_number < len(self._book.chapters) - 1: next_chapter = self._book.chapters[chapter_number + 1] self._load_chapter(next_chapter) self._gst_player.position = next_chapter.start_position + ( NS_TO_SEC * forward_seconds - (old_chapter.length * NS_TO_SEC - current_position_relative)) else: self._next_chapter()
def _load_chapter(self, chapter: Chapter): file_changed = False if not self._book: log.error("There is no book loaded but there should be.") reporter.error("player", "There is no book loaded but there should be.") return self._library.last_played_book = self._book media_file_path = self._get_playback_path(chapter) if self._gst_player.loaded_file_path == media_file_path: log.info( "Not loading a new file because the new chapter is within the old file." ) else: log.info("Loading new file for chapter.") try: self._gst_player.load_file(media_file_path) file_changed = True except FileNotFoundError: self._handle_file_not_found() return if file_changed or self._should_jump_to_chapter_position( chapter.position): self._gst_player.position = chapter.position self._gst_player.playback_speed = self._book.playback_speed if file_changed or self._book.position != chapter.id: self._book.position = chapter.id self.emit_event_main_thread("chapter-changed", self._book)
def is_external(self, directory: str) -> bool: mounts: List[Gio.Mount] = self.volume_monitor.get_mounts() for mount in mounts: root = mount.get_root() if not root: log.error( "Failed to test for external drive. Mountpoint has no root object." ) reporter.error( "fs_monitor", "Failed to test for external drive. Mountpoint has no root object." ) return False path = root.get_path() if not path: log.error( "Failed to test for external drive. Root object has no path." ) reporter.error( "fs_monitor", "Failed to test for external drive. Root object has no path." ) return False if path in directory and mount.can_unmount(): return True return False
def play_pause_chapter(self, book: Book, chapter: Chapter): if not book or not chapter: log.error("Cannot play chapter which is None.") reporter.error("player", "Cannot play chapter which is None.") return self._play_chapter(book, chapter) book.position = chapter.id
def play_pause(self): if self._gst_player.state == GstPlayerState.PAUSED: self._gst_player.play() elif self._gst_player.state == GstPlayerState.PLAYING: self._gst_player.pause() else: log.error("Trying to play/pause although player is in STOP state.") reporter.error( "player", "Trying to play/pause although player is in STOP state.")
def play_pause_book(self, book: Book): if not book: log.error("Cannot play book which is None.") reporter.error("player", "Cannot play book which is None.") return if self._book == book: self.play_pause() else: self._continue_book(book) self._gst_player.play()
def save_current_book_position(track, pos=None): """ Saves the given track to it's book as the current position to the cozy.db. :param track: track object """ if not track: log.error("Could not save book position because the given track is NULL") reporter.error("control/player", "Could not save book position because the given track is NULL") return if pos is None: pos = track.id Book.update(position=pos).where( Book.id == track.book.id).execute()
def _on_play_changed(self): playing = self._view_model.playing play_button_img = "media-playback-pause-symbolic" if playing else "media-playback-start-symbolic" self.play_img.set_from_icon_name(play_button_img, Gtk.IconSize.LARGE_TOOLBAR) if self._current_selected_chapter: self._current_selected_chapter.set_playing(playing) else: log.error("_current_selected_chapter is null. Skipping...") reporter.error( "book_detail_view", "_current_selected_chapter was NULL. No ply/pause chapter icon was changed" )
def play_pause_chapter(self, book: Book, chapter: Chapter): if not book or not chapter: log.error("Cannot play chapter which is None.") reporter.error("player", "Cannot play chapter which is None.") return if self._book and self._book.current_chapter == chapter: self.play_pause() return if self._book != book: self._load_book(book) self._load_chapter(chapter) self._gst_player.play() book.position = chapter.id
def remove_bind(self, prop: str, callback: Callable): if not prop: log.error("Cannot remove bind for empty prop.") reporter.error("observable", "Cannot remove bind for empty prop.") return if not callback: log.error("Cannot remove bind for empty callback.") reporter.error("observable", "Cannot remove bind for empty callback.") return if prop in self._observers: if callback in self._observers[prop]: self._observers[prop].remove(callback) else: log.info("Callback not found in prop's {} observers. Skipping remove bind...".format(prop)) else: log.info("Prop not found in observers. Skipping remove bind...")
def __on_gst_message(bus, message: Gst.Message): """ Handle messages from gst. """ global __speed global __set_speed t = message.type if t == Gst.MessageType.BUFFERING: if (message.percentage < 100): __player.set_state(Gst.State.PAUSED) log.info("Buffering…") else: __player.set_state(Gst.State.PLAYING) log.info("Buffering finished.") elif t == Gst.MessageType.EOS: next_track() elif t == Gst.MessageType.ERROR: error, debug_msg = message.parse_error() if error.code == Gst.ResourceError.NOT_FOUND: track = get_current_track() stop() unload() emit_event("stop") log.warning("gst: Resource not found. Stopping player.") reporter.warning("player", "gst: Resource not found. Stopping player.") emit_event("resource-not-found", track) return reporter.error("player", error) log.error(error) log.debug(debug_msg) emit_event("error", error) elif t == Gst.MessageType.STATE_CHANGED: state = get_gst_player_state() if state == Gst.State.PLAYING or state == Gst.State.PAUSED: auto_jump()
def _next_chapter(self): if not self._book: log.error( "Cannot play next chapter because no book reference is stored." ) reporter.error( "player", "Cannot play next chapter because no book reference is stored." ) index_current_chapter = self._book.chapters.index( self._book.current_chapter) self._book.current_chapter.position = self._book.current_chapter.start_position if len(self._book.chapters) <= index_current_chapter + 1: log.info("Book finished, stopping playback.") self._finish_book() self._gst_player.stop() else: chapter = self._book.chapters[index_current_chapter + 1] chapter.position = chapter.start_position self.play_pause_chapter(self._book, chapter)
def _rewind_in_book(self): if not self._book: log.error("Rewind in book not possible because no book is loaded.") reporter.error( "player", "Rewind in book not possible because no book is loaded.") return current_position = self._gst_player.position current_position_relative = max( current_position - self.loaded_chapter.start_position, 0) chapter_number = self._book.chapters.index(self._book.current_chapter) rewind_seconds = self._app_settings.rewind_duration * self.playback_speed if current_position_relative / NS_TO_SEC - rewind_seconds > 0: self._gst_player.position = current_position - NS_TO_SEC * rewind_seconds elif chapter_number > 0: previous_chapter = self._book.chapters[chapter_number - 1] self._load_chapter(previous_chapter) self._gst_player.position = previous_chapter.end_position + ( current_position_relative - NS_TO_SEC * rewind_seconds) else: self._gst_player.position = 0
def import_file(file, directory, path, update=False): """ Imports all information about a track into the database. Note: This creates also a new album object when it doesnt exist yet. Note: This does not check whether the file is already imported. :return: True if file was imported, otherwise False :return: Track object to be imported when everything passed successfully and track is not in the db already. """ if is_blacklisted(path): return True, None media_type, media_type_human = tools.__get_media_type(path) track = TrackContainer(None, path) cover = None reader = None track_number = None track_data = None # getting the some data is file specific ### MP3 ### if "audio/mpeg" in media_type: track_data = _get_mp3_tags(track, path) ### FLAC ### elif "audio/flac" in media_type or "audio/x-flac" in media_type: track_data = _get_flac_tags(track, path) ### OGG ### elif "audio/ogg" in media_type or "audio/x-ogg" in media_type: track_data = _get_ogg_tags(track, path) ### OPUS ### elif "audio/opus" in media_type or "audio/x-opus" in media_type or "codecs=opus" in media_type: track_data = _get_opus_tags(track, path) ### MP4 ### elif "audio/mp4" in media_type or "audio/x-m4a" in media_type: track_data = _get_mp4_tags(track, path) ### WAV ### elif "audio/wav" in media_type or "audio/x-wav" in media_type: track_data = TrackData(path) track_data.length = __get_wav_track_length(path) elif "ID3" in media_type_human: track_data = _get_mp3_tags(track, path) ### File will not be imported ### else: # don't use _ for ignored return value -> it is reserved for gettext ignore, file_extension = os.path.splitext(path) log.warning("Skipping file " + path + " because of mime type " + media_type + ".") reporter.error( "importer", "Mime type not detected as audio: " + media_type + " with file ending: " + file_extension) return False, None track_data.modified = __get_last_modified(path) # try to get all the remaining tags try: if track_data.track_number is None: # The track number can contain the total number of tracks track_text = str(__get_common_tag(track, "tracknumber")) track_data.track_number = int(track_text.split("/")[0]) except Exception as e: log.debug(e) track_data.track_number = 0 if track_data.book_name is None: track_data.book_name = __guess_book_name(directory) if track_data.author is None or track_data.author == "": if track_data.reader and len(track_data.reader) > 0: track_data.author = track_data.reader track_data.reader = "" else: track_data.author = _("Unknown Author") if track_data.reader is None or track_data.reader == "": track_data.reader = _("Unknown Reader") if track_data.name is None: track_data.name = __guess_title(file) if not track_data.disk: track_data.disk = 1 if not track_data.length: # Try to get the length by using gstreamer success, track_data.length = get_gstreamer_length(path) if not success: return False, None if update: if Book.select().where(Book.name == track_data.book_name).count() < 1: track_data.book = Book.create(name=track_data.book_name, author=track_data.author, reader=track_data.reader, position=0, rating=-1, cover=track_data.cover) else: track_data.book = Book.select().where( Book.name == track_data.book_name).get() Book.update(name=track_data.book_name, author=track_data.author, reader=track_data.reader, cover=track_data.cover).where( Book.id == track_data.book.id).execute() Track.update(name=track_data.name, number=track_data.track_number, book=track_data.book, disk=track_data.disk, length=track_data.length, modified=track_data.modified).where( Track.file == track_data.file).execute() else: # create database entries if Book.select().where(Book.name == track_data.book_name).count() < 1: track_data.book = Book.create(name=track_data.book_name, author=track_data.author, reader=track_data.reader, position=0, rating=-1, cover=track_data.cover) else: track_data.book = Book.select().where( Book.name == track_data.book_name).get() return True, track_data return True, None