示例#1
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
        is_external = self._filesystem_monitor.is_external(new_path)
        Storage.update(
            path=new_path,
            external=is_external).where(Storage.id == self.db_id).execute()
        self._settings.invalidate()
        self.set_external(is_external)

        # 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, None)
        else:
            self.parent.emit_event("storage-changed", self.path)
            log.info(
                "Audio book location changed, rebasing the location in cozy.")
            self._block_list.rebase_path(old_path, new_path)
            thread = Thread(target=self._library.rebase_path,
                            args=(old_path, new_path),
                            name="RebaseStorageLocationThread")
            thread.start()
示例#2
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()
示例#3
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)
示例#4
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()
示例#5
0
def peewee_database():
    from cozy.db.track import Track
    from cozy.db.book import Book
    from cozy.db.settings import Settings
    from cozy.db.storage_blacklist import StorageBlackList
    from cozy.db.storage import Storage

    db_path, models, test_db = prepare_db()

    path_of_test_folder = os.path.dirname(os.path.realpath(__file__)) + '/'

    with open(path_of_test_folder + 'books.json') as json_file:
        book_data = json.load(json_file)

    with open(path_of_test_folder + 'tracks.json') as json_file:
        track_data = json.load(json_file)

    Book.insert_many(book_data).execute()
    for chunk in chunks(track_data, 25):
        Track.insert_many(chunk).execute()

    with open(path_of_test_folder + 'storages.json') as json_file:
        storage_data = json.load(json_file)

    Storage.insert_many(storage_data).execute()

    Settings.create(path="", last_played_book=Book.get())
    StorageBlackList.create(path="/path/to/replace/test1.mp3")
    StorageBlackList.create(path="/path/to/not/replace/test2.mp3")

    print("Provide database...")
    yield test_db

    teardown_db(db_path, models, test_db)
示例#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()
示例#7
0
def test_ensure_default_storage_present_does_nothing_if_default_is_present(peewee_database):
    from cozy.model.settings import Settings
    from cozy.db.storage import Storage

    settings = Settings()
    settings._load_all_storage_locations()
    settings._ensure_default_storage_present()
    assert not Storage.get(1).default
    assert Storage.get(2).default
示例#8
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)
示例#9
0
def test_ensure_default_storage_present_adds_default_if_not_present(peewee_database):
    from cozy.model.settings import Settings
    from cozy.db.storage import Storage

    Storage.update(default=False).where(Storage.id == 2).execute()

    settings = Settings()
    settings._load_all_storage_locations()
    settings._ensure_default_storage_present()
    assert Storage.get(1).default
    assert not Storage.get(2).default
示例#10
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()
示例#11
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()
示例#12
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

            self.no_media_file_chooser.set_current_folder(path)
            self.main_stack.props.visible_child_name = "no_media"
            self.block_ui_buttons(True)
            self.category_toolbar.set_visible(False)
示例#13
0
文件: db.py 项目: leuc/cozy
def get_external_storage_locations():
    """
    Returns a list of all external storage locations.
    """
    directories = Storage.select().where(Storage.external == True)

    return directories
示例#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))
示例#15
0
文件: db.py 项目: leuc/cozy
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))
示例#16
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))
示例#17
0
文件: application.py 项目: geigi/cozy
    def do_activate(self):
        main_window_builder = self.ui.get_builder()
        self.app_controller = AppController(self, main_window_builder, self.ui)

        self.ui.activate(self.app_controller.library_view)

        if Settings.get().first_start:
            Settings.update(first_start=False).execute()

            path = os.path.join(Path.home(), _("Audiobooks"))
            Storage.create(path=path, default=True)

            os.makedirs(path, exist_ok=True)

        self.add_window(self.ui.window)
        mpris = MPRIS(self)
        mpris._on_current_changed()
示例#18
0
def test_external_storage_locations_contain_only_external_storages(peewee_database):
    from cozy.model.settings import Settings
    from cozy.db.storage import Storage

    settings = Settings()
    storage_locations = Storage.select().where(Storage.external == True)

    assert len(settings.external_storage_locations) == len(storage_locations)
    assert all([storage.external for storage in settings.external_storage_locations])
示例#19
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
示例#20
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
示例#21
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
示例#22
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
示例#23
0
def peewee_database_storage():
    from cozy.db.storage import Storage
    from cozy.db.settings import Settings
    from cozy.db.storage_blacklist import StorageBlackList

    db_path, models, test_db = prepare_db()
    path_of_test_folder = os.path.dirname(os.path.realpath(__file__)) + '/'

    with open(path_of_test_folder + 'storages.json') as json_file:
        storage_data = json.load(json_file)

    Storage.insert_many(storage_data).execute()
    Settings.create(path="", last_played_book=None)
    StorageBlackList.create(path="/path/to/replace/test1.mp3")
    StorageBlackList.create(path="/path/to/not/replace/test2.mp3")

    print("Provide database...")
    yield test_db

    teardown_db(db_path, models, test_db)
示例#24
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)
示例#25
0
def test_storage_locations_contains_every_storage_location_from_db(peewee_database):
    from cozy.model.settings import Settings
    from cozy.db.storage import Storage

    settings = Settings()
    storage_locations = Storage.select()

    assert len(settings.storage_locations) == len(storage_locations)
    assert [storage.path for storage in settings.storage_locations] == [storage.path for storage in storage_locations]
    assert [storage.location_type for storage in settings.storage_locations] == [storage.location_type for storage in
                                                                                 storage_locations]
    assert [storage.default for storage in settings.storage_locations] == [storage.default for storage in
                                                                           storage_locations]
    assert [storage.external for storage in settings.storage_locations] == [storage.external for storage in
                                                                            storage_locations]
示例#26
0
    def _init_storage(self):
        """
        Display settings from the database in the ui.
        """
        found_default = False
        self.storage_list_box.remove_all_children()
        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)
示例#27
0
def test_all_existing_paths_are_included(mocker):
    from cozy.media.importer import Importer
    from cozy.db.storage import Storage

    mocker.patch("os.path.exists", return_value=True)

    importer = Importer()
    paths_to_import = importer._get_configured_storage_paths()

    internal_storages_in_db = Storage.select()
    internal_storage_paths = [
        storage.path for storage in internal_storages_in_db
    ]

    assert len(internal_storage_paths) == len(paths_to_import)
    assert all(
        [a == b for a, b in zip(internal_storage_paths, paths_to_import)])
示例#28
0
def test_external_paths_are_excluded_when_offline(mocker):
    from cozy.media.importer import Importer
    from cozy.db.storage import Storage

    mocker.patch("os.path.exists", return_value=True)
    mocker.patch(
        "cozy.control.filesystem_monitor.FilesystemMonitor.is_storage_online",
        autospec=True,
        return_value=False)

    importer = Importer()
    paths_to_import = importer._get_configured_storage_paths()

    internal_storages_in_db = Storage.select().where(
        Storage.external is not False)
    internal_storage_paths = [
        storage.path for storage in internal_storages_in_db
    ]
    assert len(internal_storage_paths) == len(paths_to_import)
    assert all(
        [a == b for a, b in zip(internal_storage_paths, paths_to_import)])
示例#29
0
 def _get_db_object(self):
     self._db_object: StorageModel = StorageModel.get(self.id)
示例#30
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',
                                          '.m4b', '.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, importer.emit_event,
                         "import-finished")
    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()