Ejemplo n.º 1
0
def write_tracks_to_db(tracks):
    """
    """
    if tracks is None or len(tracks) < 1:
        return

    if PeeweeVersion[0] == '2':
        data = list({
            "name": t.name,
            "number": t.track_number,
            "disk": t.disk,
            "position": t.position,
            "book": t.book,
            "file": t.file,
            "length": t.length,
            "modified": t.modified
        } for t in tracks)
        Track.insert_many(data).execute()
    else:
        fields = [
            Track.name, Track.number, Track.disk, Track.position, Track.book,
            Track.file, Track.length, Track.modified
        ]
        data = list((t.name, t.track_number, t.disk, t.position, t.book,
                     t.file, t.length, t.modified) for t in tracks)
        Track.insert_many(data, fields=fields).execute()
Ejemplo n.º 2
0
    def on_close(self, widget, data=None):
        """
        Close and dispose everything that needs to be when window is closed.
        """
        log.info("Closing.")
        self.titlebar.close()
        self.fs_monitor.close()
        if self.sleep_timer.is_running():
            self.sleep_timer.stop()

        # save current position when still playing
        if player.get_gst_player_state() == Gst.State.PLAYING:
            Track.update(position=player.get_current_duration()).where(
                Track.id == player.get_current_track().id).execute()
            player.stop()

        player.dispose()

        close_db()

        report.close()

        log.info("Closing app.")
        self.app.quit()
        log.info("App closed.")
Ejemplo n.º 3
0
def play_pause(track, jump=False):
    """
    Play a new file or pause/play if the file is already loaded.
    :param track: Track object that will be played/paused.
    """
    global __current_track
    global __player
    global __wait_to_seek
    global __set_speed

    __wait_to_seek = jump

    if __current_track == track or track is None:
        # Track is already selected, only play/pause
        if get_gst_player_state() == Gst.State.PLAYING:
            __player.set_state(Gst.State.PAUSED)
            emit_event("pause")
            save_current_track_position()
        else:
            __player.set_state(Gst.State.PLAYING)
            emit_event("play", Track.get(Track.id == __current_track.id))
    else:
        load_file(track)
        __player.set_state(Gst.State.PLAYING)
        emit_event("play", Track.get(Track.id == __current_track.id))

    __set_speed = True
Ejemplo n.º 4
0
def save_current_track_position(pos=None, track=None):
    """
    Saves the current track position to the cozy.db.
    """
    if pos is None:
        pos = get_current_duration()

    if track is None:
        track = get_current_track()

    Track.update(position=pos).where(Track.id == track.id).execute()
Ejemplo n.º 5
0
def blacklist_book(book):
    """
    Removes a book from the library and adds the path(s) to the track list.
    """
    book_tracks = get_tracks(book)
    data = list((t.file, ) for t in book_tracks)
    chunks = [data[x:x + 500] for x in range(0, len(data), 500)]
    for chunk in chunks:
        StorageBlackList.insert_many(chunk,
                                     fields=[StorageBlackList.path]).execute()
    ids = list(t.id for t in book_tracks)
    Track.delete().where(Track.id << ids).execute()
    book.delete_instance()
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
 def _fetch_chapters(self):
     with self._db:
         tracks = TrackModel \
             .select(TrackModel.id) \
             .where(TrackModel.book == self._db_object) \
             .order_by(TrackModel.disk, TrackModel.number, TrackModel.name)
         self._chapters = [Track(self._db, track.id) for track in tracks]
Ejemplo n.º 8
0
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))
Ejemplo n.º 9
0
    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()
Ejemplo n.º 10
0
    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()
Ejemplo n.º 11
0
    def _fetch_chapters(self):
        tracks = TrackModel \
            .select(TrackModel.id) \
            .where(TrackModel.book == self._db_object) \
            .order_by(TrackModel.disk, TrackModel.number, TrackModel.name)
        self._chapters = [Track(self._db, track.id) for track in tracks]

        for chapter in self._chapters:
            chapter.add_listener(self._on_chapter_event)
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
def clean_db():
    """
    Delete everything from the database except settings.
    """
    q = Track.delete()
    q.execute()
    q = Book.delete()
    q.execute()
    q = ArtworkCache.delete()
    q.execute()
Ejemplo n.º 15
0
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)
Ejemplo n.º 16
0
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()
Ejemplo n.º 17
0
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)
Ejemplo n.º 18
0
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
Ejemplo n.º 19
0
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)
Ejemplo n.º 20
0
    def _fetch_chapters(self):
        tracks = TrackModel \
            .select(TrackModel.id) \
            .where(TrackModel.book == self._db_object) \
            .order_by(TrackModel.disk, TrackModel.number, TrackModel.name)

        self._chapters = []
        for track in tracks:
            try:
                track_model = Track(self._db, track.id)
                self._chapters.append(track_model)
            except TrackInconsistentData:
                log.warning("Skipping inconsistent model")
            except Exception as e:
                log.error("Could not create chapter object: {}".format(e))

        for chapter in self._chapters:
            chapter.add_listener(self._on_chapter_event)
Ejemplo n.º 21
0
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
Ejemplo n.º 22
0
def Search(search):
    return Track.select().where(search in Track.name)
Ejemplo n.º 23
0
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()
Ejemplo n.º 24
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 = 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)

    ### 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