def is_external(book): """ Tests whether the given book is saved on external storage. """ return any(storage.path in Track.select().join(Book).where( Book.id == book.id).first().file for storage in Storage.select().where(Storage.external == True))
def load_last_book(): """ Load the last played book into the player. """ global __current_track global __player last_book = Settings.get().last_played_book if last_book and last_book.position != 0: query = Track.select().where(Track.id == last_book.position) if query.exists(): last_track = query.get() if last_track: __player.set_state(Gst.State.NULL) if cozy.control.filesystem_monitor.FilesystemMonitor( ).is_track_online(last_track): path = last_track.file else: path = OfflineCache().get_cached_path(last_track) if not path: return __player.set_property("uri", "file://" + path) __player.set_state(Gst.State.PAUSED) __current_track = last_track Book.update(last_played=int(time.time())).where( Book.id == last_book.id).execute() emit_event("track-changed", last_track)
def __on_button_press(self, eventbox, event): """ Play the selected track. """ current_track = player.get_current_track() if current_track and current_track.id == self.track.id: player.play_pause(None) if player.get_gst_player_state() == Gst.State.PLAYING: player.jump_to_ns(Track.select().where( Track.id == self.track.id).get().position) else: player.load_file( Track.select().where(Track.id == self.track.id).get()) player.play_pause(None, True) Book.update(position=self.track).where( Book.id == self.track.book.id).execute()
def search_tracks(search_string): """ Search all tracks in the db with the given substring. This ignores upper/lowercase. :param search_string: substring to search for :return: tracks matching the substring """ return Track.select(Track.name).where( Track.name.contains(search_string)).order_by(Track.name)
def get_tracks(book): """ Find all tracks that belong to a given book :param book: the book object :return: all tracks belonging to the book object """ return Track.select().join(Book).where(Book.id == book.id).order_by( Track.disk, Track.number, Track.name)
def rebase_location(ui, oldPath, newPath): """ This gets called when a user changes the location of the audio book folder. Every file in the database updated with the new path. Note: This does not check for the existence of those files. """ trackCount = Track.select().count() currentTrackCount = 0 for track in Track.select(): newFilePath = track.file.replace(oldPath, newPath) Track.update(file=newFilePath).where(Track.id == track.id).execute() StorageBlackList.update(path=newFilePath).where( StorageBlackList.path == track.file).execute() Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.titlebar.update_progress_bar.set_fraction, currentTrackCount / trackCount) currentTrackCount = currentTrackCount + 1 Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.switch_to_playing)
def clean_books(): """ Remove all books that have no tracks """ for book in Book.select(): if not get_track_for_playback(book): Book.update(position=0).where(Book.id == book.id).execute() if Track.select().where(Track.book == book).count() < 1: if Settings.get().last_played_book.id == book.id: Settings.update(last_played_book=None).execute() book.delete_instance()
def remove_tracks_with_path(ui, path): """ Remove all tracks that contain the given path. """ if path == "": return for track in Track.select(): if path in track.file: track.delete_instance() clean_books() Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.refresh_content)
def remove_invalid_entries(ui=None, refresh=False): """ Remove track entries from db that no longer exist in the filesystem. """ # remove entries from the db that are no longer existent for track in Track.select(): from cozy.control.filesystem_monitor import FilesystemMonitor if not os.path.isfile( track.file) and FilesystemMonitor().is_track_online(track): track.delete_instance() clean_books() if refresh: Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.refresh_content)
def get_current_track(): """ Get the currently loaded track object. :return: currently loaded track object """ global __current_track if __current_track: query = Track.select().where(Track.id == __current_track.id) if query.exists(): return query.get() else: __current_track = None return None else: return None
def locate(self, button): """ Locate the file and update the database if the user selected one. """ directory, filename = os.path.split(self.missing_file) dialog = Gtk.FileChooserDialog( "Please locate the file " + filename, self.parent.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) filter = Gtk.FileFilter() filter.add_pattern(filename) filter.set_name(filename) dialog.add_filter(filter) path, file_extension = os.path.splitext(self.missing_file) filter = Gtk.FileFilter() filter.add_pattern("*" + file_extension) filter.set_name(file_extension + " files") dialog.add_filter(filter) filter = Gtk.FileFilter() filter.add_pattern("*") filter.set_name(_("All files")) dialog.add_filter(filter) dialog.set_local_only(False) response = dialog.run() if response == Gtk.ResponseType.OK: new_location = dialog.get_filename() Track.update(file=new_location).where( Track.file == self.missing_file).execute() directory, filename = os.path.split(new_location) importer.import_file(filename, directory, new_location, update=True) self.parent.refresh_content() self.dialog.destroy() self.parent.dialog_open = False player.load_file( Track.select().where(Track.file == new_location).get()) player.play_pause(None, True) dialog.destroy()
def get_track_for_playback(book): """ Finds the current track to playback for a given book. :param book: book which the next track is required from :return: current track position from book db """ book = Book.select().where(Book.id == book.id).get() query = Track.select().where(Track.id == book.position) if book.position < 1: track_items = get_tracks(book) if len(track_items) > 0: track = get_tracks(book)[0] else: track = None elif query.exists(): track = query.get() else: track = None return track
def update_database(ui, force=False): """ Scans the audio book directory for changes and new files. Also removes entries from the db that are no longer existent. """ paths = [] for location in Storage.select(): if os.path.exists(location.path): paths.append(location.path) # clean artwork cache artwork_cache.delete_artwork_cache() # are UI buttons currently blocked? player_blocked, importer_blocked = ui.get_ui_buttons_blocked() i = 0 percent_counter = 0 file_count = 0 for path in paths: file_count += sum([len(files) for r, d, files in os.walk(path)]) percent_threshold = file_count / 1000 failed = "" tracks_to_import = [] # Tracks which changed and need to be updated if they are cached tracks_cache_update = [] start = time.time() for path in paths: for directory, subdirectories, files in os.walk(path): for file in files: if file.lower().endswith( ('.mp3', '.ogg', '.flac', '.m4a', '.wav', '.opus')): path = os.path.join(directory, file) imported = True try: if force: imported, ignore = import_file( file, directory, path, True) tracks_cache_update.append(path) # Is the track already in the database? elif Track.select().where( Track.file == path).count() < 1: imported, track_data = import_file( file, directory, path) if track_data: tracks_to_import.append(track_data) # Has the modified date changed? elif (Track.select().where( Track.file == path).first().modified < os.path.getmtime(path)): imported, ignore = import_file(file, directory, path, update=True) tracks_cache_update.append(path) if not imported: failed += path + "\n" except UnicodeEncodeError as e: log.warning( "Could not import file because of invalid path or filename: " + path) reporter.exception("importer", e) failed += path + "\n" except Exception as e: log.warning("Could not import file: " + path) log.warning(traceback.format_exc()) reporter.exception("importer", e) failed += path + "\n" i = i + 1 if len(tracks_to_import) > 100: write_tracks_to_db(tracks_to_import) tracks_to_import = [] # don't flood gui updates if percent_counter < percent_threshold: percent_counter = percent_counter + 1 else: percent_counter = 1 Gdk.threads_add_idle( GLib.PRIORITY_DEFAULT_IDLE, ui.titlebar.progress_bar.set_fraction, i / file_count) Gdk.threads_add_idle( GLib.PRIORITY_DEFAULT_IDLE, ui.titlebar.update_progress_bar.set_fraction, i / file_count) write_tracks_to_db(tracks_to_import) end = time.time() log.info("Total import time: " + str(end - start)) # remove entries from the db that are no longer existent remove_invalid_entries() artwork_cache.generate_artwork_cache() Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.refresh_content) Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.switch_to_playing) Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.check_for_tracks) if len(failed) > 0: Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, ui.display_failed_imports, failed) OfflineCache().update_cache(tracks_cache_update) OfflineCache()._process_queue()
def Search(search): return Track.select().where(search in Track.name)