diff --git a/multisrc/build.gradle.kts b/multisrc/build.gradle.kts
index bccb47ba5..c475ae01e 100644
--- a/multisrc/build.gradle.kts
+++ b/multisrc/build.gradle.kts
@@ -20,28 +20,31 @@ repositories {
 // dependencies
 apply("$rootDir/common-dependencies.gradle")
 
-tasks.register("runAllGenerators") {
-    doLast {
-        val isWindows = System.getProperty("os.name").toString().toLowerCase().contains("win")
-        val classPath = (configurations.debugCompileOnly.get().asFileTree.toList() +
+tasks {
+    val runAllGenerators by registering {
+        doLast {
+            val isWindows = System.getProperty("os.name").toString().toLowerCase().contains("win")
+            val classPath = (configurations.debugCompileOnly.get().asFileTree.toList() +
                 listOf(
-                        configurations.androidApis.get().asFileTree.first().absolutePath, // android.jar path
-                        "$projectDir/build/intermediates/aar_main_jar/debug/classes.jar" // jar made from this module
+                    configurations.androidApis.get().asFileTree.first().absolutePath, // android.jar path
+                    "$projectDir/build/intermediates/aar_main_jar/debug/classes.jar" // jar made from this module
                 ))
                 .joinToString(if (isWindows) ";" else ":")
-        val javaPath = "${System.getProperty("java.home")}/bin/java"
+            val javaPath = "${System.getProperty("java.home")}/bin/java"
 
-        val mainClass = "eu.kanade.tachiyomi.multisrc.GeneratorMainKt" // Main class we want to execute
+            val mainClass =
+                "eu.kanade.tachiyomi.multisrc.GeneratorMainKt" // Main class we want to execute
 
-        val javaCommand = if (isWindows) {
-            "\"$javaPath\" -classpath $classPath $mainClass".replace("/", "\\")
-        } else {
-            "$javaPath -classpath $classPath $mainClass"
-        }
-        val javaProcess = Runtime.getRuntime().exec(javaCommand)
-        val exitCode = javaProcess.waitFor()
-        if (exitCode != 0){
-            throw Exception("Java process failed with exit code: $exitCode")
+            val javaCommand = if (isWindows) {
+                "\"$javaPath\" -classpath $classPath $mainClass".replace("/", "\\")
+            } else {
+                "$javaPath -classpath $classPath $mainClass"
+            }
+            val javaProcess = Runtime.getRuntime().exec(javaCommand)
+            val exitCode = javaProcess.waitFor()
+            if (exitCode != 0) {
+                throw Exception("Java process failed with exit code: $exitCode")
+            }
         }
     }
 }
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/GeneratorMain.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/GeneratorMain.kt
index 21f2253b3..9a82badab 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/GeneratorMain.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/GeneratorMain.kt
@@ -5,29 +5,24 @@ import java.io.File
 /**
  * Finds and calls all `ThemeSourceGenerator`s
  */
-
 fun main(args: Array<String>) {
     val userDir = System.getProperty("user.dir")!!
     val sourcesDirPath = "$userDir/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc"
     val sourcesDir = File(sourcesDirPath)
 
-    val directories = sourcesDir.list()!!.filter {
-        File(sourcesDir, it).isDirectory
-    }
-
     // find all theme packages
-    directories.forEach { themeSource ->
-        // find all XxxGenerator.kt files and invoke main from them
-        File("$sourcesDirPath/$themeSource").list()!!
-            .filter {
-                it.endsWith("Generator.kt")
-            }.map {// find java class and extract method lists
-                Class.forName("eu/kanade/tachiyomi/multisrc/$themeSource/$it".replace("/", ".").substringBefore(".kt")).methods.asList()
-            }
-            .flatten()
-            .filter { it.name == "main" }
-            .forEach { it.invoke(null, emptyArray<String>()) }
-    }
+    sourcesDir.list()!!
+        .filter { File(sourcesDir, it).isDirectory }
+        .forEach { themeSource ->
+            // Find all XxxGenerator.kt files and invoke main from them
+            File("$sourcesDirPath/$themeSource").list()!!
+                .filter { it.endsWith("Generator.kt") }
+                .mapNotNull { generatorClass ->
+                    // Find Java class and extract method lists
+                    Class.forName("eu/kanade/tachiyomi/multisrc/$themeSource/$generatorClass".replace("/", ".").substringBefore(".kt"))
+                        .methods
+                        .find { it.name == "main" }
+                }
+                .forEach { it.invoke(null, emptyArray<String>()) }
+        }
 }
-
-
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/ThemeSourceGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/ThemeSourceGenerator.kt
index 49b7e368c..9f2aa6dc0 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/ThemeSourceGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/ThemeSourceGenerator.kt
@@ -22,7 +22,6 @@ interface ThemeSourceGenerator {
      */
     val themePkg: String
 
-
     /**
      * Base theme version, starts with 1 and should be increased when based theme class changes
      */
@@ -35,10 +34,7 @@ interface ThemeSourceGenerator {
 
     fun createAll() {
         val userDir = System.getProperty("user.dir")!!
-
-        sources.forEach { source ->
-            createGradleProject(source, themePkg, themeClass, baseVersionCode, userDir)
-        }
+        sources.forEach { createGradleProject(it, themePkg, themeClass, baseVersionCode, userDir) }
     }
 
     companion object {
@@ -54,40 +50,33 @@ interface ThemeSourceGenerator {
         }
 
         private fun writeGradle(gradle: File, source: ThemeSourceData, baseVersionCode: Int) {
-            gradle.writeText("""apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
+            gradle.writeText("""
+                // THIS FILE IS AUTO-GENERATED; DO NOT EDIT
+                apply plugin: 'com.android.application'
+                apply plugin: 'kotlin-android'
 
-ext {
-    extName = '${source.name}'
-    pkgNameSuffix = '${pkgNameSuffix(source, ".")}'
-    extClass = '.${source.className}'
-    extVersionCode = ${baseVersionCode + source.overrideVersionCode + multisrcLibraryVersion}
-    libVersion = '1.2'
-${if (source.isNsfw) "    containsNsfw = true\n" else ""}}
+                ext {
+                    extName = '${source.name}'
+                    pkgNameSuffix = '${pkgNameSuffix(source, ".")}'
+                    extClass = '.${source.className}'
+                    extVersionCode = ${baseVersionCode + source.overrideVersionCode + multisrcLibraryVersion}
+                    libVersion = '1.2'
+                    ${if (source.isNsfw) "containsNsfw = true\n" else ""}
+                }
 
-apply from: "${'$'}rootDir/common.gradle"
-"""
-            )
+                apply from: "${'$'}rootDir/common.gradle"
+            """.trimIndent())
         }
 
         private fun writeAndroidManifest(androidManifestFile: File) {
-            androidManifestFile.writeText(
-                "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
-                    "<manifest package=\"eu.kanade.tachiyomi.extension\" />\n"
-            )
+            androidManifestFile.writeText("""
+                <?xml version="1.0" encoding="utf-8"?>
+                <!-- THIS FILE IS AUTO-GENERATED; DO NOT EDIT -->
+                <manifest package="eu.kanade.tachiyomi.extension" />
+            """.trimIndent())
         }
 
-        /**
-         * Clears directory recursively
-         */
-        private fun purgeDirectory(dir: File) {
-            for (file in dir.listFiles()!!) {
-                if (file.isDirectory) purgeDirectory(file)
-                file.delete()
-            }
-        }
-
-        fun createGradleProject(source: ThemeSourceData, themePkg: String, themeClass: String, baseVersionCode: Int, userDir: String) {
+        private fun createGradleProject(source: ThemeSourceData, themePkg: String, themeClass: String, baseVersionCode: Int, userDir: String) {
             val projectRootPath = "$userDir/generated-src/${pkgNameSuffix(source, "/")}"
             val projectSrcPath = "$projectRootPath/src/eu/kanade/tachiyomi/extension/${pkgNameSuffix(source, "/")}"
             val overridesPath = "$userDir/multisrc/overrides" // userDir = tachiyomi-extensions project root path
@@ -96,13 +85,12 @@ apply from: "${'$'}rootDir/common.gradle"
             val projectGradleFile = File("$projectRootPath/build.gradle")
             val projectAndroidManifestFile = File("$projectRootPath/AndroidManifest.xml")
 
-
             File(projectRootPath).let { projectRootFile ->
                 println("Working on $source")
 
                 projectRootFile.mkdirs()
                 // remove everything from past runs
-                purgeDirectory(projectRootFile)
+                cleanDirectory(projectRootFile)
 
                 writeGradle(projectGradleFile, source, baseVersionCode)
                 writeAndroidManifest(projectAndroidManifestFile)
@@ -150,74 +138,97 @@ apply from: "${'$'}rootDir/common.gradle"
 
         private fun writeSourceClass(classPath: File, source: ThemeSourceData, themePkg: String, themeClass: String) {
             fun factoryClassText(): String {
-                val sourceListString =
-                    (source as ThemeSourceData.MultiLang).lang.map {
-                        "        $themeClass(\"${source.name}\", \"${source.baseUrl}\", \"$it\"),"
-                    }.joinToString("\n")
+                return when (source) {
+                    is ThemeSourceData.SingleLang -> {
+                        """
+                        class ${source.className} : $themeClass("${source.name}", "${source.baseUrl}", "${source.lang}") {
+                            override val versionId = ${source.versionId}
+                        }
+                        """.trimIndent()
+                    }
+                    is ThemeSourceData.MultiLang -> {
+                        val sourceClasses = source.lang.mapIndexed { index, lang ->
+                            val indexedClassName = "$themeClass${index}"
+                            indexedClassName to """$indexedClassName : $themeClass("${source.name}", "${source.baseUrl}", "$lang") {
+                                override val versionId = ${source.versionId}
+                            }""".trimIndent()
+                        }
 
-                return """class ${source.className} : SourceFactory {
-    override fun createSources(): List<Source> = listOf(
-$sourceListString
-    )
-}"""
-            }
-            File("$classPath/${source.className}.kt").writeText(
-                """package eu.kanade.tachiyomi.extension.${pkgNameSuffix(source, ".")}
-${if (source.isNsfw) "\nimport eu.kanade.tachiyomi.annotations.Nsfw" else ""}
-import eu.kanade.tachiyomi.multisrc.$themePkg.$themeClass
-${if (source is ThemeSourceData.MultiLang)
-                    """import eu.kanade.tachiyomi.source.Source
-import eu.kanade.tachiyomi.source.SourceFactory
-                    """
-                else ""}${if (source.isNsfw) "\n@Nsfw" else ""}
-${if (source is ThemeSourceData.SingleLang) {
-                    "class ${source.className} : $themeClass(\"${source.name}\", \"${source.baseUrl}\", \"${source.lang}\")\n"
-                } else
-                    factoryClassText()
+                        """
+                        class ${source.className} : SourceFactory {
+                            ${sourceClasses.joinToString("\n") { it.second }}
+
+                            override fun createSources() = listOf(
+                                ${sourceClasses.joinToString(",\n") { "${it.first}()" }}
+                            )
+                        }
+                        """.trimIndent()
+                    }
                 }
-""")
+            }
+
+            File("$classPath/${source.className}.kt").writeText("""
+                // THIS FILE IS AUTO-GENERATED; DO NOT EDIT???
+                package eu.kanade.tachiyomi.extension.${pkgNameSuffix(source, ".")}
+
+                import eu.kanade.tachiyomi.multisrc.$themePkg.$themeClass
+                ${if (source is ThemeSourceData.MultiLang) "import eu.kanade.tachiyomi.source.SourceFactory" else ""}
+                ${if (source.isNsfw) "import eu.kanade.tachiyomi.annotations.Nsfw" else ""}
+
+                ${if (source.isNsfw) "\n@Nsfw" else ""}
+                ${factoryClassText()}
+            """.trimIndent())
         }
 
-        sealed class ThemeSourceData {
-            abstract val name: String
-            abstract val baseUrl: String
-            abstract val isNsfw: Boolean
-            abstract val className: String
-            abstract val pkgName: String
-
-            /**
-             * overrideVersionCode defaults to 0, if a source changes their source override code or
-             * a previous existing source suddenly needs source code overrides, overrideVersionCode
-             * should be increased.
-             * When a new source is added with overrides, overrideVersionCode should still be set to 0
-             *
-             * Note: source code overrides are located in "multisrc/overrides/src/<themeName>/<sourceName>"
-             */
-            abstract val overrideVersionCode: Int
-
-            data class SingleLang(
-                override val name: String,
-                override val baseUrl: String,
-                val lang: String,
-                override val isNsfw: Boolean = false,
-                override val className: String = name.replace(" ", ""),
-                override val pkgName: String = className.toLowerCase(Locale.ENGLISH),
-                override val overrideVersionCode: Int = 0,
-            ) : ThemeSourceData()
-
-            data class MultiLang(
-                override val name: String,
-                override val baseUrl: String,
-                val lang: List<String>,
-                override val isNsfw: Boolean = false,
-                override val className: String = name.replace(" ", "") + "Factory",
-                override val pkgName: String = className.substringBefore("Factory").toLowerCase(Locale.ENGLISH),
-                override val overrideVersionCode: Int = 0,
-            ) : ThemeSourceData()
+        private fun cleanDirectory(dir: File) {
+            for (file in dir.listFiles()!!) {
+                if (file.isDirectory) cleanDirectory(file)
+                file.delete()
+            }
         }
     }
 }
 
+sealed class ThemeSourceData {
+    abstract val name: String
+    abstract val baseUrl: String
+    abstract val isNsfw: Boolean
+    abstract val className: String
+    abstract val pkgName: String
+    abstract val versionId: Int
+
+    /**
+     * overrideVersionCode defaults to 0, if a source changes their source override code or
+     * a previous existing source suddenly needs source code overrides, overrideVersionCode
+     * should be increased.
+     * When a new source is added with overrides, overrideVersionCode should still be set to 0
+     *
+     * Note: source code overrides are located in "multisrc/overrides/src/<themeName>/<sourceName>"
+     */
+    abstract val overrideVersionCode: Int
+
+    data class SingleLang(
+        override val name: String,
+        override val baseUrl: String,
+        val lang: String,
+        override val isNsfw: Boolean = false,
+        override val className: String = name.replace(" ", ""),
+        override val pkgName: String = className.toLowerCase(Locale.ENGLISH),
+        override val versionId: Int = 1,
+        override val overrideVersionCode: Int = 0,
+    ) : ThemeSourceData()
+
+    data class MultiLang(
+        override val name: String,
+        override val baseUrl: String,
+        val lang: List<String>,
+        override val isNsfw: Boolean = false,
+        override val className: String = name.replace(" ", "") + "Factory",
+        override val pkgName: String = className.substringBefore("Factory").toLowerCase(Locale.ENGLISH),
+        override val versionId: Int = 1,
+        override val overrideVersionCode: Int = 0,
+    ) : ThemeSourceData()
+}
 
 /**
  * This variable should be increased when the multisrc library changes in a way that prompts global extension upgrade
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicake/ComiCakeGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicake/ComiCakeGenerator.kt
index 0275f9190..452415baa 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicake/ComiCakeGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicake/ComiCakeGenerator.kt
@@ -1,7 +1,7 @@
 package eu.kanade.tachiyomi.multisrc.comicake
 
+import eu.kanade.tachiyomi.multisrc.ThemeSourceData.SingleLang
 import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator
-import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator.Companion.ThemeSourceData.SingleLang
 
 class ComiCakeGenerator : ThemeSourceGenerator {
 
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanGenerator.kt
index 966c817c3..de70526b9 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanGenerator.kt
@@ -1,7 +1,7 @@
 package eu.kanade.tachiyomi.multisrc.genkan
 
+import eu.kanade.tachiyomi.multisrc.ThemeSourceData
 import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator
-import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator.Companion.ThemeSourceData
 
 class GenkanGenerator : ThemeSourceGenerator {
 
@@ -14,7 +14,7 @@ class GenkanGenerator : ThemeSourceGenerator {
     override val sources = listOf(
         ThemeSourceData.MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"),
             className = "LeviatanScansFactory", pkgName = "leviatanscans", overrideVersionCode = 1),
-        ThemeSourceGenerator.Companion.ThemeSourceData.SingleLang("Hunlight Scans", "https://hunlight-scans.info", "en"),
+        ThemeSourceData.SingleLang("Hunlight Scans", "https://hunlight-scans.info", "en"),
         ThemeSourceData.SingleLang("ZeroScans", "https://zeroscans.com", "en"),
         ThemeSourceData.SingleLang("The Nonames Scans", "https://the-nonames.com", "en"),
         ThemeSourceData.SingleLang("Edelgarde Scans", "https://edelgardescans.com", "en"),
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanOriginalGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanOriginalGenerator.kt
index b46bb9469..cc8908527 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanOriginalGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/genkan/GenkanOriginalGenerator.kt
@@ -1,7 +1,7 @@
 package eu.kanade.tachiyomi.multisrc.genkan
 
+import eu.kanade.tachiyomi.multisrc.ThemeSourceData
 import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator
-import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator.Companion.ThemeSourceData
 
 class GenkanOriginalGenerator : ThemeSourceGenerator {
 
@@ -13,7 +13,7 @@ class GenkanOriginalGenerator : ThemeSourceGenerator {
 
     override val sources = listOf(
         ThemeSourceData.SingleLang("Reaper Scans", "https://reaperscans.com", "en"),
-        ThemeSourceData.SingleLang("Hatigarm Scans", "https://hatigarmscanz.net", "en"),
+        ThemeSourceData.SingleLang("Hatigarm Scans", "https://hatigarmscanz.net", "en", versionId = 2),
         ThemeSourceData.SingleLang("SecretScans", "https://secretscans.co", "en"),
     )
 
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
index 20474f5be..c65e48e1b 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
@@ -1,7 +1,7 @@
 package eu.kanade.tachiyomi.multisrc.madara
 
+import eu.kanade.tachiyomi.multisrc.ThemeSourceData.SingleLang
 import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator
-import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator.Companion.ThemeSourceData.SingleLang
 
 class MadaraGenerator : ThemeSourceGenerator {
 
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt
index 5a787e856..4b63502d4 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt
@@ -23,7 +23,6 @@ import java.util.Locale
 import java.util.concurrent.TimeUnit
 
 // Based off of Mangakakalot 1.2.8
-
 abstract class MangaBox(
     override val name: String,
     override val baseUrl: String,
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxGenerator.kt
index d2e4b6081..528d97d24 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxGenerator.kt
@@ -1,8 +1,7 @@
 package eu.kanade.tachiyomi.multisrc.mangabox
 
+import eu.kanade.tachiyomi.multisrc.ThemeSourceData.SingleLang
 import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator
-import eu.kanade.tachiyomi.multisrc.ThemeSourceGenerator.Companion.ThemeSourceData.SingleLang
-
 
 class MangaBoxGenerator : ThemeSourceGenerator {