diff --git a/src/ja/ganma/build.gradle b/src/ja/ganma/build.gradle
index 198d3624a..e1a68c496 100644
--- a/src/ja/ganma/build.gradle
+++ b/src/ja/ganma/build.gradle
@@ -1,6 +1,6 @@
 ext {
     extName = 'GANMA!'
-    extClass = '.GanmaFactory'
+    extClass = '.Ganma'
     extVersionCode = 2
 }
 
diff --git a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/Ganma.kt b/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/Ganma.kt
index 2f5b2496a..c928be218 100644
--- a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/Ganma.kt
+++ b/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/Ganma.kt
@@ -1,11 +1,8 @@
 package eu.kanade.tachiyomi.extension.ja.ganma
 
-import androidx.preference.EditTextPreference
-import androidx.preference.PreferenceScreen
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.asObservable
 import eu.kanade.tachiyomi.network.asObservableSuccess
-import eu.kanade.tachiyomi.source.ConfigurableSource
 import eu.kanade.tachiyomi.source.model.Filter
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.source.model.MangasPage
@@ -13,16 +10,16 @@ import eu.kanade.tachiyomi.source.model.Page
 import eu.kanade.tachiyomi.source.model.SChapter
 import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.HttpSource
+import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.decodeFromStream
 import okhttp3.Request
 import okhttp3.Response
 import rx.Observable
+import uy.kohesive.injekt.injectLazy
 
-open class Ganma : HttpSource(), ConfigurableSource {
-    override val id = sourceId
-    override val name = sourceName
-    override val lang = sourceLang
-    override val versionId = sourceVersionId
+class Ganma : HttpSource() {
+    override val name = "GANMA!"
+    override val lang = "ja"
     override val baseUrl = "https://ganma.jp"
     override val supportsLatest = true
 
@@ -112,11 +109,5 @@ open class Ganma : HttpSource(), ConfigurableSource {
         json.decodeFromStream<Result<T>>(it.body.byteStream()).root
     }
 
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        EditTextPreference(screen.context).apply {
-            key = METADATA_PREF
-            title = "Metadata (Debug)"
-            setDefaultValue("")
-        }.let { screen.addPreference(it) }
-    }
+    val json: Json by injectLazy()
 }
diff --git a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaApp.kt b/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaApp.kt
deleted file mode 100644
index 380488c82..000000000
--- a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaApp.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-package eu.kanade.tachiyomi.extension.ja.ganma
-
-import android.widget.Toast
-import androidx.preference.PreferenceScreen
-import androidx.preference.SwitchPreferenceCompat
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.source.model.Page
-import eu.kanade.tachiyomi.source.model.SChapter
-import eu.kanade.tachiyomi.source.model.SManga
-import okhttp3.Cookie
-import okhttp3.CookieJar
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.HttpUrl
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import okhttp3.Response
-
-class GanmaApp(private val metadata: Metadata) : Ganma() {
-
-    override val client = network.client.newBuilder()
-        .cookieJar(Cookies(metadata.baseUrl.toHttpUrl().host, metadata.cookieName))
-        .build()
-
-    private val appHeaders: Headers = Headers.Builder().apply {
-        add("User-Agent", metadata.userAgent)
-        add("X-From", metadata.baseUrl)
-    }.build()
-
-    override fun chapterListRequest(manga: SManga): Request {
-        checkSession()
-        return GET(metadata.baseUrl + String.format(metadata.magazineUrl, manga.url.mangaId()), appHeaders)
-    }
-
-    override fun List<SChapter>.sortedDescending() = this
-
-    override fun pageListRequest(chapter: SChapter): Request {
-        checkSession()
-        val (mangaId, chapterId) = chapter.url.chapterDir()
-        return GET(metadata.baseUrl + String.format(metadata.storyUrl, mangaId, chapterId), appHeaders)
-    }
-
-    override fun pageListParse(chapter: SChapter, response: Response): List<Page> =
-        try {
-            response.parseAs<AppStory>().toPageList()
-        } catch (e: Exception) {
-            throw Exception("Chapter not available!")
-        }
-
-    private fun checkSession() {
-        val expiration = preferences.getLong(SESSION_EXPIRATION_PREF, 0)
-        if (System.currentTimeMillis() + 60 * 1000 <= expiration) return // at least 1 minute
-        var field1 = preferences.getString(TOKEN_FIELD1_PREF, "")!!
-        var field2 = preferences.getString(TOKEN_FIELD2_PREF, "")!!
-        if (field1.isEmpty() || field2.isEmpty()) {
-            val response = client.newCall(POST(metadata.baseUrl + metadata.tokenUrl, appHeaders)).execute()
-            val token: Map<String, String> = response.parseAs()
-            field1 = token[metadata.tokenField1]!!
-            field2 = token[metadata.tokenField2]!!
-        }
-        val requestBody = FormBody.Builder().apply {
-            add(metadata.tokenField1, field1)
-            add(metadata.tokenField2, field2)
-        }.build()
-        val response = client.newCall(POST(metadata.baseUrl + metadata.sessionUrl, appHeaders, requestBody)).execute()
-        val session: Session = response.parseAs()
-        preferences.edit().apply {
-            putString(TOKEN_FIELD1_PREF, field1)
-            putString(TOKEN_FIELD2_PREF, field2)
-            putLong(SESSION_EXPIRATION_PREF, session.expire)
-        }.apply()
-    }
-
-    private fun clearSession(clearToken: Boolean) {
-        preferences.edit().apply {
-            putString(SESSION_PREF, "")
-            putLong(SESSION_EXPIRATION_PREF, 0)
-            if (clearToken) {
-                putString(TOKEN_FIELD1_PREF, "")
-                putString(TOKEN_FIELD2_PREF, "")
-            }
-        }.apply()
-    }
-
-    override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        super.setupPreferenceScreen(screen)
-        SwitchPreferenceCompat(screen.context).apply {
-            title = "Clear session"
-            setOnPreferenceChangeListener { _, _ ->
-                clearSession(clearToken = false)
-                Toast.makeText(screen.context, "Session cleared", Toast.LENGTH_SHORT).show()
-                false
-            }
-        }.let { screen.addPreference(it) }
-        SwitchPreferenceCompat(screen.context).apply {
-            title = "Clear token"
-            setOnPreferenceChangeListener { _, _ ->
-                clearSession(clearToken = true)
-                Toast.makeText(screen.context, "Token cleared", Toast.LENGTH_SHORT).show()
-                false
-            }
-        }.let { screen.addPreference(it) }
-    }
-
-    class Cookies(private val host: String, private val name: String) : CookieJar {
-        override fun loadForRequest(url: HttpUrl): List<Cookie> {
-            if (url.host != host) return emptyList()
-            val cookie = Cookie.Builder().apply {
-                name(name)
-                value(preferences.getString(SESSION_PREF, "")!!)
-                domain(host)
-            }.build()
-            return listOf(cookie)
-        }
-
-        override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
-            if (url.host != host) return
-            for (cookie in cookies) {
-                if (cookie.name == name) {
-                    preferences.edit().putString(SESSION_PREF, cookie.value).apply()
-                }
-            }
-        }
-    }
-
-    companion object {
-        private const val TOKEN_FIELD1_PREF = "TOKEN_FIELD1"
-        private const val TOKEN_FIELD2_PREF = "TOKEN_FIELD2"
-        private const val SESSION_PREF = "SESSION"
-        private const val SESSION_EXPIRATION_PREF = "SESSION_EXPIRATION"
-    }
-}
diff --git a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaDto.kt b/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaDto.kt
index 72fc0478c..ec78a2ff7 100644
--- a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaDto.kt
+++ b/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaDto.kt
@@ -166,42 +166,3 @@ class Directory(
             Page(i, imageUrl = "$baseUrl$file?$token")
         }
 }
-
-@Serializable
-class AppStory(val pages: List<AppPage>) {
-    fun toPageList(): List<Page> {
-        val result = ArrayList<Page>(pages.size)
-        pages.forEach {
-            if (it.imageURL != null) {
-                result.add(Page(result.size, imageUrl = it.imageURL.url))
-            } else if (it.afterwordImageURL != null) {
-                result.add(Page(result.size, imageUrl = it.afterwordImageURL.url))
-            }
-        }
-        return result
-    }
-}
-
-@Serializable
-class AppPage(
-    val imageURL: File? = null,
-    val afterwordImageURL: File? = null,
-)
-
-// Please keep the data private to support the site,
-// otherwise they might change their APIs.
-@Serializable
-class Metadata(
-    val userAgent: String,
-    val baseUrl: String,
-    val tokenUrl: String,
-    val tokenField1: String,
-    val tokenField2: String,
-    val sessionUrl: String,
-    val cookieName: String,
-    val magazineUrl: String,
-    val storyUrl: String,
-)
-
-@Serializable
-class Session(val expire: Long)
diff --git a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaFactory.kt b/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaFactory.kt
deleted file mode 100644
index 862c4afbe..000000000
--- a/src/ja/ganma/src/eu/kanade/tachiyomi/extension/ja/ganma/GanmaFactory.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package eu.kanade.tachiyomi.extension.ja.ganma
-
-import android.app.Application
-import android.content.SharedPreferences
-import android.util.Base64
-import eu.kanade.tachiyomi.source.Source
-import eu.kanade.tachiyomi.source.SourceFactory
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import java.security.MessageDigest
-
-// source ID needed before class construction
-// generated by running main() below
-const val sourceId = 8045942616403978870
-const val sourceName = "GANMA!"
-const val sourceLang = "ja"
-const val sourceVersionId = 1 // != extension version code
-const val METADATA_PREF = "METADATA"
-
-val json: Json = Injekt.get()
-val preferences: SharedPreferences =
-    Injekt.get<Application>().getSharedPreferences("source_$sourceId", 0x0000)
-
-class GanmaFactory : SourceFactory {
-    override fun createSources(): List<Source> {
-        val source = try {
-            val metadata = preferences.getString(METADATA_PREF, "")!!
-                .also { if (it.isEmpty()) throw Exception() }
-                .let { Base64.decode(it.toByteArray(), Base64.DEFAULT) }
-            GanmaApp(json.decodeFromString(String(metadata)))
-        } catch (e: Exception) {
-            Ganma()
-        }
-        return listOf(source)
-    }
-}
-
-fun main() {
-    println(getSourceId()) // unfortunately there's no constexpr in Kotlin
-}
-
-fun getSourceId() = run { // copied from HttpSource
-    val key = "${sourceName.lowercase()}/$sourceLang/$sourceVersionId"
-    val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
-    (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
-}