Skip to content

Commit

Permalink
Enable backend media handling; add media check
Browse files Browse the repository at this point in the history
  • Loading branch information
dae authored and mikehardy committed Jun 29, 2022
1 parent 76fd011 commit 4f843dd
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,12 @@ internal constructor(
*/
val PRIORITY_FILES = listOf(
SQLiteDBFiles("collection.anki2"), // Anki collection
SQLiteDBFiles("collection.media.ad.db2"), // media database + journal
if (BackendFactory.defaultLegacySchema) {
SQLiteDBFiles("collection.media.ad.db2")
} else {
// this is created on demand in the new backend
OptionalFile("collection.media.db")
}, // media database + journal
OptionalFile(".nomedia"), // written immediately
OptionalFile("collection.log") // written immediately and conflicts
)
Expand Down
10 changes: 8 additions & 2 deletions AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import com.ichi2.libanki.sched.DeckTreeNode
import com.ichi2.libanki.sched.TreeNode
import com.ichi2.utils.*
import com.ichi2.utils.SyncStatus.Companion.ignoreDatabaseModification
import net.ankiweb.rsdroid.BackendFactory
import net.ankiweb.rsdroid.RustCleanup
import org.apache.commons.compress.archivers.zip.ZipFile
import timber.log.Timber
Expand Down Expand Up @@ -1078,8 +1079,13 @@ open class CollectionTask<Progress, Result>(val task: TaskDelegateBase<Progress,
class DeleteMedia(private val unused: List<String>) : TaskDelegate<Void, Int>() {
override fun task(col: Collection, collectionTask: ProgressSenderAndCancelListener<Void>): Int {
val m = col.media
for (fname in unused) {
m.removeFile(fname)
if (!BackendFactory.defaultLegacySchema) {
// FIXME: this provides progress info that is not currently used
col.newMedia.removeFiles(unused)
} else {
for (fname in unused) {
m.removeFile(fname)
}
}
return unused.size
}
Expand Down
71 changes: 71 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/libanki/BackendMedia.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/***************************************************************************************
* Copyright (c) 2012 Ankitects Pty Ltd <http://apps.ankiweb.net> *
* *
* This program is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 3 of the License, or (at your option) any later *
* version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT ANY *
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License along with *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/

package com.ichi2.libanki

class BackendMedia(val col: CollectionV16, server: Boolean) : Media(col, server) {
override fun connect() {
// no-op
}

override fun close() {
// no-op
}

override fun rebuildIfInvalid() {
// no-op
}

override fun findChanges(force: Boolean) {
// no-op
}

override fun markFileAdd(fname: String) {
// no-op; will no longer be called when migrating to new import code.
}

override fun forceResync() {
col.backend.removeMediaDb(col.path)
}

override fun removeFile(fname: String) {
removeFiles(listOf(fname))
}

// markFileAdd

// FIXME: this also provides trash count, but UI can not handle it yet
override fun check(): List<List<String>> {
val out = col.backend.checkMedia()
return listOf(out.missingList, out.unusedList, listOf())
}

// FIXME: this currently removes files immediately, as the UI does not expose a way
// to empty the trash or restore media files yet
fun removeFiles(files: Iterable<String>) {
col.backend.trashMediaFiles(files)
emptyTrash()
}

private fun emptyTrash() {
col.backend.emptyTrash()
}

@Suppress("UNUSED")
private fun restoreTrash() {
col.backend.restoreTrash()
}
}
12 changes: 9 additions & 3 deletions AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ open class Collection constructor(

open val newBackend: CollectionV16
get() = throw Exception("invalid call to newBackend on old backend")
open val newMedia: BackendMedia
get() = throw Exception("invalid call to newMedia on old backend")

@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun debugEnsureNoOpenPointers() {
Expand Down Expand Up @@ -144,8 +146,8 @@ open class Collection constructor(
private var mStartReps: Int

// BEGIN: SQL table columns
var crt: Long = 0
var mod: Long = 0
open var crt: Long = 0
open var mod: Long = 0
var scm: Long = 0
var dirty: Boolean = false
private var mUsn = 0
Expand All @@ -163,7 +165,7 @@ open class Collection constructor(
private var mLogHnd: PrintWriter? = null

init {
media = Media(this, server)
media = initMedia()
val created = reopen()
log(path, VersionUtils.pkgVersionName)
// mLastSave = getTime().now(); // assigned but never accessed - only leaving in for upstream comparison
Expand All @@ -186,6 +188,10 @@ open class Collection constructor(
}
}

protected open fun initMedia(): Media {
return Media(this, server)
}

@KotlinCleanup("remove :DeckManager, remove ? on return value")
protected open fun initDecks(deckConf: String?): DeckManager? {
val deckManager: DeckManager = Decks(this)
Expand Down
17 changes: 17 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/libanki/CollectionV16.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,26 @@ class CollectionV16(
return ConfigV16(RustConfigBackend(backend))
}

override fun initMedia(): BackendMedia {
return BackendMedia(this, server)
}

override val newBackend: CollectionV16
get() = this

override val newMedia: BackendMedia
get() = this.media as BackendMedia

override fun flush(mod: Long) {
// no-op
}

override var mod: Long = 0
get() = db.queryLongScalar("select mod from col")

override var crt: Long = 0
get() = db.queryLongScalar("select crt from col")

/** col.conf is now unused, handled by [ConfigV16] which has a separate table */
override fun flushConf(): Boolean = false

Expand Down
4 changes: 1 addition & 3 deletions AnkiDroid/src/main/java/com/ichi2/libanki/Media.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,6 @@ public Media(Collection col, boolean server) {
Timber.e("Cannot create media directory: %s", mDir);
}
}
// change database
// connect();
}


Expand Down Expand Up @@ -182,7 +180,7 @@ public void _initDB() {
}


public void maybeUpgrade() {
private void maybeUpgrade() {
String oldPath = dir() + ".db";
File oldDbFile = new File(oldPath);
if (oldDbFile.exists()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,15 @@ class MigrateEssentialFilesTest : RobolectricTest() {

col.close() // required for Windows, can't delete if locked.

CompatHelper.compat.deleteFile(File(defaultCollectionSourcePath, "collection.media.ad.db2"))
CompatHelper.compat.deleteFile(File(defaultCollectionSourcePath, "collection.anki2"))

val ex = assertThrows<MissingEssentialFileException> {
executeAlgorithmSuccessfully(defaultCollectionSourcePath) {
Mockito.doReturn(Mockito.mock(LockedCollection::class.java)).whenever(it).createLockedCollection()
}
}

assertThat(ex.file.name, equalTo("collection.media.ad.db2"))
assertThat(ex.file.name, equalTo("collection.anki2"))
}

/**
Expand Down
5 changes: 5 additions & 0 deletions AnkiDroid/src/test/java/com/ichi2/libanki/CheckMediaTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.ichi2.anki.RunInBackground
import com.ichi2.async.CollectionTask
import com.ichi2.async.CollectionTask.CheckMedia
import com.ichi2.async.TaskManager
import net.ankiweb.rsdroid.BackendFactory
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Test
Expand All @@ -38,6 +39,10 @@ class CheckMediaTest : RobolectricTest() {
@Suppress("deprecation")
@Throws(ExecutionException::class, InterruptedException::class)
fun checkMediaWorksAfterMissingMetaTable() {
if (!BackendFactory.defaultLegacySchema) {
// this should not happen on the backend, as it creates the tables in a transaction
return
}
// 7421
col.media.db.database.execSQL("drop table meta")
assertThat(
Expand Down

0 comments on commit 4f843dd

Please sign in to comment.