Ejemplo n.º 1
0
def copy_to_audiobook_folder(path):
    """
    Copies the given path (folder or file) to the audio book folder.
    """
    try:
        name = os.path.basename(os.path.normpath(path))
        shutil.copytree(
            path,
            Storage.select().where(Storage.default == True).get().path + "/" +
            name)
    except OSError as exc:
        reporter.exception("importer", exc)
        if exc.errno == errno.ENOTDIR:
            try:
                shutil.copy(
                    path,
                    Storage.select().where(Storage.default == True).get().path)
            except OSError as e:
                if e.errno == 95:
                    log.error("Could not import file " + path)
                    log.error(exc)
                else:
                    log.error(e)
        elif exc.errno == errno.ENOTSUP:
            log.error("Could not import file " + path)
            log.error(exc)
        else:
            log.error("Could not import file " + path)
            log.error(exc)
Ejemplo n.º 2
0
    def __on_folder_changed(self, widget):
        """
        Update the location in the database.
        Start an import scan or a rebase operation.
        """
        new_path = self.location_chooser.get_file().get_path()
        # First test if the new location is already in the database
        if Storage.select().where(Storage.path == new_path).count() > 0:
            return

        # If not, add it to the database
        old_path = Storage.select().where(Storage.id == self.db_id).get().path
        self.path = new_path
        Storage.update(path=new_path).where(Storage.id == self.db_id).execute()

        # Run a reimport or rebase
        if old_path == "":
            self.parent.emit_event("storage-added", self.path)
            log.info("New audiobook location added. Starting import scan.")
            self.ui.scan(None, False)
        else:
            self.parent.emit_event("storage-changed", self.path)
            log.info(
                "Audio book location changed, rebasing the location in cozy.")
            self.ui.switch_to_working(_("Changing audio book location…"),
                                      False)
            thread = Thread(target=importer.rebase_location,
                            args=(self.ui, old_path, new_path),
                            name="RebaseStorageLocationThread")
            thread.start()
Ejemplo n.º 3
0
    def check_for_tracks(self):
        """
        Check if there are any imported files.
        If there aren't display a welcome screen.
        """
        if books().count() < 1:
            path = ""
            if Storage.select().count() > 0:
                path = Storage.select().where(
                    Storage.default == True).get().path

            if not path:
                path = os.path.join(os.path.expanduser("~"), _("Audiobooks"))

                if not os.path.exists(path):
                    os.mkdir(path)

                Storage.create(path=path, default=True)

            self.no_media_file_chooser.set_current_folder(path)
            self.main_stack.props.visible_child_name = "no_media"
            self.block_ui_buttons(True)
            self.titlebar.stop()
            self.category_toolbar.set_visible(False)
        else:
            self.main_stack.props.visible_child_name = "main"
            # This displays the placeholder if there is not a recent book yet
            self.__on_sort_stack_changed(None, None)
Ejemplo n.º 4
0
def __update_db_3(db):
    current_path = Settings.get().path

    db.create_tables([Storage])
    Storage.create(path=current_path, default=True)
    Settings.update(path="NOT_USED").execute()
    Settings.update(version=3).execute()
Ejemplo n.º 5
0
def test_setting_invalid_path_raises_exception(peewee_database_storage):
    from cozy.model.storage import Storage, InvalidPath

    invalid_path = "not an absolute path"
    storage = Storage(peewee_database_storage, 1)

    with pytest.raises(InvalidPath):
        storage.path = invalid_path
Ejemplo n.º 6
0
 def set_default(self, default):
     """
     Set this storage location as the default
     :param default: Boolean
     """
     self.default = default
     self.default_image.set_visible(default)
     Storage.update(default=default).where(
         Storage.id == self.db_id).execute()
Ejemplo n.º 7
0
def test_setting_path_updates_in_track_object_and_database(
        peewee_database_storage):
    from cozy.db.storage import Storage as StorageModel
    from cozy.model.storage import Storage

    new_path = "/tmp/media2"

    storage = Storage(peewee_database_storage, 1)
    storage.path = new_path
    assert storage.path == new_path
    assert StorageModel.get_by_id(1).path == new_path
Ejemplo n.º 8
0
def test_setting_default_updates_in_track_object_and_database(
        peewee_database_storage):
    from cozy.db.storage import Storage as StorageModel
    from cozy.model.storage import Storage

    new_default = True

    storage = Storage(peewee_database_storage, 1)
    storage.default = new_default
    assert storage.default == new_default
    assert StorageModel.get_by_id(1).default == new_default
Ejemplo n.º 9
0
def test_setting_external_updates_in_track_object_and_database(
        peewee_database_storage):
    from cozy.db.storage import Storage as StorageModel
    from cozy.model.storage import Storage

    new_external = True

    storage = Storage(peewee_database_storage, 1)
    storage.external = new_external
    assert storage.external == new_external
    assert StorageModel.get_by_id(1).external == new_external
Ejemplo n.º 10
0
def test_setting_location_type_updates_in_track_object_and_database(
        peewee_database_storage):
    from cozy.db.storage import Storage as StorageModel
    from cozy.model.storage import Storage

    new_location_type = 555

    storage = Storage(peewee_database_storage, 1)
    storage.location_type = new_location_type
    assert storage.location_type == new_location_type
    assert StorageModel.get_by_id(1).location_type == new_location_type
Ejemplo n.º 11
0
 def __on_no_media_folder_changed(self, sender):
     """
     Get's called when the user changes the audiobook location from
     the no media screen. Now we want to do a first scan instead of a rebase.
     """
     location = self.no_media_file_chooser.get_file().get_path()
     external = self.external_switch.get_active()
     Storage.delete().where(Storage.path != "").execute()
     Storage.create(path=location, default=True, external=external)
     self.main_stack.props.visible_child_name = "import"
     self.scan(None, True)
     self.settings._init_storage()
     self.fs_monitor.init_offline_mode()
Ejemplo n.º 12
0
 def __on_remove_storage_clicked(self, widget):
     """
     Remove a storage selector from the ui and database.
     """
     row = self.storage_list_box.get_selected_row()
     Storage.select().where(
         Storage.path == row.path).get().delete_instance()
     self.storage_list_box.remove(row)
     self.emit_event("storage-removed", row.path)
     thread = Thread(target=remove_tracks_with_path,
                     args=(self.ui, row.path),
                     name=("RemoveStorageFromDB"))
     thread.start()
     self.__on_storage_box_changed(None, None)
Ejemplo n.º 13
0
def get_external_storage_locations():
    """
    Returns a list of all external storage locations.
    """
    directories = Storage.select().where(Storage.external == True)

    return directories
Ejemplo n.º 14
0
 def __on_add_storage_clicked(self, widget):
     """
     Add a new storage selector to the ui.
     """
     db_obj = Storage.create(path="")
     self.storage_list_box.add(
         StorageListBoxRow(self, db_obj.id, "", False, False))
Ejemplo n.º 15
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.º 16
0
    def set_external(self, external):
        """
        Set this entry as external/internal storage.
        This method also writes the setting to the cozy.
        """
        self.external = external
        if external:
            self.type_image.set_from_icon_name("network-server-symbolic",
                                               Gtk.IconSize.LARGE_TOOLBAR)
            self.type_image.set_tooltip_text(_("External drive"))
        else:
            self.type_image.set_from_icon_name("drive-harddisk-symbolic",
                                               Gtk.IconSize.LARGE_TOOLBAR)
            self.type_image.set_tooltip_text(_("Internal drive"))

        Storage.update(external=external).where(
            Storage.id == self.db_id).execute()
Ejemplo n.º 17
0
 def _load_all_storage_locations(self):
     for storage_db_obj in StorageModel.select(StorageModel.id):
         try:
             self._storages.append(Storage(self._db, storage_db_obj.id))
         except InvalidPath:
             log.error(
                 "Invalid path found in database, skipping: {}".format(
                     storage_db_obj.path))
Ejemplo n.º 18
0
    def _init_storage(self):
        """
        Display settings from the database in the ui.
        """
        found_default = False
        tools.remove_all_children(self.storage_list_box)
        for location in Storage.select():
            row = StorageListBoxRow(self, location.id, location.path,
                                    location.external, location.default)
            self.storage_list_box.add(row)
            if location.default:
                if found_default:
                    row.set_default(False)
                else:
                    found_default = True
                    self.storage_list_box.select_row(row)

        self.__on_storage_box_changed(None, None)
Ejemplo n.º 19
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.º 20
0
def test_external_returns_correct_default_value(peewee_database_storage):
    from cozy.model.storage import Storage

    storage = Storage(peewee_database_storage, 1)
    assert storage.external == False
Ejemplo n.º 21
0
def test_path_returns_correct_value(peewee_database_storage):
    from cozy.model.storage import Storage

    storage = Storage(peewee_database_storage, 1)
    assert storage.path == "/media/test"
Ejemplo n.º 22
0
def test_location_type_returns_correct_default_value(peewee_database_storage):
    from cozy.model.storage import Storage

    storage = Storage(peewee_database_storage, 1)
    assert storage.location_type == 0