Compare commits

...

10 Commits

Author SHA1 Message Date
Jobobby04 2f54f00bf7 Revert "Minor fix for history url"
This reverts commit 28edaca869.
2024-03-17 20:08:03 -04:00
ɴᴇᴋᴏ 87feb58055 Add files via upload (#1120) 2024-03-17 19:57:33 -04:00
Jobobby04 28edaca869 Minor fix for history url 2024-03-17 19:56:06 -04:00
Jobobby04 d14f012bbb Update firebase 2024-03-17 19:53:23 -04:00
Jobobby04 adc6bbf54f Minor doc fix 2024-03-17 19:53:12 -04:00
Jobobby04 2b064baca1 Update baseline 2024-03-17 19:52:59 -04:00
Jobobby04 983a80ba42 History url is not globally unique 2024-03-17 19:52:38 -04:00
Fermín Cirella 911e959fcf Add option to reset custom manga info (#1118)
* Add option to reset custom manga info

* Remove extra line

* Update app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt

---------

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2024-03-16 23:59:19 -04:00
KaiserBh a425cae73b fix: The trigger for library update wasn't working properly. (#1119)
Missed them, so it was always updating library without actually syncing even when the trigger was on.

Signed-off-by: KaiserBh <kaiserbh@proton.me>
2024-03-16 23:56:00 -04:00
Jobobby04 d12a9d329b [skip ci] Add instructions for supporting Cloud Sync Google Drive Impl 2024-03-16 15:59:00 -04:00
16 changed files with 103 additions and 46 deletions
+17
View File
@@ -52,3 +52,20 @@ When creating a fork, remember to:
- To avoid having your data polluting the main app's analytics and crash report services:
- If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/standard/google-services.json) with your own
- If you want to use ACRA crash reporting, replace the `ACRA_URI` endpoint in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts) with your own
### Supporting Cloud Sync - Google Drive Implementation
1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create a new project
3. Go to API & Services -> Library -> Google Drive API and click enable
4. Go to API & Services -> Oauth consent screen
5. Create it, fill in the app name, user support email, and developer contact information
6. In the next screen, click add or remove scopes, and add the `.../auth/drive.appdata` and `.../auth/drive.file` scopes
7. Don't add any test users and go back to the dashboard
8. Click publish
9. Go to API & Services -> Credentials
10. Click Create credentials -> Oauth client ID
11. Select Android, give it a name, and set `eu.kanade.google.oauth` as the package name
12. To get the SHA-1 key, run `keytool -printcert -jarfile app-standard-universal-release.apk` on your apk, and copy the listed SHA-1
13. Expand advanced settings, and enable Custom URL scheme
14. After that just download the json, name it to `client_secrets.json` and put it in `app/src/main/assets/`
@@ -73,7 +73,6 @@ class SyncPreferences(
syncOnChapterOpen = preferenceStore.getBoolean("sync_on_chapter_open", false).get(),
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", false).get(),
syncOnAppResume = preferenceStore.getBoolean("sync_on_app_resume", false).get(),
syncOnLibraryUpdate = preferenceStore.getBoolean("sync_on_library_update", false).get(),
)
}
@@ -86,7 +85,5 @@ class SyncPreferences(
.set(syncTriggerOptions.syncOnAppStart)
preferenceStore.getBoolean("sync_on_app_resume", false)
.set(syncTriggerOptions.syncOnAppResume)
preferenceStore.getBoolean("sync_on_library_update", false)
.set(syncTriggerOptions.syncOnLibraryUpdate)
}
}
@@ -239,6 +239,7 @@ fun LibraryBottomActionMenu(
onClickCleanTitles: (() -> Unit)?,
onClickMigrate: (() -> Unit)?,
onClickAddToMangaDex: (() -> Unit)?,
onClickResetInfo: (() -> Unit)?,
// SY <--
modifier: Modifier = Modifier,
) {
@@ -267,7 +268,7 @@ fun LibraryBottomActionMenu(
}
}
// SY -->
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null || onClickResetInfo != null
val configuration = LocalConfiguration.current
val moveMarkPrev = remember { !configuration.isTabletUi() }
var overFlowOpen by remember { mutableStateOf(false) }
@@ -364,6 +365,12 @@ fun LibraryBottomActionMenu(
onClick = onClickAddToMangaDex,
)
}
if (onClickResetInfo != null) {
DropdownMenuItem(
text = { Text(text = stringResource(SYMR.strings.reset_info)) },
onClick = onClickResetInfo,
)
}
}
} else {
Button(
@@ -313,7 +313,7 @@ class MangaRestorer(
restoreCategories(manga, categories, backupCategories)
restoreChapters(manga, chapters)
restoreTracking(manga, tracks)
restoreHistory(history)
restoreHistory(manga, history)
restoreExcludedScanlators(manga, excludedScanlators)
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
// SY -->
@@ -359,13 +359,14 @@ class MangaRestorer(
}
}
private suspend fun restoreHistory(backupHistory: List<BackupHistory>) {
private suspend fun restoreHistory(manga: Manga, backupHistory: List<BackupHistory>) {
val toUpdate = backupHistory.mapNotNull { history ->
val dbHistory = handler.awaitOneOrNull { historyQueries.getHistoryByChapterUrl(history.url) }
val dbHistory = handler.awaitOneOrNull { historyQueries.getHistoryByChapterUrl(manga.id, history.url) }
val item = history.getHistoryImpl()
if (dbHistory == null) {
val chapter = handler.awaitOneOrNull { chaptersQueries.getChapterByUrl(history.url) }
val chapter = handler.awaitList { chaptersQueries.getChapterByUrl(history.url) }
.find { it.manga_id == manga.id }
return@mapNotNull if (chapter == null) {
// Chapter doesn't exist; skip
null
@@ -817,19 +817,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
KEY_GROUP_EXTRA to groupExtra,
// SY <--
)
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
.addTag(TAG)
.addTag(WORK_NAME_MANUAL)
.setInputData(inputData)
.build()
wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request)
val syncPreferences: SyncPreferences = Injekt.get()
// Only proceed with SyncDataJob if sync is enabled and the specific sync on library update flag is set
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnLibraryUpdate
) {
// Always sync the data before library update if syncing is enabled.
if (syncPreferences.isSyncEnabled()) {
// Check if SyncDataJob is already running
if (wm.isRunning(SyncDataJob.TAG_MANUAL)) {
// SyncDataJob is already running
@@ -842,7 +834,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
.build()
// Chain SyncDataJob to run before LibraryUpdateJob
val inputData = workDataOf(KEY_CATEGORY to category?.id)
val libraryUpdateJob = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
.addTag(TAG)
.addTag(WORK_NAME_MANUAL)
@@ -853,7 +844,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
.then(libraryUpdateJob)
.enqueue()
} else {
val inputData = workDataOf(KEY_CATEGORY to category?.id)
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
.addTag(TAG)
.addTag(WORK_NAME_MANUAL)
@@ -9,21 +9,18 @@ data class SyncTriggerOptions(
val syncOnChapterOpen: Boolean = false,
val syncOnAppStart: Boolean = false,
val syncOnAppResume: Boolean = false,
val syncOnLibraryUpdate: Boolean = false,
) {
fun asBooleanArray() = booleanArrayOf(
syncOnChapterRead,
syncOnChapterOpen,
syncOnAppStart,
syncOnAppResume,
syncOnLibraryUpdate,
)
fun anyEnabled() = syncOnChapterRead ||
syncOnChapterOpen ||
syncOnAppStart ||
syncOnAppResume ||
syncOnLibraryUpdate
syncOnAppResume
companion object {
val mainOptions = persistentListOf(
@@ -47,11 +44,6 @@ data class SyncTriggerOptions(
getter = SyncTriggerOptions::syncOnAppResume,
setter = { options, enabled -> options.copy(syncOnAppResume = enabled) },
),
Entry(
label = MR.strings.sync_on_library_update,
getter = SyncTriggerOptions::syncOnLibraryUpdate,
setter = { options, enabled -> options.copy(syncOnLibraryUpdate = enabled) },
),
)
fun fromBooleanArray(array: BooleanArray) = SyncTriggerOptions(
@@ -59,7 +51,6 @@ data class SyncTriggerOptions(
syncOnChapterOpen = array[1],
syncOnAppStart = array[2],
syncOnAppResume = array[3],
syncOnLibraryUpdate = array[4],
)
}
@@ -750,6 +750,24 @@ class LibraryScreenModel(
clearSelection()
}
}
fun resetInfo() {
state.value.selection.fastForEach { (manga) ->
val mangaInfo = CustomMangaInfo(
id = manga.id,
title = null,
author = null,
artist = null,
thumbnailUrl = null,
description = null,
genre = null,
status = null,
)
setCustomMangaInfo.set(mangaInfo)
}
clearSelection()
}
// SY <--
/**
@@ -1336,6 +1354,18 @@ class LibraryScreenModel(
val showAddToMangadex: Boolean by lazy {
selection.any { it.manga.source in mangaDexSourceIds }
}
val showResetInfo: Boolean by lazy {
selection.fastAny { (manga) ->
manga.title != manga.ogTitle ||
manga.author != manga.ogAuthor ||
manga.artist != manga.ogArtist ||
manga.thumbnailUrl != manga.ogThumbnailUrl ||
manga.description != manga.ogDescription ||
manga.genre != manga.ogGenre ||
manga.status != manga.ogStatus
}
}
// SY <--
fun getLibraryItemsByCategoryId(categoryId: Long): List<LibraryItem>? {
@@ -205,6 +205,7 @@ object LibraryTab : Tab {
}
},
onClickAddToMangaDex = screenModel::syncMangaToDex.takeIf { state.showAddToMangadex },
onClickResetInfo = screenModel::resetInfo.takeIf { state.showResetInfo },
// SY <--
)
},
@@ -201,6 +201,7 @@ private fun onViewCreated(manga: Manga, context: Context, binding: EditMangaDial
binding.mangaGenresTags.clearFocus()
binding.resetTags.setOnClickListener { resetTags(manga, binding, scope) }
binding.resetInfo.setOnClickListener { resetInfo(manga, binding, scope) }
}
private fun resetTags(manga: Manga, binding: EditMangaDialogBinding, scope: CoroutineScope) {
@@ -217,6 +218,15 @@ private fun loadCover(manga: Manga, context: Context, binding: EditMangaDialogBi
}
}
private fun resetInfo(manga: Manga, binding: EditMangaDialogBinding, scope: CoroutineScope) {
binding.title.setText("")
binding.mangaAuthor.setText("")
binding.mangaArtist.setText("")
binding.thumbnailUrl.setText("")
binding.mangaDescription.setText("")
resetTags(manga, binding, scope)
}
private fun ChipGroup.setChips(items: List<String>, scope: CoroutineScope) {
removeAllViews()
@@ -139,6 +139,16 @@
android:text="@string/reset_tags"
android:textAllCaps="false" />
<Button
android:id="@+id/reset_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="@string/reset_info"
android:textAllCaps="false" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
+4 -8
View File
@@ -1727,9 +1727,6 @@
<ID>SwallowedException:GalleryAdder.kt$GalleryAdder$e: Exception</ID>
<ID>SwallowedException:GetChapterByUrlAndMangaId.kt$GetChapterByUrlAndMangaId$e: Exception</ID>
<ID>SwallowedException:GetPagePreviews.kt$GetPagePreviews$e: Exception</ID>
<ID>SwallowedException:GoogleDriveSyncService.kt$GoogleDriveService$e: IOException</ID>
<ID>SwallowedException:GoogleDriveSyncService.kt$GoogleDriveService$e: TokenResponseException</ID>
<ID>SwallowedException:GoogleDriveSyncService.kt$GoogleDriveSyncService$e: Exception</ID>
<ID>SwallowedException:HttpSource.kt$HttpSource$e: URISyntaxException</ID>
<ID>SwallowedException:ImageUtil.kt$ImageUtil$e: Exception</ID>
<ID>SwallowedException:Kavita.kt$Kavita$e: Exception</ID>
@@ -1767,7 +1764,6 @@
<ID>SwallowedException:SourceFeedScreenModel.kt$SourceFeedScreenModel$e: Exception</ID>
<ID>SwallowedException:StorageStep.kt$StorageStep$e: ActivityNotFoundException</ID>
<ID>SwallowedException:Suwayomi.kt$Suwayomi$e: Exception</ID>
<ID>SwallowedException:SyncManager.kt$SyncManager$e: IOException</ID>
<ID>SwallowedException:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$e: Exception</ID>
<ID>SwallowedException:UrlImportableSource.kt$UrlImportableSource$e: URISyntaxException</ID>
<ID>ThrowingExceptionsWithoutMessageOrCause:MangaScreenModel.kt$MangaScreenModel$IllegalStateException()</ID>
@@ -1966,12 +1962,12 @@
<ID>TooGenericExceptionThrown:EHentai.kt$EHentai$throw Exception(it.text())</ID>
<ID>TooGenericExceptionThrown:ExtensionLoader.kt$ExtensionLoader$throw Exception("Unknown source class type: ${obj.javaClass}")</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveService$throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": ${e.message}")</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": ${e.message}", e)</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": Max retries reached.")</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_deleting_google_drive_lock_file))</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_uploading_sync_data) + ": ${e.message}")</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_deleting_google_drive_lock_file), e)</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_uploading_sync_data) + ": ${e.message}", e)</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(e.message)</ID>
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(e.message, e)</ID>
<ID>TooGenericExceptionThrown:HttpSource.kt$HttpSource$throw RuntimeException(e)</ID>
<ID>TooGenericExceptionThrown:KitsuApi.kt$KitsuApi$throw Exception("Could not find manga")</ID>
<ID>TooGenericExceptionThrown:KitsuInterceptor.kt$KitsuInterceptor$throw Exception("Not authenticated with Kitsu")</ID>
@@ -31,7 +31,7 @@ H.time_read
FROM history H
JOIN chapters C
ON H.chapter_id = C._id
WHERE C.url = :chapterUrl AND C._id = H.chapter_id;
WHERE C.manga_id = :mangaId AND C.url = :chapterUrl AND C._id = H.chapter_id;
resetHistoryById:
UPDATE history
+3 -3
View File
@@ -1,9 +1,9 @@
[versions]
[libraries]
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.2.2"
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.3.6"
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.5"
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.1"
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.6.2"
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
simularity = "info.debatty:java-string-similarity:2.0.0"
xlog = "com.elvishew:xlog:1.11.0"
@@ -350,6 +350,7 @@
<!-- Entry Info Edit -->
<string name="reset_tags">Reset Tags</string>
<string name="add_tag">Add Tag</string>
<string name="reset_info">Reset Info</string>
<string name="title_hint">Title: %1$s</string>
<string name="description_hint">Description: %1$s</string>
<string name="author_hint">Author: %1$s</string>
@@ -172,6 +172,10 @@
<string name="library_group_updates_global">總是載入全域更新</string>
<string name="library_group_updates_all_but_ungrouped">僅為未分組的作品載入全域更新,為其他載入目錄更新</string>
<string name="library_group_updates_all">每次啟動後載入目錄更新</string>
<string name="pref_mark_read_dupe_chapters">將重複章節標記為已讀</string>
<string name="pref_mark_read_dupe_chapters_summary">閱讀後將重複章節標記為已讀</string>
<string name="pref_library_mark_duplicate_chapters">標記新的重複章節為已讀</string>
<string name="pref_library_mark_duplicate_chapters_summary">自動將讀過的新章節標記為已讀</string>
<!-- Browse settings -->
<string name="pref_hide_feed">隱藏訂閱標籤</string>
@@ -311,9 +315,12 @@
<string name="pref_center_margin">中間空白類型</string>
<string name="pref_center_margin_summary">插入空白以適應可摺疊裝置的螢幕死角。</string>
<!-- Cache archived manga to disk -->
<string name="cache_archived_manga_to_disk">快取 CBZ/CBR 檔案中的圖片</string>
<string name="cache_archived_manga_to_disk_subtitle">閱讀時臨時複製壓縮包中的圖片到快取\n可以改善閱讀器的性能</string>
<!-- Archive reader mode -->
<string name="archive_mode_load_from_file">從文件加載</string>
<string name="archive_mode_load_into_memory">加載到記憶體中</string>
<string name="archive_mode_cache_to_disk">複製到磁碟</string>
<string name="pref_archive_reader_mode">檔案讀取模式</string>
<string name="pref_archive_reader_mode_summary">加載壓縮文件(如CBZ或CBR)中圖像的方式</string>
<!-- Entry Page -->
<!-- Entry Info -->
@@ -591,7 +591,6 @@
<string name="sync_on_chapter_open">Sync on Chapter Open</string>
<string name="sync_on_app_start">Sync on App Start</string>
<string name="sync_on_app_resume">Sync on App Resume</string>
<string name="sync_on_library_update">Sync on Library Update</string>
<string name="sync_library">Sync library</string>
<!-- Advanced section -->