def sync_both_differ( db: Connection, counter: Counter[DataMismatchCounterEnum], *, include_filesystem_uris: bool = False, ): stdout.print( "Synchronising differences between existing entries in both databases.", style="notice" ) results = db.execute(load_both_differ_query) for result in results: track_uri = result["track_uri"] parsed_track_uri = urlparse(track_uri) if not parsed_track_uri.scheme: stderr.print( f"Invalid track URI found in internal corrections database: {track_uri}", style="warning", ) continue elif parsed_track_uri.scheme in ("file", "local"): if not include_filesystem_uris: continue table = make_differ_table(result) stdout.print(table) response = questionary.select( message="", choices=(choice_choose_internal, choice_choose_external, choice_manual_edit), ).ask() if response is None: raise AbortCommand if response == DataMismatchChoiceEnum.CHOOSE_INTERNAL: update_ext_from_main(db, result) elif response == DataMismatchChoiceEnum.CHOOSE_EXTERNAL: update_main_from_ext(db, result) elif response == DataMismatchChoiceEnum.MANUAL_EDIT: while True: title, artist, album = edit_values(result) if questionary.confirm("Empty album correct?", auto_enter=False).ask(): break update_both(db, track_uri, title, artist, album) elif response == DataMismatchChoiceEnum.SKIP_TRACK: counter.incr(DataMismatchCounterEnum.SKIP) continue else: stderr.print("Unrecognised option.", style="error") raise AbortCommand counter.incr(DataMismatchCounterEnum.UPDATE)
def connect_internal_db(config) -> Connection: db_path = get_db_path(config) if not db_path.is_file(): stderr.print("Internal database does not exist.", style="error") raise AbortCommand stdout.print("Connecting to internal database.", style="notice") db: Connection = sqlite3.connect( db_path, timeout=config["advanced_scrobbler"]["db_timeout"], factory=Connection, ) internal_user_version = db.execute( "PRAGMA user_version").fetchone()["user_version"] if internal_user_version != SCHEMA_VERSION: stderr.print( f"Internal database schema is out of date. Latest version is v{SCHEMA_VERSION}.", style="error", ) raise AbortCommand stdout.print("Connected to internal database.", style="info") return db
def _connect_external_db(db_path: Path, config) -> Connection: if not db_path.is_file(): stderr.print("Specified external database file does not exist.", style="error") raise AbortCommand stdout.print("Connecting to external database.", style="notice") db: Connection = sqlite3.connect( db_path, timeout=config["advanced_scrobbler"]["db_timeout"], factory=Connection, ) external_user_version = db.execute( "PRAGMA user_version").fetchone()["user_version"] if external_user_version != SCHEMA_VERSION: stderr.print( f"External database schema is out of date. Latest version is v{SCHEMA_VERSION}.", style="error", ) db.close() raise AbortCommand return db
def run(args: Namespace, config): verify_external_db(args.external_db, config) db = connect_internal_db(config) attach_external_db(db, args.external_db) differ_counter: Counter[DataMismatchCounterEnum] = Counter() try: sync_both_differ(db, differ_counter, include_filesystem_uris=args.include_filesystem_uris) finally: updated_count = differ_counter.get(DataMismatchCounterEnum.UPDATE) if updated_count > 0: stdout.print(f"Updated {updated_count} entries!", style="success") else: stdout.print("No entries were updated.", style="notice") skipped_count = differ_counter.get(DataMismatchCounterEnum.SKIP) if skipped_count > 0: stdout.print(f"Skipped {skipped_count} entries.", style="notice") sync_only_in_main(db, include_filesystem_uris=args.include_filesystem_uris) sync_only_in_ext(db, include_filesystem_uris=args.include_filesystem_uris) stdout.print("Success!", style="success") return 0
def sync_only_in_ext(db: Connection, *, include_filesystem_uris: bool = False) -> int: stdout.print("Copying entries only in external database to internal database.", style="notice") insert_query = "INSERT INTO main.corrections (track_uri, title, artist, album)" query = f"{insert_query} {load_only_in_ext_query}" if not include_filesystem_uris: query += ( " AND ext.corrections.track_uri NOT LIKE 'file:%'" " AND ext.corrections.track_uri NOT LIKE 'local:%'" ) cursor = db.execute(query) entry_count = cursor.rowcount if entry_count > 0: stdout.print(f"Copied {entry_count} entries!", style="success") else: stdout.print("No entries were copied.", style="notice") return entry_count
def attach_external_db(db: Connection, db_path: Path): db.execute("ATTACH DATABASE ? as ext", (str(db_path), )) stdout.print("Connected to external database.", style="info")
def verify_external_db(db_path: Path, config): _connect_external_db(db_path, config) stdout.print("Verified external database.", style="info")
def connect_external_db(db_path: Path, config) -> Connection: db = _connect_external_db(db_path, config) stdout.print("Connected to external database.", style="info") return db