diff options
author | cos <cos> | 2024-07-03 10:20:17 +0200 |
---|---|---|
committer | cos <cos> | 2024-07-03 11:31:23 +0200 |
commit | 9b452109b4fd29b0eaa659605ccf4ab74ba9693d (patch) | |
tree | b3141f0cfad8225903cad85fe26864220e23e9aa | |
parent | ceda401ec7ddb035602b2e25b526d8e5f8047ff8 (diff) | |
download | AntennaPodDbFixer-9b452109b4fd29b0eaa659605ccf4ab74ba9693d.zip |
Add more, and conditional, progress output
This commit changes the default output to print one dot per step in when
copying tables. That should make it easier to gain a quick understanding
of where a potential failure happens.
A `--verbose` flag is also added, which when given should make it easier
to reproduce the steps of this script manually.
-rwxr-xr-x | AntennaPodDbFixer.py | 79 |
1 files changed, 58 insertions, 21 deletions
diff --git a/AntennaPodDbFixer.py b/AntennaPodDbFixer.py index 840ee03..f772c9d 100755 --- a/AntennaPodDbFixer.py +++ b/AntennaPodDbFixer.py @@ -4,16 +4,29 @@ import json import shutil import os import sys +import getopt def usage(): - return sys.argv[0] + " [--help] [corrupt.db] [empty.db]" + return sys.argv[0] + " [--help] [--verbose] [corrupt.db] [empty.db]" -if len(sys.argv) > 1: - if sys.argv[1] == "--help": +try: + opts, args = getopt.gnu_getopt(sys.argv, "hv", ["help", "verbose"]) +except getopt.GetoptError as err: + print(usage(), file=sys.stderr) + sys.exit(1) + +verbose = False +for o, a in opts: + if o in ("-v", "--verbose"): + verbose = True + elif o in ("-h", "--help"): print(usage()) - exit(0) + sys.exit() else: - inputFilePath = sys.argv[1] + assert False, "Bug: option " + o + " not handled." + +if len(args) >= 1: + inputFilePath = args[1] else: inputFilePath = input("Enter file path to your corrupted AntennaPod database: ") @@ -27,6 +40,9 @@ if dbpage_ext != DBPAGE_SYM: print("Could not detect sqlite3 dbpage extension. Aborting.", file=sys.stderr) print("https://sqlite.org/dbpage.html (required by .recover)", file=sys.stderr) exit(1) +else: + if verbose: + print("It seems sqlite3 on this system has the dbpage extension. Good!") corruptedVersion = subprocess.run( ["sqlite3", inputFilePath, "PRAGMA user_version;"], @@ -38,8 +54,8 @@ if corruptedVersion == "0": exit(1) print("Corrupted file version: " + corruptedVersion) -if len(sys.argv) > 2: - emptyFilePath = sys.argv[2] +if len(args) >= 2: + emptyFilePath = args[2] else: emptyFilePath = "empty/" + corruptedVersion + ".db" if not os.path.isfile(emptyFilePath): @@ -60,6 +76,8 @@ repairedFilePath = inputFilePath + "-repaired.db" sqlFilePath = inputFilePath + ".sql.tmp" workingcopyFilePath = inputFilePath + ".tmp" +if verbose: + print("cp '" + emptyFilePath + "' '" + repairedFilePath + "'") shutil.copyfile(emptyFilePath, repairedFilePath, follow_symlinks=True) if os.path.exists(sqlFilePath): os.remove(sqlFilePath) if os.path.exists(workingcopyFilePath): os.remove(workingcopyFilePath) @@ -67,6 +85,8 @@ if os.path.exists(workingcopyFilePath): os.remove(workingcopyFilePath) # Recover to SQL commands and insert back into a database print("Recovering database.") +if verbose: + print("sqlite3 '" + inputFilePath + "' '.recover --ignore-freelist' >" + sqlFilePath) subprocess.run( ["sqlite3", inputFilePath, ".recover --ignore-freelist"], check=True, @@ -79,6 +99,8 @@ f = open(sqlFilePath,'w') # Avoid a warning that could be confusing to users f.write(filedata.replace("CREATE TABLE sqlite_sequence(name,seq);","")) f.close() +if verbose: + print("sqlite3 '" + workingcopyFilePath + "' <" + sqlFilePath) subprocess.run(["sqlite3", workingcopyFilePath], stdin=open(sqlFilePath, 'r')) print() @@ -88,14 +110,14 @@ def query(db, query): result = "" try: result = subprocess.run( - ["sqlite3", "-json", db, query], + ["sqlite3", "-init", "/tmp/conf", "-json", db, query], capture_output=True, check=True, text=True ).stdout return json.loads(result) except subprocess.CalledProcessError as err: - print(err.stderr) + print(err.stderr, file=sys.stderr) exit(1) except json.decoder.JSONDecodeError as err: return result @@ -103,21 +125,36 @@ def query(db, query): tables = query(emptyFilePath, "SELECT name FROM sqlite_schema WHERE type='table';") for table in tables: table = table["name"] - print("Copying " + table + "...") - columns = query( - emptyFilePath, - "SELECT GROUP_CONCAT(NAME,',') AS columns FROM PRAGMA_TABLE_INFO('" + table + "')" - )[0]["columns"] - - query(repairedFilePath, "DELETE FROM " + table) - query( - repairedFilePath, - "attach '" + workingcopyFilePath + "' AS old; INSERT INTO main." + table + " SELECT " + - columns + " from old." + table + ";" - ) + print("Copying " + table, end="", flush=True) + sql = "SELECT GROUP_CONCAT(NAME,',') AS columns FROM PRAGMA_TABLE_INFO('" + table + "')" + if verbose: + print() + print("sqlite3 '" + emptyFilePath + "'") + print(sql + ";") + else: + print(".", end="", flush=True) + columns = query(emptyFilePath, sql)[0]["columns"] + + sql ="DELETE FROM " + table + if verbose: + print("sqlite3 '" + repairedFilePath + "'") + print(sql + ";") + else: + print(".", end="", flush=True) + query(repairedFilePath, sql) + sql = ("attach '" + workingcopyFilePath + "' AS old; INSERT INTO main." + table + " SELECT " + + columns + " from old." + table + ";") + if verbose: + print(sql + ";") + else: + print(".", end="", flush=True) + query(repairedFilePath, sql) + print() print() # Cleanup +if verbose: + print("rm '" + sqlFilePath + "' '" + workingcopyFilePath + "'") os.remove(sqlFilePath) os.remove(workingcopyFilePath) print("Done. Output file: " + repairedFilePath) |