summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcos <cos>2024-07-03 10:20:17 +0200
committercos <cos>2024-07-03 11:31:23 +0200
commit9b452109b4fd29b0eaa659605ccf4ab74ba9693d (patch)
treeb3141f0cfad8225903cad85fe26864220e23e9aa
parentceda401ec7ddb035602b2e25b526d8e5f8047ff8 (diff)
downloadAntennaPodDbFixer-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-xAntennaPodDbFixer.py79
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)