Update Mangadex Similar to GoldBattles latest version
This commit is contained in:
parent
e500d0bebf
commit
08f1eff450
@ -189,10 +189,6 @@
|
|||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- EH -->
|
<!-- EH -->
|
||||||
<service
|
|
||||||
android:name="exh.md.similar.SimilarUpdateService"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="exh.eh.EHentaiUpdateWorker"
|
android:name="exh.eh.EHentaiUpdateWorker"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||||
|
@ -21,9 +21,6 @@ import eu.kanade.tachiyomi.data.database.queries.HistoryQueries
|
|||||||
import eu.kanade.tachiyomi.data.database.queries.MangaCategoryQueries
|
import eu.kanade.tachiyomi.data.database.queries.MangaCategoryQueries
|
||||||
import eu.kanade.tachiyomi.data.database.queries.MangaQueries
|
import eu.kanade.tachiyomi.data.database.queries.MangaQueries
|
||||||
import eu.kanade.tachiyomi.data.database.queries.TrackQueries
|
import eu.kanade.tachiyomi.data.database.queries.TrackQueries
|
||||||
import exh.md.similar.sql.mappers.SimilarTypeMapping
|
|
||||||
import exh.md.similar.sql.models.MangaSimilar
|
|
||||||
import exh.md.similar.sql.queries.SimilarQueries
|
|
||||||
import exh.merged.sql.mappers.MergedMangaTypeMapping
|
import exh.merged.sql.mappers.MergedMangaTypeMapping
|
||||||
import exh.merged.sql.models.MergedMangaReference
|
import exh.merged.sql.models.MergedMangaReference
|
||||||
import exh.merged.sql.queries.MergedQueries
|
import exh.merged.sql.queries.MergedQueries
|
||||||
@ -42,7 +39,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
|||||||
* This class provides operations to manage the database through its interfaces.
|
* This class provides operations to manage the database through its interfaces.
|
||||||
*/
|
*/
|
||||||
open class DatabaseHelper(context: Context) :
|
open class DatabaseHelper(context: Context) :
|
||||||
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries /* SY --> */, SearchMetadataQueries, SearchTagQueries, SearchTitleQueries, MergedQueries, SimilarQueries /* SY <-- */ {
|
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries /* SY --> */, SearchMetadataQueries, SearchTagQueries, SearchTitleQueries, MergedQueries /* SY <-- */ {
|
||||||
|
|
||||||
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
|
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
|
||||||
.name(DbOpenCallback.DATABASE_NAME)
|
.name(DbOpenCallback.DATABASE_NAME)
|
||||||
@ -62,7 +59,6 @@ open class DatabaseHelper(context: Context) :
|
|||||||
.addTypeMapping(SearchTag::class.java, SearchTagTypeMapping())
|
.addTypeMapping(SearchTag::class.java, SearchTagTypeMapping())
|
||||||
.addTypeMapping(SearchTitle::class.java, SearchTitleTypeMapping())
|
.addTypeMapping(SearchTitle::class.java, SearchTitleTypeMapping())
|
||||||
.addTypeMapping(MergedMangaReference::class.java, MergedMangaTypeMapping())
|
.addTypeMapping(MergedMangaReference::class.java, MergedMangaTypeMapping())
|
||||||
.addTypeMapping(MangaSimilar::class.java, SimilarTypeMapping())
|
|
||||||
// SY <--
|
// SY <--
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.data.database.tables.HistoryTable
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
|
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable
|
||||||
import exh.md.similar.sql.tables.SimilarTable
|
|
||||||
import exh.merged.sql.tables.MergedTable
|
import exh.merged.sql.tables.MergedTable
|
||||||
import exh.metadata.sql.tables.SearchMetadataTable
|
import exh.metadata.sql.tables.SearchMetadataTable
|
||||||
import exh.metadata.sql.tables.SearchTagTable
|
import exh.metadata.sql.tables.SearchTagTable
|
||||||
@ -25,7 +24,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
/**
|
/**
|
||||||
* Version of the database.
|
* Version of the database.
|
||||||
*/
|
*/
|
||||||
const val DATABASE_VERSION = /* SY --> */ 6 /* SY <-- */
|
const val DATABASE_VERSION = /* SY --> */ 7 /* SY <-- */
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||||
@ -40,7 +39,6 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
execSQL(SearchTagTable.createTableQuery)
|
execSQL(SearchTagTable.createTableQuery)
|
||||||
execSQL(SearchTitleTable.createTableQuery)
|
execSQL(SearchTitleTable.createTableQuery)
|
||||||
execSQL(MergedTable.createTableQuery)
|
execSQL(MergedTable.createTableQuery)
|
||||||
execSQL(SimilarTable.createTableQuery)
|
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
// DB indexes
|
// DB indexes
|
||||||
@ -57,7 +55,6 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
execSQL(SearchTitleTable.createMangaIdIndexQuery)
|
execSQL(SearchTitleTable.createMangaIdIndexQuery)
|
||||||
execSQL(SearchTitleTable.createTitleIndexQuery)
|
execSQL(SearchTitleTable.createTitleIndexQuery)
|
||||||
execSQL(MergedTable.createIndexQuery)
|
execSQL(MergedTable.createIndexQuery)
|
||||||
execSQL(SimilarTable.createMangaIdIndexQuery)
|
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,13 +71,16 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
db.execSQL(MergedTable.createTableQuery)
|
db.execSQL(MergedTable.createTableQuery)
|
||||||
db.execSQL(MergedTable.createIndexQuery)
|
db.execSQL(MergedTable.createIndexQuery)
|
||||||
}
|
}
|
||||||
if (oldVersion < 5) {
|
/*if (oldVersion < 5) {
|
||||||
db.execSQL(SimilarTable.createTableQuery)
|
db.execSQL(SimilarTable.createTableQuery)
|
||||||
db.execSQL(SimilarTable.createMangaIdIndexQuery)
|
db.execSQL(SimilarTable.createMangaIdIndexQuery)
|
||||||
}
|
}*/
|
||||||
if (oldVersion < 6) {
|
if (oldVersion < 6) {
|
||||||
db.execSQL(MangaTable.addFilteredScanlators)
|
db.execSQL(MangaTable.addFilteredScanlators)
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 7) {
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS manga_related")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||||
|
@ -26,7 +26,6 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
|
|||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import exh.md.similar.SimilarUpdateService
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
@ -111,9 +110,6 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
"text/plain",
|
"text/plain",
|
||||||
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
||||||
)
|
)
|
||||||
// SY -->
|
|
||||||
ACTION_CANCEL_SIMILAR_UPDATE -> cancelSimilarUpdate(context)
|
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,18 +251,6 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
|
||||||
/**
|
|
||||||
* Method called when user wants to stop a similar manga update
|
|
||||||
*
|
|
||||||
* @param context context of application
|
|
||||||
*/
|
|
||||||
private fun cancelSimilarUpdate(context: Context) {
|
|
||||||
SimilarUpdateService.stop(context)
|
|
||||||
Handler().post { dismissNotification(context, Notifications.ID_SIMILAR_PROGRESS) }
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NAME = "NotificationReceiver"
|
private const val NAME = "NotificationReceiver"
|
||||||
|
|
||||||
@ -299,11 +283,6 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
private const val EXTRA_CHAPTER_URL = "$ID.$NAME.EXTRA_CHAPTER_URL"
|
private const val EXTRA_CHAPTER_URL = "$ID.$NAME.EXTRA_CHAPTER_URL"
|
||||||
private const val EXTRA_IS_LEGACY_BACKUP = "$ID.$NAME.EXTRA_IS_LEGACY_BACKUP"
|
private const val EXTRA_IS_LEGACY_BACKUP = "$ID.$NAME.EXTRA_IS_LEGACY_BACKUP"
|
||||||
|
|
||||||
// Sy -->
|
|
||||||
// Called to cancel similar manga update.
|
|
||||||
private const val ACTION_CANCEL_SIMILAR_UPDATE = "$ID.$NAME.CANCEL_SIMILAR_UPDATE"
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a [PendingIntent] that resumes the download of a chapter
|
* Returns a [PendingIntent] that resumes the download of a chapter
|
||||||
*
|
*
|
||||||
@ -572,20 +551,5 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
|
||||||
/**
|
|
||||||
* Returns [PendingIntent] that starts a service which stops the similar update
|
|
||||||
*
|
|
||||||
* @param context context of application
|
|
||||||
* @return [PendingIntent]
|
|
||||||
*/
|
|
||||||
internal fun cancelSimilarUpdatePendingBroadcast(context: Context): PendingIntent {
|
|
||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
|
||||||
action = ACTION_CANCEL_SIMILAR_UPDATE
|
|
||||||
}
|
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,15 +74,6 @@ object Notifications {
|
|||||||
const val CHANNEL_INCOGNITO_MODE = "incognito_mode_channel"
|
const val CHANNEL_INCOGNITO_MODE = "incognito_mode_channel"
|
||||||
const val ID_INCOGNITO_MODE = -701
|
const val ID_INCOGNITO_MODE = -701
|
||||||
|
|
||||||
// SY -->
|
|
||||||
/**
|
|
||||||
* Notification channel and ids used for backup and restore.
|
|
||||||
*/
|
|
||||||
const val CHANNEL_SIMILAR = "similar_channel"
|
|
||||||
const val ID_SIMILAR_PROGRESS = -901
|
|
||||||
const val ID_SIMILAR_COMPLETE = -902
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
private val deprecatedChannels = listOf(
|
private val deprecatedChannels = listOf(
|
||||||
"downloader_channel",
|
"downloader_channel",
|
||||||
"backup_restore_complete_channel"
|
"backup_restore_complete_channel"
|
||||||
@ -174,14 +165,7 @@ object Notifications {
|
|||||||
CHANNEL_INCOGNITO_MODE,
|
CHANNEL_INCOGNITO_MODE,
|
||||||
context.getString(R.string.pref_incognito_mode),
|
context.getString(R.string.pref_incognito_mode),
|
||||||
NotificationManager.IMPORTANCE_LOW
|
NotificationManager.IMPORTANCE_LOW
|
||||||
),
|
)
|
||||||
NotificationChannel(
|
|
||||||
CHANNEL_SIMILAR,
|
|
||||||
context.getString(R.string.similar_manga),
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
).apply {
|
|
||||||
setShowBadge(false)
|
|
||||||
}
|
|
||||||
).forEach(context.notificationManager::createNotificationChannel)
|
).forEach(context.notificationManager::createNotificationChannel)
|
||||||
|
|
||||||
// Delete old notification channels
|
// Delete old notification channels
|
||||||
|
@ -327,12 +327,6 @@ object PreferenceKeys {
|
|||||||
|
|
||||||
const val mangaDexForceLatestCovers = "manga_dex_force_latest_covers"
|
const val mangaDexForceLatestCovers = "manga_dex_force_latest_covers"
|
||||||
|
|
||||||
const val mangadexSimilarEnabled = "pref_related_show_tab_key"
|
|
||||||
|
|
||||||
const val mangadexSimilarUpdateInterval = "related_update_interval"
|
|
||||||
|
|
||||||
const val mangadexSimilarOnlyOverWifi = "pref_simular_only_over_wifi_key"
|
|
||||||
|
|
||||||
const val mangadexSyncToLibraryIndexes = "pref_mangadex_sync_to_library_indexes"
|
const val mangadexSyncToLibraryIndexes = "pref_mangadex_sync_to_library_indexes"
|
||||||
|
|
||||||
const val preferredMangaDexId = "preferred_mangaDex_id"
|
const val preferredMangaDexId = "preferred_mangaDex_id"
|
||||||
|
@ -446,16 +446,8 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun preferredMangaDexId() = flowPrefs.getString(Keys.preferredMangaDexId, "0")
|
fun preferredMangaDexId() = flowPrefs.getString(Keys.preferredMangaDexId, "0")
|
||||||
|
|
||||||
fun mangadexSimilarEnabled() = flowPrefs.getBoolean(Keys.mangadexSimilarEnabled, false)
|
|
||||||
|
|
||||||
fun shownMangaDexSimilarAskDialog() = flowPrefs.getBoolean("shown_similar_ask_dialog", false)
|
|
||||||
|
|
||||||
fun mangadexSimilarOnlyOverWifi() = flowPrefs.getBoolean(Keys.mangadexSimilarOnlyOverWifi, true)
|
|
||||||
|
|
||||||
fun mangadexSyncToLibraryIndexes() = flowPrefs.getStringSet(Keys.mangadexSyncToLibraryIndexes, emptySet())
|
fun mangadexSyncToLibraryIndexes() = flowPrefs.getStringSet(Keys.mangadexSyncToLibraryIndexes, emptySet())
|
||||||
|
|
||||||
fun mangadexSimilarUpdateInterval() = flowPrefs.getInt(Keys.mangadexSimilarUpdateInterval, 2)
|
|
||||||
|
|
||||||
fun dataSaver() = flowPrefs.getBoolean(Keys.dataSaver, false)
|
fun dataSaver() = flowPrefs.getBoolean(Keys.dataSaver, false)
|
||||||
|
|
||||||
fun ignoreJpeg() = flowPrefs.getBoolean(Keys.ignoreJpeg, false)
|
fun ignoreJpeg() = flowPrefs.getBoolean(Keys.ignoreJpeg, false)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.source.online.all
|
package eu.kanade.tachiyomi.source.online.all
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
@ -242,8 +241,8 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang).fetchRandomMangaId()
|
return MangaHandler(baseHttpClient, headers, mdLang.lang).fetchRandomMangaId()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchMangaSimilar(manga: Manga): MangasPage {
|
suspend fun getMangaSimilar(manga: MangaInfo): MangasPage {
|
||||||
return SimilarHandler(preferences, useLowQualityThumbnail()).fetchSimilar(manga)
|
return SimilarHandler(baseHttpClient, mdLang.lang, preferences, useLowQualityThumbnail()).getSimilar(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
/*private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
||||||
|
@ -35,7 +35,6 @@ import eu.kanade.tachiyomi.source.LocalSource
|
|||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.source.online.LoginSource
|
import eu.kanade.tachiyomi.source.online.LoginSource
|
||||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.FabController
|
import eu.kanade.tachiyomi.ui.base.controller.FabController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
@ -57,7 +56,6 @@ import eu.kanade.tachiyomi.util.view.snack
|
|||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
import eu.kanade.tachiyomi.widget.EmptyView
|
import eu.kanade.tachiyomi.widget.EmptyView
|
||||||
import exh.log.xLogW
|
import exh.log.xLogW
|
||||||
import exh.md.similar.ui.EnableMangaDexSimilarDialogController
|
|
||||||
import exh.savedsearches.EXHSavedSearch
|
import exh.savedsearches.EXHSavedSearch
|
||||||
import exh.source.getMainSource
|
import exh.source.getMainSource
|
||||||
import exh.source.isEhBasedSource
|
import exh.source.isEhBasedSource
|
||||||
@ -172,11 +170,6 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
val mainSource = presenter.source.getMainSource()
|
val mainSource = presenter.source.getMainSource()
|
||||||
if (mainSource is MangaDex && !preferences.mangadexSimilarEnabled().get() && !preferences.shownMangaDexSimilarAskDialog().get()) {
|
|
||||||
EnableMangaDexSimilarDialogController().showDialog(router)
|
|
||||||
preferences.shownMangaDexSimilarAskDialog().set(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainSource is LoginSource && mainSource.requiresLogin && !mainSource.isLogged()) {
|
if (mainSource is LoginSource && mainSource.requiresLogin && !mainSource.isLogged()) {
|
||||||
val dialog = MangadexLoginDialog(mainSource)
|
val dialog = MangadexLoginDialog(mainSource)
|
||||||
dialog.showDialog(router)
|
dialog.showDialog(router)
|
||||||
|
@ -102,7 +102,7 @@ import eu.kanade.tachiyomi.util.view.getCoordinates
|
|||||||
import eu.kanade.tachiyomi.util.view.shrinkOnScroll
|
import eu.kanade.tachiyomi.util.view.shrinkOnScroll
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
import eu.kanade.tachiyomi.util.view.snack
|
||||||
import exh.log.xLogD
|
import exh.log.xLogD
|
||||||
import exh.md.similar.ui.MangaDexSimilarController
|
import exh.md.similar.MangaDexSimilarController
|
||||||
import exh.metadata.metadata.base.FlatMetadata
|
import exh.metadata.metadata.base.FlatMetadata
|
||||||
import exh.recs.RecommendsController
|
import exh.recs.RecommendsController
|
||||||
import exh.source.MERGED_SOURCE_ID
|
import exh.source.MERGED_SOURCE_ID
|
||||||
@ -767,7 +767,7 @@ class MangaController :
|
|||||||
// AZ -->
|
// AZ -->
|
||||||
fun openRecommends() {
|
fun openRecommends() {
|
||||||
val source = presenter.source.getMainSource()
|
val source = presenter.source.getMainSource()
|
||||||
if (source is MangaDex && preferences.mangadexSimilarEnabled().get()) {
|
if (source is MangaDex) {
|
||||||
MaterialDialog(activity!!)
|
MaterialDialog(activity!!)
|
||||||
.title(R.string.az_recommends)
|
.title(R.string.az_recommends)
|
||||||
.listItemsSingleChoice(
|
.listItemsSingleChoice(
|
||||||
|
@ -6,27 +6,16 @@ import com.afollestad.materialdialogs.list.listItemsMultiChoice
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
|
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
|
||||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
|
||||||
import eu.kanade.tachiyomi.util.preference.defaultValue
|
|
||||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
|
||||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
|
||||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||||
import eu.kanade.tachiyomi.util.preference.onClick
|
import eu.kanade.tachiyomi.util.preference.onClick
|
||||||
import eu.kanade.tachiyomi.util.preference.preference
|
import eu.kanade.tachiyomi.util.preference.preference
|
||||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
|
||||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
|
||||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import exh.md.similar.SimilarUpdateJob
|
|
||||||
import exh.md.utils.MdUtil
|
import exh.md.utils.MdUtil
|
||||||
import exh.widget.preference.MangaDexLoginPreference
|
import exh.widget.preference.MangaDexLoginPreference
|
||||||
import exh.widget.preference.MangadexLoginDialog
|
import exh.widget.preference.MangadexLoginDialog
|
||||||
import exh.widget.preference.MangadexLogoutDialog
|
import exh.widget.preference.MangadexLogoutDialog
|
||||||
import kotlinx.coroutines.flow.drop
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
|
|
||||||
class SettingsMangaDexController :
|
class SettingsMangaDexController :
|
||||||
SettingsController(),
|
SettingsController(),
|
||||||
@ -108,76 +97,6 @@ class SettingsMangaDexController :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceCategory {
|
|
||||||
titleRes = R.string.similar_settings
|
|
||||||
|
|
||||||
preference {
|
|
||||||
key = "pref_similar_screen"
|
|
||||||
summaryRes = R.string.similar_screen_summary_message
|
|
||||||
isIconSpaceReserved = true
|
|
||||||
}
|
|
||||||
|
|
||||||
switchPreference {
|
|
||||||
key = PreferenceKeys.mangadexSimilarEnabled
|
|
||||||
titleRes = R.string.similar_screen
|
|
||||||
defaultValue = false
|
|
||||||
onClick {
|
|
||||||
SimilarUpdateJob.setupTask(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switchPreference {
|
|
||||||
key = PreferenceKeys.mangadexSimilarOnlyOverWifi
|
|
||||||
titleRes = R.string.pref_download_only_over_wifi
|
|
||||||
defaultValue = true
|
|
||||||
onClick {
|
|
||||||
SimilarUpdateJob.setupTask(context, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preference {
|
|
||||||
key = "pref_similar_manually_update"
|
|
||||||
titleRes = R.string.similar_manually_update
|
|
||||||
summaryRes = R.string.similar_manually_update_message
|
|
||||||
onClick {
|
|
||||||
SimilarUpdateJob.doWorkNow(context)
|
|
||||||
context.toast(R.string.similar_manually_toast)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
intListPreference {
|
|
||||||
key = PreferenceKeys.mangadexSimilarUpdateInterval
|
|
||||||
titleRes = R.string.similar_update_fequency
|
|
||||||
entriesRes = arrayOf(
|
|
||||||
R.string.update_never,
|
|
||||||
R.string.update_24hour,
|
|
||||||
R.string.update_48hour,
|
|
||||||
R.string.update_weekly,
|
|
||||||
R.string.update_monthly
|
|
||||||
)
|
|
||||||
entryValues = arrayOf("0", "1", "2", "7", "30")
|
|
||||||
defaultValue = "2"
|
|
||||||
|
|
||||||
preferences.mangadexSimilarUpdateInterval()
|
|
||||||
.asImmediateFlow {
|
|
||||||
SimilarUpdateJob.setupTask(context, true)
|
|
||||||
}
|
|
||||||
.drop(1)
|
|
||||||
.launchIn(viewScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
preference {
|
|
||||||
key = "similar_credits"
|
|
||||||
titleRes = R.string.similar_credit
|
|
||||||
val url = "https://github.com/goldbattle/MangadexRecomendations"
|
|
||||||
summary = context.getString(R.string.similar_credit_message, url)
|
|
||||||
onClick {
|
|
||||||
openInBrowser(url)
|
|
||||||
}
|
|
||||||
isIconSpaceReserved = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun siteLoginDialogClosed(source: Source) {
|
override fun siteLoginDialogClosed(source: Source) {
|
||||||
|
@ -1,36 +1,40 @@
|
|||||||
package exh.md.handlers
|
package exh.md.handlers
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.await
|
||||||
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import exh.md.similar.sql.models.MangaSimilarImpl
|
import exh.md.handlers.serializers.SimilarMangaResponse
|
||||||
import exh.md.utils.MdUtil
|
import exh.md.utils.MdUtil
|
||||||
import exh.util.executeOnIO
|
import okhttp3.CacheControl
|
||||||
import uy.kohesive.injekt.Injekt
|
import okhttp3.Headers
|
||||||
import uy.kohesive.injekt.api.get
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import tachiyomi.source.model.MangaInfo
|
||||||
|
|
||||||
class SimilarHandler(val preferences: PreferencesHelper, private val useLowQualityCovers: Boolean) {
|
class SimilarHandler(val client: OkHttpClient, val lang: String, val preferences: PreferencesHelper, private val useLowQualityCovers: Boolean) {
|
||||||
|
|
||||||
/**
|
suspend fun getSimilar(manga: MangaInfo): MangasPage {
|
||||||
* fetch our similar mangas
|
val response = client.newCall(similarMangaRequest(manga)).await()
|
||||||
*/
|
return similarMangaParse(response)
|
||||||
suspend fun fetchSimilar(manga: Manga): MangasPage {
|
}
|
||||||
// Parse the Mangadex id from the URL
|
|
||||||
val mangaId = MdUtil.getMangaId(manga.url).toLong()
|
private fun similarMangaRequest(manga: MangaInfo): Request {
|
||||||
val similarMangaDb = Injekt.get<DatabaseHelper>().getSimilar(mangaId).executeOnIO()
|
val tempUrl = MdUtil.similarBaseApi + MdUtil.getMangaId(manga.key) + ".json"
|
||||||
return if (similarMangaDb != null) {
|
return GET(tempUrl, Headers.Builder().build(), CacheControl.FORCE_NETWORK)
|
||||||
val similarMangaTitles = similarMangaDb.matched_titles.split(MangaSimilarImpl.DELIMITER)
|
}
|
||||||
val similarMangaIds = similarMangaDb.matched_ids.split(MangaSimilarImpl.DELIMITER)
|
|
||||||
val similarMangas = similarMangaIds.mapIndexed { index, similarId ->
|
private fun similarMangaParse(response: Response): MangasPage {
|
||||||
|
val mangaList = response.parseAs<SimilarMangaResponse>().matches.map {
|
||||||
SManga.create().apply {
|
SManga.create().apply {
|
||||||
title = similarMangaTitles[index]
|
url = "/manga/" + it.id
|
||||||
url = "/manga/$similarId/"
|
title = MdUtil.cleanString(it.title[lang] ?: it.title["en"]!!)
|
||||||
thumbnail_url = MdUtil.formThumbUrl(url, useLowQualityCovers)
|
thumbnail_url = "https://coverapi.orell.dev/api/v1/mdaltimage/manga/${it.id}/cover"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MangasPage(similarMangas, false)
|
return MangasPage(mangaList, false)
|
||||||
} else MangasPage(mutableListOf(), false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package exh.md.handlers.serializers
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SimilarMangaResponse(
|
||||||
|
val id: String,
|
||||||
|
val title: Map<String, String>,
|
||||||
|
val contentRating: String,
|
||||||
|
val matches: List<Matches>,
|
||||||
|
val updatedAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Matches(
|
||||||
|
val id: String,
|
||||||
|
val title: Map<String, String>,
|
||||||
|
val contentRating: String,
|
||||||
|
val score: Double
|
||||||
|
)
|
@ -1,4 +1,4 @@
|
|||||||
package exh.md.similar.ui
|
package exh.md.similar
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
@ -1,6 +1,7 @@
|
|||||||
package exh.md.similar.ui
|
package exh.md.similar
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
|
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
|
||||||
@ -16,7 +17,7 @@ import rx.schedulers.Schedulers
|
|||||||
class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() {
|
class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() {
|
||||||
|
|
||||||
override fun requestNext(): Observable<MangasPage> {
|
override fun requestNext(): Observable<MangasPage> {
|
||||||
return runAsObservable({ source.fetchMangaSimilar(manga) })
|
return runAsObservable({ source.getMangaSimilar(manga.toMangaInfo()) })
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnNext {
|
.doOnNext {
|
@ -1,4 +1,4 @@
|
|||||||
package exh.md.similar.ui
|
package exh.md.similar
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
@ -1,74 +0,0 @@
|
|||||||
package exh.md.similar
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.Constraints
|
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
|
||||||
import androidx.work.NetworkType
|
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import androidx.work.Worker
|
|
||||||
import androidx.work.WorkerParameters
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class SimilarUpdateJob(private val context: Context, workerParams: WorkerParameters) :
|
|
||||||
Worker(context, workerParams) {
|
|
||||||
|
|
||||||
override fun doWork(): Result {
|
|
||||||
SimilarUpdateService.start(context)
|
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "RelatedUpdate"
|
|
||||||
|
|
||||||
fun setupTask(context: Context, skipInitial: Boolean = false) {
|
|
||||||
val preferences = Injekt.get<PreferencesHelper>()
|
|
||||||
val enabled = preferences.mangadexSimilarEnabled().get()
|
|
||||||
val interval = preferences.mangadexSimilarUpdateInterval().get()
|
|
||||||
if (enabled) {
|
|
||||||
// We are enabled, so construct the constraints
|
|
||||||
val wifiRestriction = if (preferences.mangadexSimilarOnlyOverWifi().get()) {
|
|
||||||
NetworkType.UNMETERED
|
|
||||||
} else {
|
|
||||||
NetworkType.CONNECTED
|
|
||||||
}
|
|
||||||
val constraints = Constraints.Builder()
|
|
||||||
.setRequiredNetworkType(wifiRestriction)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
// If we are not skipping the initial then run it right now
|
|
||||||
// Note that we won't run it if the constraints are not satisfied
|
|
||||||
if (!skipInitial) {
|
|
||||||
WorkManager.getInstance(context).enqueue(OneTimeWorkRequestBuilder<SimilarUpdateJob>().setConstraints(constraints).build())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally build the periodic request
|
|
||||||
val request = PeriodicWorkRequestBuilder<SimilarUpdateJob>(
|
|
||||||
interval.toLong(),
|
|
||||||
TimeUnit.DAYS,
|
|
||||||
1,
|
|
||||||
TimeUnit.HOURS
|
|
||||||
)
|
|
||||||
.addTag(TAG)
|
|
||||||
.setConstraints(constraints)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
if (interval > 0) {
|
|
||||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
|
||||||
} else {
|
|
||||||
WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun doWorkNow(context: Context) {
|
|
||||||
WorkManager.getInstance(context).enqueue(OneTimeWorkRequestBuilder<SimilarUpdateJob>().build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,351 +0,0 @@
|
|||||||
package exh.md.similar
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.PowerManager
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.text.isDigitsOnly
|
|
||||||
import com.squareup.moshi.JsonReader
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
|
||||||
import eu.kanade.tachiyomi.network.await
|
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
|
||||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
|
||||||
import exh.log.xLogE
|
|
||||||
import exh.md.similar.sql.models.MangaSimilarImpl
|
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import okio.buffer
|
|
||||||
import okio.sink
|
|
||||||
import okio.source
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class SimilarUpdateService(
|
|
||||||
val db: DatabaseHelper = Injekt.get()
|
|
||||||
) : Service() {
|
|
||||||
|
|
||||||
private val client by lazy {
|
|
||||||
Injekt.get<NetworkHelper>().client.newBuilder()
|
|
||||||
// unzip interceptor which will add the correct headers
|
|
||||||
.addNetworkInterceptor { chain ->
|
|
||||||
val originalResponse = chain.proceed(chain.request())
|
|
||||||
originalResponse.newBuilder()
|
|
||||||
.header("Content-Encoding", "gzip")
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wake lock that will be held until the service is destroyed.
|
|
||||||
*/
|
|
||||||
private lateinit var wakeLock: PowerManager.WakeLock
|
|
||||||
|
|
||||||
private val similarServiceScope = CoroutineScope(Dispatchers.IO + Job())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription where the update is done.
|
|
||||||
*/
|
|
||||||
private var job: Job? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pending intent of action that cancels the library update
|
|
||||||
*/
|
|
||||||
private val cancelIntent by lazy {
|
|
||||||
NotificationReceiver.cancelSimilarUpdatePendingBroadcast(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val progressNotification by lazy {
|
|
||||||
NotificationCompat.Builder(this, Notifications.CHANNEL_SIMILAR)
|
|
||||||
.setLargeIcon(BitmapFactory.decodeResource(this.resources, R.mipmap.ic_launcher))
|
|
||||||
.setSmallIcon(R.drawable.ic_tachi)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setContentTitle(getString(R.string.similar_loading_progress_start))
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.addAction(
|
|
||||||
R.drawable.ic_close_24dp,
|
|
||||||
getString(android.R.string.cancel),
|
|
||||||
cancelIntent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method called when the service is created. It injects dagger dependencies and acquire
|
|
||||||
* the wake lock.
|
|
||||||
*/
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
wakeLock = acquireWakeLock("SimilarUpdateService")
|
|
||||||
startForeground(Notifications.ID_SIMILAR_PROGRESS, progressNotification.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stopService(name: Intent?): Boolean {
|
|
||||||
destroyJob()
|
|
||||||
return super.stopService(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
destroyJob()
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun destroyJob() {
|
|
||||||
job?.cancel()
|
|
||||||
if (similarServiceScope.isActive) similarServiceScope.cancel()
|
|
||||||
if (wakeLock.isHeld) {
|
|
||||||
wakeLock.release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method needs to be implemented, but it's not used/needed.
|
|
||||||
*/
|
|
||||||
override fun onBind(intent: Intent): IBinder? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method called when the service receives an intent.
|
|
||||||
*
|
|
||||||
* @param intent the start intent from.
|
|
||||||
* @param flags the flags of the command.
|
|
||||||
* @param startId the start id of this command.
|
|
||||||
* @return the start value of the command.
|
|
||||||
*/
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
if (intent == null) return START_NOT_STICKY
|
|
||||||
|
|
||||||
// Unsubscribe from any previous subscription if needed.
|
|
||||||
job?.cancel()
|
|
||||||
val handler = CoroutineExceptionHandler { _, exception ->
|
|
||||||
xLogE("Similar manga update error", exception)
|
|
||||||
stopSelf(startId)
|
|
||||||
showResultNotification(true)
|
|
||||||
cancelProgressNotification()
|
|
||||||
}
|
|
||||||
job = similarServiceScope.launch(handler) {
|
|
||||||
updateSimilar()
|
|
||||||
}
|
|
||||||
job?.invokeOnCompletion { stopSelf(startId) }
|
|
||||||
|
|
||||||
return START_REDELIVER_INTENT
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that updates the similar database for manga
|
|
||||||
*/
|
|
||||||
private suspend fun updateSimilar() = withIOContext {
|
|
||||||
val response = client
|
|
||||||
.newCall(GET(similarUrl))
|
|
||||||
.await()
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
throw Exception("Error trying to download similar file")
|
|
||||||
}
|
|
||||||
val destinationFile = File(filesDir, "neko-similar.json")
|
|
||||||
val buffer = withIOContext { destinationFile.sink().buffer() }
|
|
||||||
|
|
||||||
// write json to file
|
|
||||||
response.body?.byteStream()?.source()?.use { input ->
|
|
||||||
buffer.use { output ->
|
|
||||||
output.writeAll(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val listSimilar = getSimilar(destinationFile)
|
|
||||||
|
|
||||||
// Delete the old similar table
|
|
||||||
db.deleteAllSimilar().executeAsBlocking()
|
|
||||||
|
|
||||||
val totalManga = listSimilar.size
|
|
||||||
|
|
||||||
// Loop through each and insert into the database
|
|
||||||
|
|
||||||
val dataToInsert = listSimilar.mapIndexed { index, similarFromJson ->
|
|
||||||
showProgressNotification(index, totalManga)
|
|
||||||
|
|
||||||
if (similarFromJson.similarIds.size != similarFromJson.similarTitles.size) {
|
|
||||||
return@mapIndexed null
|
|
||||||
}
|
|
||||||
|
|
||||||
MangaSimilarImpl().apply {
|
|
||||||
id = index.toLong()
|
|
||||||
manga_id = similarFromJson.id.toLong()
|
|
||||||
matched_ids = similarFromJson.similarIds.joinToString(MangaSimilarImpl.DELIMITER)
|
|
||||||
matched_titles = similarFromJson.similarTitles.joinToString(MangaSimilarImpl.DELIMITER)
|
|
||||||
}
|
|
||||||
}.filterNotNull()
|
|
||||||
|
|
||||||
showProgressNotification(dataToInsert.size, totalManga)
|
|
||||||
|
|
||||||
if (dataToInsert.isNotEmpty()) {
|
|
||||||
db.insertSimilar(dataToInsert).executeAsBlocking()
|
|
||||||
}
|
|
||||||
destinationFile.delete()
|
|
||||||
showResultNotification(!this.isActive)
|
|
||||||
cancelProgressNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSimilar(destinationFile: File): List<SimilarFromJson> {
|
|
||||||
val reader = JsonReader.of(destinationFile.source().buffer())
|
|
||||||
|
|
||||||
var processingManga = false
|
|
||||||
var processingTitles = false
|
|
||||||
var mangaId: String? = null
|
|
||||||
var similarIds = mutableListOf<String>()
|
|
||||||
var similarTitles = mutableListOf<String>()
|
|
||||||
val similars = mutableListOf<SimilarFromJson>()
|
|
||||||
|
|
||||||
while (reader.peek() != JsonReader.Token.END_DOCUMENT) {
|
|
||||||
when (reader.peek()) {
|
|
||||||
JsonReader.Token.BEGIN_OBJECT -> {
|
|
||||||
reader.beginObject()
|
|
||||||
}
|
|
||||||
JsonReader.Token.NAME -> {
|
|
||||||
val name = reader.nextName()
|
|
||||||
if (!processingManga && name.isDigitsOnly()) {
|
|
||||||
processingManga = true
|
|
||||||
// similar add id
|
|
||||||
mangaId = name
|
|
||||||
} else if (name == "m_titles") {
|
|
||||||
processingTitles = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JsonReader.Token.BEGIN_ARRAY -> {
|
|
||||||
reader.beginArray()
|
|
||||||
}
|
|
||||||
JsonReader.Token.END_ARRAY -> {
|
|
||||||
reader.endArray()
|
|
||||||
if (processingTitles) {
|
|
||||||
processingManga = false
|
|
||||||
processingTitles = false
|
|
||||||
similars.add(SimilarFromJson(mangaId!!, similarIds.toList(), similarTitles.toList()))
|
|
||||||
mangaId = null
|
|
||||||
similarIds = mutableListOf()
|
|
||||||
similarTitles = mutableListOf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JsonReader.Token.NUMBER -> {
|
|
||||||
similarIds.add(reader.nextInt().toString())
|
|
||||||
}
|
|
||||||
JsonReader.Token.STRING -> {
|
|
||||||
if (processingTitles) {
|
|
||||||
similarTitles.add(reader.nextString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JsonReader.Token.END_OBJECT -> {
|
|
||||||
reader.endObject()
|
|
||||||
}
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return similars
|
|
||||||
}
|
|
||||||
|
|
||||||
data class SimilarFromJson(val id: String, val similarIds: List<String>, val similarTitles: List<String>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the notification containing the currently updating manga and the progress.
|
|
||||||
*
|
|
||||||
* @param current the current progress.
|
|
||||||
* @param total the total progress.
|
|
||||||
*/
|
|
||||||
private fun showProgressNotification(current: Int, total: Int) {
|
|
||||||
notificationManager.notify(
|
|
||||||
Notifications.ID_SIMILAR_PROGRESS,
|
|
||||||
progressNotification
|
|
||||||
.setContentTitle(
|
|
||||||
getString(
|
|
||||||
R.string.similar_loading_percent,
|
|
||||||
current,
|
|
||||||
total
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setProgress(total, current, false)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the notification containing the result of the update done by the service.
|
|
||||||
*
|
|
||||||
* @param error if the result was a error.
|
|
||||||
*/
|
|
||||||
private fun showResultNotification(error: Boolean = false) {
|
|
||||||
val title = if (error) {
|
|
||||||
getString(R.string.similar_loading_complete_error)
|
|
||||||
} else {
|
|
||||||
getString(
|
|
||||||
R.string.similar_loading_complete
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = NotificationCompat.Builder(this, Notifications.CHANNEL_SIMILAR)
|
|
||||||
.setContentTitle(title)
|
|
||||||
.setLargeIcon(BitmapFactory.decodeResource(this.resources, R.mipmap.ic_launcher))
|
|
||||||
.setSmallIcon(R.drawable.ic_tachi)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
NotificationManagerCompat.from(this)
|
|
||||||
.notify(Notifications.ID_SIMILAR_COMPLETE, result.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the progress notification.
|
|
||||||
*/
|
|
||||||
private fun cancelProgressNotification() {
|
|
||||||
notificationManager.cancel(Notifications.ID_SIMILAR_PROGRESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val similarUrl = "https://raw.githubusercontent.com/goldbattle/MangadexRecomendations/master/output/mangas_compressed.json.gz"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the status of the service.
|
|
||||||
*
|
|
||||||
* @param context the application context.
|
|
||||||
* @return true if the service is running, false otherwise.
|
|
||||||
*/
|
|
||||||
fun isRunning(context: Context): Boolean {
|
|
||||||
return context.isServiceRunning(SimilarUpdateService::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the service. It will be started only if there isn't another instance already
|
|
||||||
* running.
|
|
||||||
*
|
|
||||||
* @param context the application context.
|
|
||||||
*/
|
|
||||||
fun start(context: Context) {
|
|
||||||
if (!isRunning(context)) {
|
|
||||||
val intent = Intent(context, SimilarUpdateService::class.java)
|
|
||||||
ContextCompat.startForegroundService(context, intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the service.
|
|
||||||
*
|
|
||||||
* @param context the application context.
|
|
||||||
*/
|
|
||||||
fun stop(context: Context) {
|
|
||||||
context.stopService(Intent(context, SimilarUpdateService::class.java))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package exh.md.similar.sql.mappers
|
|
||||||
|
|
||||||
import android.database.Cursor
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
|
||||||
import exh.md.similar.sql.models.MangaSimilar
|
|
||||||
import exh.md.similar.sql.models.MangaSimilarImpl
|
|
||||||
import exh.md.similar.sql.tables.SimilarTable.COL_ID
|
|
||||||
import exh.md.similar.sql.tables.SimilarTable.COL_MANGA_ID
|
|
||||||
import exh.md.similar.sql.tables.SimilarTable.COL_MANGA_SIMILAR_MATCHED_IDS
|
|
||||||
import exh.md.similar.sql.tables.SimilarTable.COL_MANGA_SIMILAR_MATCHED_TITLES
|
|
||||||
import exh.md.similar.sql.tables.SimilarTable.TABLE
|
|
||||||
|
|
||||||
class SimilarTypeMapping : SQLiteTypeMapping<MangaSimilar>(
|
|
||||||
SimilarPutResolver(),
|
|
||||||
SimilarGetResolver(),
|
|
||||||
SimilarDeleteResolver()
|
|
||||||
)
|
|
||||||
|
|
||||||
class SimilarPutResolver : DefaultPutResolver<MangaSimilar>() {
|
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: MangaSimilar) = InsertQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: MangaSimilar) = UpdateQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToContentValues(obj: MangaSimilar) = contentValuesOf(
|
|
||||||
COL_ID to obj.id,
|
|
||||||
COL_MANGA_ID to obj.manga_id,
|
|
||||||
COL_MANGA_SIMILAR_MATCHED_IDS to obj.matched_ids,
|
|
||||||
COL_MANGA_SIMILAR_MATCHED_TITLES to obj.matched_titles
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SimilarGetResolver : DefaultGetResolver<MangaSimilar>() {
|
|
||||||
|
|
||||||
override fun mapFromCursor(cursor: Cursor): MangaSimilar = MangaSimilarImpl().apply {
|
|
||||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
|
||||||
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
|
|
||||||
matched_ids = cursor.getString(cursor.getColumnIndex(COL_MANGA_SIMILAR_MATCHED_IDS))
|
|
||||||
matched_titles = cursor.getString(cursor.getColumnIndex(COL_MANGA_SIMILAR_MATCHED_TITLES))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SimilarDeleteResolver : DefaultDeleteResolver<MangaSimilar>() {
|
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: MangaSimilar) = DeleteQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package exh.md.similar.sql.models
|
|
||||||
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object containing the history statistics of a chapter
|
|
||||||
*/
|
|
||||||
interface MangaSimilar : Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id of this similar manga object.
|
|
||||||
*/
|
|
||||||
var id: Long?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id of matching manga
|
|
||||||
*/
|
|
||||||
var manga_id: Long?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSONArray.toString() list of ids for this manga
|
|
||||||
* Example: [3467, 5907, 21052, 2141, 6139, 5602, 3999]
|
|
||||||
*/
|
|
||||||
var matched_ids: String
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSONArray.toString() list of titles for this manga
|
|
||||||
* Example: [Title1, Title2, ..., Title10]
|
|
||||||
*/
|
|
||||||
var matched_titles: String
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package exh.md.similar.sql.models
|
|
||||||
|
|
||||||
class MangaSimilarImpl : MangaSimilar {
|
|
||||||
|
|
||||||
override var id: Long? = null
|
|
||||||
|
|
||||||
override var manga_id: Long? = null
|
|
||||||
|
|
||||||
override lateinit var matched_ids: String
|
|
||||||
|
|
||||||
override lateinit var matched_titles: String
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other == null || javaClass != other.javaClass) return false
|
|
||||||
|
|
||||||
other as MangaSimilar
|
|
||||||
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (manga_id != other.manga_id) return false
|
|
||||||
if (matched_ids != other.matched_ids) return false
|
|
||||||
return matched_titles != other.matched_titles
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return id.hashCode() + manga_id.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val DELIMITER = "|*|"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package exh.md.similar.sql.queries
|
|
||||||
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.Query
|
|
||||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
|
||||||
import exh.md.similar.sql.models.MangaSimilar
|
|
||||||
import exh.md.similar.sql.tables.SimilarTable
|
|
||||||
|
|
||||||
interface SimilarQueries : DbProvider {
|
|
||||||
|
|
||||||
fun getAllSimilar() = db.get()
|
|
||||||
.listOfObjects(MangaSimilar::class.java)
|
|
||||||
.withQuery(
|
|
||||||
Query.builder()
|
|
||||||
.table(SimilarTable.TABLE)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.prepare()
|
|
||||||
|
|
||||||
fun getSimilar(manga_id: Long) = db.get()
|
|
||||||
.`object`(MangaSimilar::class.java)
|
|
||||||
.withQuery(
|
|
||||||
Query.builder()
|
|
||||||
.table(SimilarTable.TABLE)
|
|
||||||
.where("${SimilarTable.COL_MANGA_ID} = ?")
|
|
||||||
.whereArgs(manga_id)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.prepare()
|
|
||||||
|
|
||||||
fun insertSimilar(similar: MangaSimilar) = db.put().`object`(similar).prepare()
|
|
||||||
|
|
||||||
fun insertSimilar(similarList: List<MangaSimilar>) = db.put().objects(similarList).prepare()
|
|
||||||
|
|
||||||
fun deleteAllSimilar() = db.delete()
|
|
||||||
.byQuery(
|
|
||||||
DeleteQuery.builder()
|
|
||||||
.table(SimilarTable.TABLE)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.prepare()
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package exh.md.similar.sql.tables
|
|
||||||
|
|
||||||
object SimilarTable {
|
|
||||||
|
|
||||||
const val TABLE = "manga_related"
|
|
||||||
|
|
||||||
const val COL_ID = "_id"
|
|
||||||
|
|
||||||
const val COL_MANGA_ID = "manga_id"
|
|
||||||
|
|
||||||
const val COL_MANGA_SIMILAR_MATCHED_IDS = "matched_ids"
|
|
||||||
|
|
||||||
const val COL_MANGA_SIMILAR_MATCHED_TITLES = "matched_titles"
|
|
||||||
|
|
||||||
val createTableQuery: String
|
|
||||||
get() =
|
|
||||||
"""CREATE TABLE $TABLE(
|
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
$COL_MANGA_ID INTEGER NOT NULL,
|
|
||||||
$COL_MANGA_SIMILAR_MATCHED_IDS TEXT NOT NULL,
|
|
||||||
$COL_MANGA_SIMILAR_MATCHED_TITLES TEXT NOT NULL,
|
|
||||||
UNIQUE ($COL_ID) ON CONFLICT REPLACE
|
|
||||||
)"""
|
|
||||||
|
|
||||||
val createMangaIdIndexQuery: String
|
|
||||||
get() = "CREATE INDEX ${TABLE}_${COL_MANGA_SIMILAR_MATCHED_IDS}_index ON $TABLE($COL_MANGA_SIMILAR_MATCHED_IDS)"
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package exh.md.similar.ui
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
|
||||||
import exh.md.similar.SimilarUpdateJob
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class EnableMangaDexSimilarDialogController : DialogController() {
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
|
||||||
val activity = activity!!
|
|
||||||
val preferences = Injekt.get<PreferencesHelper>()
|
|
||||||
return MaterialDialog(activity)
|
|
||||||
.title(text = activity.getString(R.string.similar_ask_to_enable_title))
|
|
||||||
.message(R.string.similar_ask_to_enable)
|
|
||||||
.negativeButton(R.string.similar_ask_to_enable_no, activity.getString(R.string.similar_ask_to_enable_no))
|
|
||||||
.positiveButton(R.string.similar_ask_to_enable_yes, activity.getString(R.string.similar_ask_to_enable_yes)) {
|
|
||||||
preferences.mangadexSimilarEnabled().set(true)
|
|
||||||
SimilarUpdateJob.setupTask(activity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,8 +42,6 @@ class MdUtil {
|
|||||||
const val cdnUrl = "https://mangadex.org" // "https://s0.mangadex.org"
|
const val cdnUrl = "https://mangadex.org" // "https://s0.mangadex.org"
|
||||||
const val baseUrl = "https://mangadex.org"
|
const val baseUrl = "https://mangadex.org"
|
||||||
const val apiUrl = "https://api.mangadex.org"
|
const val apiUrl = "https://api.mangadex.org"
|
||||||
const val apiUrlCdnCache = "https://cdn.statically.io/gh/goldbattle/MangadexRecomendations/master/output/api/"
|
|
||||||
const val apiUrlCache = "https://raw.githubusercontent.com/goldbattle/MangadexRecomendations/master/output/api/"
|
|
||||||
const val imageUrlCacheNotFound = "https://cdn.statically.io/img/raw.githubusercontent.com/CarlosEsco/Neko/master/.github/manga_cover_not_found.png"
|
const val imageUrlCacheNotFound = "https://cdn.statically.io/img/raw.githubusercontent.com/CarlosEsco/Neko/master/.github/manga_cover_not_found.png"
|
||||||
const val atHomeUrl = "$apiUrl/at-home/server"
|
const val atHomeUrl = "$apiUrl/at-home/server"
|
||||||
const val chapterUrl = "$apiUrl/chapter/"
|
const val chapterUrl = "$apiUrl/chapter/"
|
||||||
@ -70,6 +68,10 @@ class MdUtil {
|
|||||||
}.build().toString()
|
}.build().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val similarCache = "https://raw.githubusercontent.com/goldbattle/MangadexRecomendations/master/output/api/"
|
||||||
|
const val similarCacheCdn = "https://cdn.statically.io/gh/goldbattle/MangadexRecomendations/master/output/api/"
|
||||||
|
const val similarBaseApi = "https://api.similarmanga.com/similar/"
|
||||||
|
|
||||||
const val groupSearchUrl = "$baseUrl/groups/0/1/"
|
const val groupSearchUrl = "$baseUrl/groups/0/1/"
|
||||||
const val apiCovers = "/covers"
|
const val apiCovers = "/covers"
|
||||||
const val reportUrl = "https://api.mangadex.network/report"
|
const val reportUrl = "https://api.mangadex.network/report"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user