[importers] Also speed up koreader imports
This commit is contained in:
@ -71,10 +71,14 @@ def import_from_webdav_for_all_users(restart=False, update_retroarch_hash=False)
|
|||||||
|
|
||||||
|
|
||||||
def scan_webdav_for_koreader(webdav_client, user_id, restart=False):
|
def scan_webdav_for_koreader(webdav_client, user_id, restart=False):
|
||||||
"""Check for koreader statistics.sqlite3 and queue import if changed."""
|
"""Check for koreader statistics.sqlite3 and queue import if changed.
|
||||||
|
|
||||||
|
Uses the remote file's ETag (from a single PROPFIND) to detect changes
|
||||||
|
without downloading the file, making the common no-change case fast.
|
||||||
|
"""
|
||||||
koreader_path = DEFAULT_KOREADER_PATH + "statistics.sqlite3"
|
koreader_path = DEFAULT_KOREADER_PATH + "statistics.sqlite3"
|
||||||
try:
|
try:
|
||||||
webdav_client.info(koreader_path)
|
remote_info = webdav_client.info(koreader_path)
|
||||||
except:
|
except:
|
||||||
logger.info(
|
logger.info(
|
||||||
"No koreader stats file found on webdav",
|
"No koreader stats file found on webdav",
|
||||||
@ -82,12 +86,26 @@ def scan_webdav_for_koreader(webdav_client, user_id, restart=False):
|
|||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
remote_etag = remote_info.get("etag") or ""
|
||||||
|
|
||||||
last_import = (
|
last_import = (
|
||||||
KoReaderImport.objects.filter(user_id=user_id, processed_finished__isnull=False)
|
KoReaderImport.objects.filter(user_id=user_id, processed_finished__isnull=False)
|
||||||
.order_by("processed_finished")
|
.order_by("processed_finished")
|
||||||
.last()
|
.last()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if last_import and last_import.webdav_etag and remote_etag:
|
||||||
|
if last_import.webdav_etag == remote_etag:
|
||||||
|
logger.info(
|
||||||
|
"koreader stats file unchanged (ETag match)",
|
||||||
|
extra={
|
||||||
|
"user_id": user_id,
|
||||||
|
"etag": remote_etag,
|
||||||
|
"last_import_id": last_import.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
file_path = fetch_file_from_webdav(user_id)
|
file_path = fetch_file_from_webdav(user_id)
|
||||||
if not file_path:
|
if not file_path:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
@ -99,9 +117,9 @@ def scan_webdav_for_koreader(webdav_client, user_id, restart=False):
|
|||||||
new_hash = get_file_md5_hash(file_path)
|
new_hash = get_file_md5_hash(file_path)
|
||||||
old_hash = last_import.file_md5_hash() if last_import else None
|
old_hash = last_import.file_md5_hash() if last_import else None
|
||||||
|
|
||||||
if last_import and new_hash == old_hash:
|
if last_import and remote_etag and new_hash == old_hash:
|
||||||
logger.info(
|
logger.info(
|
||||||
"koreader stats file has not changed",
|
"koreader stats file has not changed (content hash match)",
|
||||||
extra={
|
extra={
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
"new_hash": new_hash,
|
"new_hash": new_hash,
|
||||||
@ -124,6 +142,7 @@ def scan_webdav_for_koreader(webdav_client, user_id, restart=False):
|
|||||||
os.unlink(file_path)
|
os.unlink(file_path)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
koreader_import.webdav_etag = remote_etag
|
||||||
koreader_import.save_sqlite_file_to_self(file_path)
|
koreader_import.save_sqlite_file_to_self(file_path)
|
||||||
process_koreader_import.delay(koreader_import.id)
|
process_koreader_import.delay(koreader_import.id)
|
||||||
os.unlink(file_path)
|
os.unlink(file_path)
|
||||||
@ -219,8 +238,8 @@ def scan_webdav_for_retroarch(webdav_client, user_id, update_hash_only=False):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
lrtl_files = sorted(
|
lrtl_files = sorted(
|
||||||
[f for f in files if f.get("name", "").lower().endswith(".lrtl")],
|
[f for f in files if (f.get("name") or "").lower().endswith(".lrtl")],
|
||||||
key=lambda x: x.get("name", ""),
|
key=lambda x: (x.get("name") or ""),
|
||||||
)
|
)
|
||||||
if not lrtl_files:
|
if not lrtl_files:
|
||||||
logger.info("No .lrtl files found on webdav", extra={"user_id": user_id})
|
logger.info("No .lrtl files found on webdav", extra={"user_id": user_id})
|
||||||
@ -239,7 +258,7 @@ def scan_webdav_for_retroarch(webdav_client, user_id, update_hash_only=False):
|
|||||||
# Compute hash from filenames + ETags (no downloads needed)
|
# Compute hash from filenames + ETags (no downloads needed)
|
||||||
hasher = hashlib.md5()
|
hasher = hashlib.md5()
|
||||||
for f in lrtl_files:
|
for f in lrtl_files:
|
||||||
hasher.update(f.get("name", "").encode())
|
hasher.update((f.get("name") or "").encode())
|
||||||
hasher.update((f.get("etag") or f.get("modified") or "").encode())
|
hasher.update((f.get("etag") or f.get("modified") or "").encode())
|
||||||
content_hash = hasher.hexdigest()
|
content_hash = hasher.hexdigest()
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.29 on 2026-05-26 13:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("scrobbles", "0086_add_import_in_progress_to_lastfmimport"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="koreaderimport",
|
||||||
|
name="webdav_etag",
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -188,6 +188,7 @@ class KoReaderImport(BaseFileImportMixin):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
sqlite_file = models.FileField(upload_to=get_path, **BNULL)
|
sqlite_file = models.FileField(upload_to=get_path, **BNULL)
|
||||||
|
webdav_etag = models.CharField(max_length=255, **BNULL)
|
||||||
|
|
||||||
def save_sqlite_file_to_self(self, file_path):
|
def save_sqlite_file_to_self(self, file_path):
|
||||||
with open(file_path, "rb") as f:
|
with open(file_path, "rb") as f:
|
||||||
|
|||||||
Reference in New Issue
Block a user