@@ -292,35 +292,41 @@ class AppContainer(context: Context) {
292292 },
293293 isPresent = { identity -> documentRepository.findByIdentity(identity) != null },
294294 downloadAndInsert = { c ->
295- val fileName = " ${java.util.UUID .randomUUID()} .epub"
296- val file = bookDownloader.download(c.opdsHref, fileName) { _, _ -> }
297- val coverFile: java.io.File ? = null
298- // If identity extraction / OPF read / insert throws after the bytes
299- // landed, delete the temp file before rethrowing so a re-run (this
300- // feature is explicitly re-runnable) doesn't accumulate orphans.
301- try {
302- val identity = extractIdentity(file)
303- if (documentRepository.findByIdentity(identity) != null ) {
295+ // Identity hashing (whole-EPUB hash) and OPF zip parsing are plain
296+ // CPU/IO with no internal dispatcher; the use case runs on Main
297+ // (viewModelScope). Move all per-book work off the main thread to
298+ // avoid an ANR while restoring.
299+ withContext(Dispatchers .IO ) {
300+ val fileName = " ${java.util.UUID .randomUUID()} .epub"
301+ val file = bookDownloader.download(c.opdsHref, fileName) { _, _ -> }
302+ val coverFile: java.io.File ? = null
303+ // If identity extraction / OPF read / insert throws after the bytes
304+ // landed, delete the temp file before rethrowing so a re-run (this
305+ // feature is explicitly re-runnable) doesn't accumulate orphans.
306+ try {
307+ val identity = extractIdentity(file)
308+ if (documentRepository.findByIdentity(identity) != null ) {
309+ file.delete()
310+ coverFile?.delete()
311+ } else {
312+ val opf = readOpfBundle(file, fallbackTitle = c.title)
313+ documentRepository.insert(
314+ identity = identity,
315+ title = c.title,
316+ author = c.authors.firstOrNull(),
317+ downloadUrl = c.opdsHref,
318+ localPath = file.absolutePath,
319+ coverPath = coverFile?.absolutePath,
320+ downloadedAt = System .currentTimeMillis(),
321+ seriesName = opf.seriesName,
322+ seriesIndex = opf.seriesPosition?.toDouble(),
323+ )
324+ }
325+ } catch (t: Throwable ) {
304326 file.delete()
305327 coverFile?.delete()
306- } else {
307- val opf = readOpfBundle(file, fallbackTitle = c.title)
308- documentRepository.insert(
309- identity = identity,
310- title = c.title,
311- author = c.authors.firstOrNull(),
312- downloadUrl = c.opdsHref,
313- localPath = file.absolutePath,
314- coverPath = coverFile?.absolutePath,
315- downloadedAt = System .currentTimeMillis(),
316- seriesName = opf.seriesName,
317- seriesIndex = opf.seriesPosition?.toDouble(),
318- )
328+ throw t
319329 }
320- } catch (t: Throwable ) {
321- file.delete()
322- coverFile?.delete()
323- throw t
324330 }
325331 },
326332 applyPositions = { items -> syncOrchestrator.applyProgressItems(items) },
0 commit comments