Move LibraryManga
to domain layer (#8126)
(cherry picked from commit ea8383978b1783fd60d6057deb6c91cb5a85c505) # Conflicts: # app/src/main/java/eu/kanade/data/manga/MangaMapper.kt # app/src/main/java/eu/kanade/presentation/library/LibraryState.kt # app/src/main/java/eu/kanade/tachiyomi/data/database/models/LibraryManga.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt
This commit is contained in:
parent
37fd74f141
commit
71076bd56a
@ -4,35 +4,40 @@ import com.squareup.sqldelight.Query
|
|||||||
import com.squareup.sqldelight.db.SqlCursor
|
import com.squareup.sqldelight.db.SqlCursor
|
||||||
import com.squareup.sqldelight.db.SqlDriver
|
import com.squareup.sqldelight.db.SqlDriver
|
||||||
import com.squareup.sqldelight.internal.copyOnWriteList
|
import com.squareup.sqldelight.internal.copyOnWriteList
|
||||||
|
import eu.kanade.data.listOfStringsAdapter
|
||||||
|
import eu.kanade.data.listOfStringsAndAdapter
|
||||||
import eu.kanade.data.updateStrategyAdapter
|
import eu.kanade.data.updateStrategyAdapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import exh.source.MERGED_SOURCE_ID
|
import exh.source.MERGED_SOURCE_ID
|
||||||
|
|
||||||
private val mapper = { cursor: SqlCursor ->
|
private val mapper = { cursor: SqlCursor ->
|
||||||
LibraryManga().apply {
|
LibraryManga(
|
||||||
id = cursor.getLong(0)!!
|
manga = mangaMapper(
|
||||||
source = cursor.getLong(1)!!
|
cursor.getLong(0)!!,
|
||||||
url = cursor.getString(2)!!
|
cursor.getLong(1)!!,
|
||||||
artist = cursor.getString(3)
|
cursor.getString(2)!!,
|
||||||
author = cursor.getString(4)
|
cursor.getString(3),
|
||||||
description = cursor.getString(5)
|
cursor.getString(4),
|
||||||
genre = cursor.getString(6)
|
cursor.getString(5),
|
||||||
title = cursor.getString(7)!!
|
cursor.getString(6)?.let(listOfStringsAdapter::decode),
|
||||||
status = cursor.getLong(8)!!.toInt()
|
cursor.getString(7)!!,
|
||||||
thumbnail_url = cursor.getString(9)
|
cursor.getLong(8)!!,
|
||||||
favorite = cursor.getLong(10)!! == 1L
|
cursor.getString(9),
|
||||||
last_update = cursor.getLong(11) ?: 0
|
cursor.getLong(10)!! == 1L,
|
||||||
initialized = cursor.getLong(13)!! == 1L
|
cursor.getLong(11) ?: 0,
|
||||||
viewer_flags = cursor.getLong(14)!!.toInt()
|
null,
|
||||||
chapter_flags = cursor.getLong(15)!!.toInt()
|
cursor.getLong(13)!! == 1L,
|
||||||
cover_last_modified = cursor.getLong(16)!!
|
cursor.getLong(14)!!,
|
||||||
date_added = cursor.getLong(17)!!
|
cursor.getLong(15)!!,
|
||||||
filtered_scanlators = cursor.getString(18)
|
cursor.getLong(16)!!,
|
||||||
update_strategy = updateStrategyAdapter.decode(cursor.getLong(19)!!)
|
cursor.getLong(17)!!,
|
||||||
unreadCount = cursor.getLong(20)!!.toInt()
|
cursor.getString(18)?.let(listOfStringsAndAdapter::decode),
|
||||||
readCount = cursor.getLong(21)!!.toInt()
|
updateStrategyAdapter.decode(cursor.getLong(19)!!),
|
||||||
category = cursor.getLong(22)!!.toInt()
|
),
|
||||||
}
|
unreadCount = cursor.getLong(20)!!,
|
||||||
|
readCount = cursor.getLong(21)!!,
|
||||||
|
category = cursor.getLong(22)!!,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class LibraryQuery(val driver: SqlDriver) : Query<LibraryManga>(copyOnWriteList(), mapper) {
|
class LibraryQuery(val driver: SqlDriver) : Query<LibraryManga>(copyOnWriteList(), mapper) {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package eu.kanade.data.manga
|
package eu.kanade.data.manga
|
||||||
|
|
||||||
import eu.kanade.data.listOfStringsAndAdapter
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
|
|
||||||
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List<String>?, UpdateStrategy) -> Manga =
|
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List<String>?, UpdateStrategy) -> Manga =
|
||||||
@ -35,29 +34,32 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List<String>?, UpdateStrategy, Long, Long, Long) -> LibraryManga =
|
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List<String>?, UpdateStrategy, Long, Long, Long) -> LibraryManga =
|
||||||
{ _id, source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, filtered_scanlators, update_strategy, unread_count, read_count, category ->
|
{ _id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, filteredScanlators, updateStrategy, unreadCount, readCount, category ->
|
||||||
LibraryManga().apply {
|
LibraryManga(
|
||||||
this.id = _id
|
manga = mangaMapper(
|
||||||
this.source = source
|
_id,
|
||||||
this.url = url
|
source,
|
||||||
this.artist = artist
|
url,
|
||||||
this.author = author
|
artist,
|
||||||
this.description = description
|
author,
|
||||||
this.genre = genre?.joinToString()
|
description,
|
||||||
this.title = title
|
genre,
|
||||||
this.status = status.toInt()
|
title,
|
||||||
this.thumbnail_url = thumbnail_url
|
status,
|
||||||
this.favorite = favorite
|
thumbnailUrl,
|
||||||
this.last_update = last_update ?: 0
|
favorite,
|
||||||
this.update_strategy = update_strategy
|
lastUpdate,
|
||||||
this.initialized = initialized
|
nextUpdate,
|
||||||
this.viewer_flags = viewer.toInt()
|
initialized,
|
||||||
this.chapter_flags = chapter_flags.toInt()
|
viewerFlags,
|
||||||
this.cover_last_modified = cover_last_modified
|
chapterFlags,
|
||||||
this.date_added = date_added
|
coverLastModified,
|
||||||
this.filtered_scanlators = filtered_scanlators?.let(listOfStringsAndAdapter::encode)
|
dateAdded,
|
||||||
this.unreadCount = unread_count.toInt()
|
filteredScanlators,
|
||||||
this.readCount = read_count.toInt()
|
updateStrategy,
|
||||||
this.category = category.toInt()
|
),
|
||||||
}
|
category = category,
|
||||||
|
unreadCount = unreadCount,
|
||||||
|
readCount = readCount,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import eu.kanade.data.DatabaseHandler
|
|||||||
import eu.kanade.data.listOfStringsAdapter
|
import eu.kanade.data.listOfStringsAdapter
|
||||||
import eu.kanade.data.listOfStringsAndAdapter
|
import eu.kanade.data.listOfStringsAndAdapter
|
||||||
import eu.kanade.data.updateStrategyAdapter
|
import eu.kanade.data.updateStrategyAdapter
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.MangaUpdate
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toLong
|
import eu.kanade.tachiyomi.util.system.toLong
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package eu.kanade.domain.library.model
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
|
||||||
|
data class LibraryManga(
|
||||||
|
val manga: Manga,
|
||||||
|
val category: Long,
|
||||||
|
val unreadCount: Long,
|
||||||
|
val readCount: Long,
|
||||||
|
) {
|
||||||
|
val totalChapters
|
||||||
|
get() = readCount + unreadCount
|
||||||
|
|
||||||
|
val hasStarted
|
||||||
|
get() = readCount > 0
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.domain.manga.interactor
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class GetLibraryManga(
|
class GetLibraryManga(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package eu.kanade.domain.manga.repository
|
package eu.kanade.domain.manga.repository
|
||||||
|
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.MangaUpdate
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface MangaRepository {
|
interface MangaRepository {
|
||||||
|
@ -4,12 +4,12 @@ import androidx.compose.animation.Crossfade
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.presentation.components.LibraryBottomActionMenu
|
import eu.kanade.presentation.components.LibraryBottomActionMenu
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.library.components.LibraryContent
|
import eu.kanade.presentation.library.components.LibraryContent
|
||||||
import eu.kanade.presentation.library.components.LibraryToolbar
|
import eu.kanade.presentation.library.components.LibraryToolbar
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
|
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ fun LibraryScreen(
|
|||||||
onChangeCategoryClicked = onChangeCategoryClicked,
|
onChangeCategoryClicked = onChangeCategoryClicked,
|
||||||
onMarkAsReadClicked = onMarkAsReadClicked,
|
onMarkAsReadClicked = onMarkAsReadClicked,
|
||||||
onMarkAsUnreadClicked = onMarkAsUnreadClicked,
|
onMarkAsUnreadClicked = onMarkAsUnreadClicked,
|
||||||
onDownloadClicked = onDownloadClicked.takeIf { presenter.selection.none { it.source == LocalSource.ID } },
|
onDownloadClicked = onDownloadClicked.takeIf { presenter.selection.none { it.manga.source == LocalSource.ID } },
|
||||||
onDeleteClicked = onDeleteClicked,
|
onDeleteClicked = onDeleteClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
onClickCleanTitles = onClickCleanTitles.takeIf { presenter.showCleanTitles },
|
onClickCleanTitles = onClickCleanTitles.takeIf { presenter.showCleanTitles },
|
||||||
|
@ -7,7 +7,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.domain.library.model.LibraryGroup
|
import eu.kanade.domain.library.model.LibraryGroup
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
|
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
|
||||||
import exh.source.PERV_EDEN_EN_SOURCE_ID
|
import exh.source.PERV_EDEN_EN_SOURCE_ID
|
||||||
import exh.source.PERV_EDEN_IT_SOURCE_ID
|
import exh.source.PERV_EDEN_IT_SOURCE_ID
|
||||||
@ -55,14 +55,14 @@ class LibraryStateImpl : LibraryState {
|
|||||||
override var showSyncExh: Boolean by mutableStateOf(true)
|
override var showSyncExh: Boolean by mutableStateOf(true)
|
||||||
override val showCleanTitles: Boolean by derivedStateOf {
|
override val showCleanTitles: Boolean by derivedStateOf {
|
||||||
selection.any {
|
selection.any {
|
||||||
it.isEhBasedManga() ||
|
it.manga.isEhBasedManga() ||
|
||||||
it.source in nHentaiSourceIds ||
|
it.manga.source in nHentaiSourceIds ||
|
||||||
it.source == PERV_EDEN_EN_SOURCE_ID ||
|
it.manga.source == PERV_EDEN_EN_SOURCE_ID ||
|
||||||
it.source == PERV_EDEN_IT_SOURCE_ID
|
it.manga.source == PERV_EDEN_IT_SOURCE_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override val showAddToMangadex: Boolean by derivedStateOf {
|
override val showAddToMangadex: Boolean by derivedStateOf {
|
||||||
selection.any { it.source in mangaDexSourceIds }
|
selection.any { it.manga.source in mangaDexSourceIds }
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.manga.model.MangaCover
|
import eu.kanade.domain.manga.model.MangaCover
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -41,7 +41,7 @@ fun LibraryComfortableGrid(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
LibraryComfortableGridItem(
|
LibraryComfortableGridItem(
|
||||||
libraryItem,
|
libraryItem,
|
||||||
libraryItem.manga in selection,
|
libraryItem.libraryManga in selection,
|
||||||
onClick,
|
onClick,
|
||||||
onLongClick,
|
onLongClick,
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -60,35 +60,36 @@ fun LibraryComfortableGridItem(
|
|||||||
onLongClick: (LibraryManga) -> Unit,
|
onLongClick: (LibraryManga) -> Unit,
|
||||||
onOpenReader: (LibraryManga) -> Unit,
|
onOpenReader: (LibraryManga) -> Unit,
|
||||||
) {
|
) {
|
||||||
val manga = item.manga
|
val libraryManga = item.libraryManga
|
||||||
|
val manga = libraryManga.manga
|
||||||
LibraryGridItemSelectable(isSelected = isSelected) {
|
LibraryGridItemSelectable(isSelected = isSelected) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick(manga)
|
onClick(libraryManga)
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onLongClick(manga)
|
onLongClick(libraryManga)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
LibraryGridCover(
|
LibraryGridCover(
|
||||||
mangaCover = MangaCover(
|
mangaCover = MangaCover(
|
||||||
manga.id!!,
|
manga.id,
|
||||||
manga.source,
|
manga.source,
|
||||||
manga.favorite,
|
manga.favorite,
|
||||||
manga.thumbnail_url,
|
manga.thumbnailUrl,
|
||||||
manga.cover_last_modified,
|
manga.coverLastModified,
|
||||||
),
|
),
|
||||||
downloadCount = item.downloadCount,
|
downloadCount = item.downloadCount,
|
||||||
unreadCount = item.unreadCount,
|
unreadCount = item.unreadCount,
|
||||||
isLocal = item.isLocal,
|
isLocal = item.isLocal,
|
||||||
language = item.sourceLanguage,
|
language = item.sourceLanguage,
|
||||||
// SY -->
|
// SY -->
|
||||||
showPlayButton = item.startReadingButton && item.manga.unreadCount > 0,
|
showPlayButton = item.startReadingButton && item.libraryManga.unreadCount > 0,
|
||||||
onOpenReader = {
|
onOpenReader = {
|
||||||
onOpenReader(manga)
|
onOpenReader(item.libraryManga)
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,7 @@ import androidx.compose.ui.graphics.Shadow
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -50,7 +50,7 @@ fun LibraryCompactGrid(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
LibraryCompactGridItem(
|
LibraryCompactGridItem(
|
||||||
item = libraryItem,
|
item = libraryItem,
|
||||||
isSelected = libraryItem.manga in selection,
|
isSelected = libraryItem.libraryManga in selection,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -71,34 +71,35 @@ fun LibraryCompactGridItem(
|
|||||||
onOpenReader: (LibraryManga) -> Unit,
|
onOpenReader: (LibraryManga) -> Unit,
|
||||||
// SY <--
|
// SY <--
|
||||||
) {
|
) {
|
||||||
val manga = item.manga
|
val libraryManga = item.libraryManga
|
||||||
|
val manga = libraryManga.manga
|
||||||
LibraryGridCover(
|
LibraryGridCover(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.selectedOutline(isSelected)
|
.selectedOutline(isSelected)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick(manga)
|
onClick(libraryManga)
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onLongClick(manga)
|
onLongClick(libraryManga)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
mangaCover = eu.kanade.domain.manga.model.MangaCover(
|
mangaCover = eu.kanade.domain.manga.model.MangaCover(
|
||||||
manga.id!!,
|
manga.id,
|
||||||
manga.source,
|
manga.source,
|
||||||
manga.favorite,
|
manga.favorite,
|
||||||
manga.thumbnail_url,
|
manga.thumbnailUrl,
|
||||||
manga.cover_last_modified,
|
manga.coverLastModified,
|
||||||
),
|
),
|
||||||
downloadCount = item.downloadCount,
|
downloadCount = item.downloadCount,
|
||||||
unreadCount = item.unreadCount,
|
unreadCount = item.unreadCount,
|
||||||
isLocal = item.isLocal,
|
isLocal = item.isLocal,
|
||||||
language = item.sourceLanguage,
|
language = item.sourceLanguage,
|
||||||
// SY -->
|
// SY -->
|
||||||
showPlayButton = item.startReadingButton && item.manga.unreadCount > 0,
|
showPlayButton = item.startReadingButton && item.libraryManga.unreadCount > 0,
|
||||||
playButtonPosition = PlayButtonPosition.Top,
|
playButtonPosition = PlayButtonPosition.Top,
|
||||||
onOpenReader = {
|
onOpenReader = {
|
||||||
onOpenReader(manga)
|
onOpenReader(item.libraryManga)
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
) {
|
) {
|
||||||
|
@ -21,11 +21,11 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
|||||||
import eu.kanade.core.prefs.PreferenceMutableState
|
import eu.kanade.core.prefs.PreferenceMutableState
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
||||||
import eu.kanade.presentation.library.LibraryState
|
import eu.kanade.presentation.library.LibraryState
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
import eu.kanade.tachiyomi.widget.EmptyView
|
import eu.kanade.tachiyomi.widget.EmptyView
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@ -86,7 +86,7 @@ fun LibraryContent(
|
|||||||
|
|
||||||
val onClickManga = { manga: LibraryManga ->
|
val onClickManga = { manga: LibraryManga ->
|
||||||
if (state.selectionMode.not()) {
|
if (state.selectionMode.not()) {
|
||||||
onMangaClicked(manga.id!!)
|
onMangaClicked(manga.manga.id)
|
||||||
} else {
|
} else {
|
||||||
onToggleSelection(manga)
|
onToggleSelection(manga)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -33,7 +33,7 @@ fun LibraryCoverOnlyGrid(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
LibraryCoverOnlyGridItem(
|
LibraryCoverOnlyGridItem(
|
||||||
item = libraryItem,
|
item = libraryItem,
|
||||||
isSelected = libraryItem.manga in selection,
|
isSelected = libraryItem.libraryManga in selection,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -54,33 +54,34 @@ fun LibraryCoverOnlyGridItem(
|
|||||||
onOpenReader: (LibraryManga) -> Unit,
|
onOpenReader: (LibraryManga) -> Unit,
|
||||||
// SY <--
|
// SY <--
|
||||||
) {
|
) {
|
||||||
val manga = item.manga
|
val libraryManga = item.libraryManga
|
||||||
|
val manga = libraryManga.manga
|
||||||
LibraryGridCover(
|
LibraryGridCover(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.selectedOutline(isSelected)
|
.selectedOutline(isSelected)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick(manga)
|
onClick(libraryManga)
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onLongClick(manga)
|
onLongClick(libraryManga)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
mangaCover = eu.kanade.domain.manga.model.MangaCover(
|
mangaCover = eu.kanade.domain.manga.model.MangaCover(
|
||||||
manga.id!!,
|
manga.id,
|
||||||
manga.source,
|
manga.source,
|
||||||
manga.favorite,
|
manga.favorite,
|
||||||
manga.thumbnail_url,
|
manga.thumbnailUrl,
|
||||||
manga.cover_last_modified,
|
manga.coverLastModified,
|
||||||
),
|
),
|
||||||
downloadCount = item.downloadCount,
|
downloadCount = item.downloadCount,
|
||||||
unreadCount = item.unreadCount,
|
unreadCount = item.unreadCount,
|
||||||
isLocal = item.isLocal,
|
isLocal = item.isLocal,
|
||||||
language = item.sourceLanguage,
|
language = item.sourceLanguage,
|
||||||
// SY -->
|
// SY -->
|
||||||
showPlayButton = item.startReadingButton && item.manga.unreadCount > 0,
|
showPlayButton = item.startReadingButton && item.libraryManga.unreadCount > 0,
|
||||||
onOpenReader = {
|
onOpenReader = {
|
||||||
onOpenReader(manga)
|
onOpenReader(item.libraryManga)
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
|
@ -80,8 +80,8 @@ fun MangaGridCover(
|
|||||||
fun LibraryGridCover(
|
fun LibraryGridCover(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
mangaCover: eu.kanade.domain.manga.model.MangaCover,
|
mangaCover: eu.kanade.domain.manga.model.MangaCover,
|
||||||
downloadCount: Int,
|
downloadCount: Long,
|
||||||
unreadCount: Int,
|
unreadCount: Long,
|
||||||
isLocal: Boolean,
|
isLocal: Boolean,
|
||||||
language: String,
|
language: String,
|
||||||
// SY -->
|
// SY -->
|
||||||
|
@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.manga.model.MangaCover
|
import eu.kanade.domain.manga.model.MangaCover
|
||||||
import eu.kanade.presentation.components.Badge
|
import eu.kanade.presentation.components.Badge
|
||||||
import eu.kanade.presentation.components.BadgeGroup
|
import eu.kanade.presentation.components.BadgeGroup
|
||||||
@ -28,7 +29,6 @@ import eu.kanade.presentation.util.horizontalPadding
|
|||||||
import eu.kanade.presentation.util.selectedBackground
|
import eu.kanade.presentation.util.selectedBackground
|
||||||
import eu.kanade.presentation.util.verticalPadding
|
import eu.kanade.presentation.util.verticalPadding
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -61,7 +61,7 @@ fun LibraryList(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
LibraryListItem(
|
LibraryListItem(
|
||||||
item = libraryItem,
|
item = libraryItem,
|
||||||
isSelected = libraryItem.manga in selection,
|
isSelected = libraryItem.libraryManga in selection,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
)
|
)
|
||||||
@ -76,19 +76,20 @@ fun LibraryListItem(
|
|||||||
onClick: (LibraryManga) -> Unit,
|
onClick: (LibraryManga) -> Unit,
|
||||||
onLongClick: (LibraryManga) -> Unit,
|
onLongClick: (LibraryManga) -> Unit,
|
||||||
) {
|
) {
|
||||||
val manga = item.manga
|
val libraryManga = item.libraryManga
|
||||||
|
val manga = libraryManga.manga
|
||||||
MangaListItem(
|
MangaListItem(
|
||||||
modifier = Modifier.selectedBackground(isSelected),
|
modifier = Modifier.selectedBackground(isSelected),
|
||||||
title = manga.title,
|
title = manga.title,
|
||||||
cover = MangaCover(
|
cover = MangaCover(
|
||||||
manga.id!!,
|
manga.id,
|
||||||
manga.source,
|
manga.source,
|
||||||
manga.favorite,
|
manga.favorite,
|
||||||
manga.thumbnail_url,
|
manga.thumbnailUrl,
|
||||||
manga.cover_last_modified,
|
manga.coverLastModified,
|
||||||
),
|
),
|
||||||
onClick = { onClick(manga) },
|
onClick = { onClick(libraryManga) },
|
||||||
onLongClick = { onLongClick(manga) },
|
onLongClick = { onLongClick(libraryManga) },
|
||||||
) {
|
) {
|
||||||
if (item.downloadCount > 0) {
|
if (item.downloadCount > 0) {
|
||||||
Badge(
|
Badge(
|
||||||
|
@ -13,7 +13,7 @@ import com.google.accompanist.pager.HorizontalPager
|
|||||||
import com.google.accompanist.pager.PagerState
|
import com.google.accompanist.pager.PagerState
|
||||||
import eu.kanade.core.prefs.PreferenceMutableState
|
import eu.kanade.core.prefs.PreferenceMutableState
|
||||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.data.database.models
|
|
||||||
|
|
||||||
class LibraryManga : MangaImpl() {
|
|
||||||
|
|
||||||
var unreadCount: Int = 0
|
|
||||||
var readCount: Int = 0
|
|
||||||
|
|
||||||
val totalChapters
|
|
||||||
get() = readCount + unreadCount
|
|
||||||
|
|
||||||
val hasStarted
|
|
||||||
get() = readCount > 0
|
|
||||||
|
|
||||||
var category: Int = 0
|
|
||||||
|
|
||||||
// SY -->
|
|
||||||
var read: Int = 0
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is LibraryManga) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
if (unreadCount != other.unreadCount) return false
|
|
||||||
if (readCount != other.readCount) return false
|
|
||||||
if (category != other.category) return false
|
|
||||||
if (read != other.read) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + unreadCount
|
|
||||||
result = 31 * result + readCount
|
|
||||||
result = 31 * result + category
|
|
||||||
result = 31 * result + read
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,6 @@ import eu.kanade.tachiyomi.data.download.Downloader
|
|||||||
import eu.kanade.tachiyomi.data.notification.NotificationHandler
|
import eu.kanade.tachiyomi.data.notification.NotificationHandler
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.util.lang.chop
|
import eu.kanade.tachiyomi.util.lang.chop
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
@ -71,7 +70,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
* @param current the current progress.
|
* @param current the current progress.
|
||||||
* @param total the total progress.
|
* @param total the total progress.
|
||||||
*/
|
*/
|
||||||
fun showProgressNotification(manga: List</* SY --> */SManga/* SY <-- */>, current: Int, total: Int) {
|
fun showProgressNotification(manga: List<Manga>, current: Int, total: Int) {
|
||||||
if (preferences.hideNotificationContent().get()) {
|
if (preferences.hideNotificationContent().get()) {
|
||||||
progressNotificationBuilder
|
progressNotificationBuilder
|
||||||
.setContentTitle(context.getString(R.string.notification_check_updates))
|
.setContentTitle(context.getString(R.string.notification_check_updates))
|
||||||
|
@ -17,6 +17,7 @@ import eu.kanade.domain.chapter.model.toDbChapter
|
|||||||
import eu.kanade.domain.download.service.DownloadPreferences
|
import eu.kanade.domain.download.service.DownloadPreferences
|
||||||
import eu.kanade.domain.library.model.GroupLibraryMode
|
import eu.kanade.domain.library.model.GroupLibraryMode
|
||||||
import eu.kanade.domain.library.model.LibraryGroup
|
import eu.kanade.domain.library.model.LibraryGroup
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
import eu.kanade.domain.manga.interactor.GetFavorites
|
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
@ -25,6 +26,7 @@ import eu.kanade.domain.manga.interactor.GetMergedMangaForDownloading
|
|||||||
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
|
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
|
||||||
import eu.kanade.domain.manga.interactor.InsertManga
|
import eu.kanade.domain.manga.interactor.InsertManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
@ -34,10 +36,7 @@ import eu.kanade.domain.track.model.toDomainTrack
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
|
||||||
@ -94,7 +93,6 @@ import java.util.concurrent.CopyOnWriteArrayList
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import eu.kanade.domain.chapter.model.Chapter as DomainChapter
|
import eu.kanade.domain.chapter.model.Chapter as DomainChapter
|
||||||
import eu.kanade.domain.manga.model.Manga as DomainManga
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class will take care of updating the chapters of the manga from the library. It can be
|
* This class will take care of updating the chapters of the manga from the library. It can be
|
||||||
@ -308,28 +306,28 @@ class LibraryUpdateService(
|
|||||||
*
|
*
|
||||||
* @param categoryId the ID of the category to update, or -1 if no category specified.
|
* @param categoryId the ID of the category to update, or -1 if no category specified.
|
||||||
*/
|
*/
|
||||||
fun addMangaToQueue(categoryId: Long, group: Int, groupExtra: String?) {
|
private fun addMangaToQueue(categoryId: Long, group: Int, groupExtra: String?) {
|
||||||
val libraryManga = runBlocking { getLibraryManga.await() }
|
val libraryManga = runBlocking { getLibraryManga.await() }
|
||||||
// SY -->
|
// SY -->
|
||||||
val groupLibraryUpdateType = libraryPreferences.groupLibraryUpdateType().get()
|
val groupLibraryUpdateType = libraryPreferences.groupLibraryUpdateType().get()
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
val listToUpdate = if (categoryId != -1L) {
|
val listToUpdate = if (categoryId != -1L) {
|
||||||
libraryManga.filter { it.category.toLong() == categoryId }
|
libraryManga.filter { it.category == categoryId }
|
||||||
// SY -->
|
// SY -->
|
||||||
} else if (
|
} else if (
|
||||||
group == LibraryGroup.BY_DEFAULT ||
|
group == LibraryGroup.BY_DEFAULT ||
|
||||||
groupLibraryUpdateType == GroupLibraryMode.GLOBAL ||
|
groupLibraryUpdateType == GroupLibraryMode.GLOBAL ||
|
||||||
(groupLibraryUpdateType == GroupLibraryMode.ALL_BUT_UNGROUPED && group == LibraryGroup.UNGROUPED)
|
(groupLibraryUpdateType == GroupLibraryMode.ALL_BUT_UNGROUPED && group == LibraryGroup.UNGROUPED)
|
||||||
) {
|
) {
|
||||||
val categoriesToUpdate = libraryPreferences.libraryUpdateCategories().get().map(String::toInt)
|
val categoriesToUpdate = libraryPreferences.libraryUpdateCategories().get().map(String::toLong)
|
||||||
val listToInclude = if (categoriesToUpdate.isNotEmpty()) {
|
val listToInclude = if (categoriesToUpdate.isNotEmpty()) {
|
||||||
libraryManga.filter { it.category in categoriesToUpdate }
|
libraryManga.filter { it.category in categoriesToUpdate }
|
||||||
} else {
|
} else {
|
||||||
libraryManga
|
libraryManga
|
||||||
}
|
}
|
||||||
|
|
||||||
val categoriesToExclude = libraryPreferences.libraryUpdateCategoriesExclude().get().map(String::toInt)
|
val categoriesToExclude = libraryPreferences.libraryUpdateCategoriesExclude().get().map { it.toLong() }
|
||||||
val listToExclude = if (categoriesToExclude.isNotEmpty()) {
|
val listToExclude = if (categoriesToExclude.isNotEmpty()) {
|
||||||
libraryManga.filter { it.category in categoriesToExclude }.toSet()
|
libraryManga.filter { it.category in categoriesToExclude }.toSet()
|
||||||
} else {
|
} else {
|
||||||
@ -343,7 +341,7 @@ class LibraryUpdateService(
|
|||||||
val trackingExtra = groupExtra?.toIntOrNull() ?: -1
|
val trackingExtra = groupExtra?.toIntOrNull() ?: -1
|
||||||
val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
|
val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
|
||||||
|
|
||||||
libraryManga.filter { manga ->
|
libraryManga.filter { (manga) ->
|
||||||
val status = tracks[manga.id]?.firstNotNullOfOrNull { track ->
|
val status = tracks[manga.id]?.firstNotNullOfOrNull { track ->
|
||||||
TrackStatus.parseTrackerStatus(track.syncId, track.status)
|
TrackStatus.parseTrackerStatus(track.syncId, track.status)
|
||||||
} ?: TrackStatus.OTHER
|
} ?: TrackStatus.OTHER
|
||||||
@ -352,17 +350,17 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
LibraryGroup.BY_SOURCE -> {
|
LibraryGroup.BY_SOURCE -> {
|
||||||
val sourceExtra = groupExtra?.nullIfBlank()?.toIntOrNull()
|
val sourceExtra = groupExtra?.nullIfBlank()?.toIntOrNull()
|
||||||
val source = libraryManga.map { it.source }
|
val source = libraryManga.map { it.manga.source }
|
||||||
.distinct()
|
.distinct()
|
||||||
.sorted()
|
.sorted()
|
||||||
.getOrNull(sourceExtra ?: -1)
|
.getOrNull(sourceExtra ?: -1)
|
||||||
|
|
||||||
if (source != null) libraryManga.filter { it.source == source } else emptyList()
|
if (source != null) libraryManga.filter { it.manga.source == source } else emptyList()
|
||||||
}
|
}
|
||||||
LibraryGroup.BY_STATUS -> {
|
LibraryGroup.BY_STATUS -> {
|
||||||
val statusExtra = groupExtra?.toIntOrNull() ?: -1
|
val statusExtra = groupExtra?.toLongOrNull() ?: -1
|
||||||
libraryManga.filter {
|
libraryManga.filter {
|
||||||
it.status == statusExtra
|
it.manga.status == statusExtra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibraryGroup.UNGROUPED -> libraryManga
|
LibraryGroup.UNGROUPED -> libraryManga
|
||||||
@ -372,12 +370,12 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
mangaToUpdate = listToUpdate
|
mangaToUpdate = listToUpdate
|
||||||
.distinctBy { it.id }
|
.distinctBy { it.manga.id }
|
||||||
.sortedBy { it.title }
|
.sortedBy { it.manga.title }
|
||||||
|
|
||||||
// Warn when excessively checking a single source
|
// Warn when excessively checking a single source
|
||||||
val maxUpdatesFromSource = mangaToUpdate
|
val maxUpdatesFromSource = mangaToUpdate
|
||||||
.groupBy { it.source }
|
.groupBy { it.manga.source }
|
||||||
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
||||||
.maxOfOrNull { it.value.size } ?: 0
|
.maxOfOrNull { it.value.size } ?: 0
|
||||||
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||||
@ -396,8 +394,8 @@ class LibraryUpdateService(
|
|||||||
private suspend fun updateChapterList() {
|
private suspend fun updateChapterList() {
|
||||||
val semaphore = Semaphore(5)
|
val semaphore = Semaphore(5)
|
||||||
val progressCount = AtomicInteger(0)
|
val progressCount = AtomicInteger(0)
|
||||||
val currentlyUpdatingManga = CopyOnWriteArrayList<LibraryManga>()
|
val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
|
||||||
val newUpdates = CopyOnWriteArrayList<Pair<DomainManga, Array<DomainChapter>>>()
|
val newUpdates = CopyOnWriteArrayList<Pair<Manga, Array<DomainChapter>>>()
|
||||||
val skippedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
|
val skippedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
|
||||||
val failedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
|
val failedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
|
||||||
val hasDownloads = AtomicBoolean(false)
|
val hasDownloads = AtomicBoolean(false)
|
||||||
@ -406,61 +404,59 @@ class LibraryUpdateService(
|
|||||||
val restrictions = libraryPreferences.libraryUpdateMangaRestriction().get()
|
val restrictions = libraryPreferences.libraryUpdateMangaRestriction().get()
|
||||||
|
|
||||||
withIOContext {
|
withIOContext {
|
||||||
mangaToUpdate.groupBy { it.source }
|
mangaToUpdate.groupBy { it.manga.source }
|
||||||
.filterNot { it.key in LIBRARY_UPDATE_EXCLUDED_SOURCES }
|
.filterNot { it.key in LIBRARY_UPDATE_EXCLUDED_SOURCES }
|
||||||
.values
|
.values
|
||||||
.map { mangaInSource ->
|
.map { mangaInSource ->
|
||||||
async {
|
async {
|
||||||
semaphore.withPermit {
|
semaphore.withPermit {
|
||||||
mangaInSource.forEach { manga ->
|
mangaInSource.forEach { libraryManga ->
|
||||||
|
val manga = libraryManga.manga
|
||||||
if (updateJob?.isActive != true) {
|
if (updateJob?.isActive != true) {
|
||||||
return@async
|
return@async
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't continue to update if manga not in library
|
// Don't continue to update if manga is not in library
|
||||||
manga.id?.let { getManga.await(it) } ?: return@forEach
|
manga.id.let { getManga.await(it) } ?: return@forEach
|
||||||
|
|
||||||
withUpdateNotification(
|
withUpdateNotification(
|
||||||
currentlyUpdatingManga,
|
currentlyUpdatingManga,
|
||||||
progressCount,
|
progressCount,
|
||||||
manga,
|
manga,
|
||||||
) { mangaWithNotif ->
|
) {
|
||||||
try {
|
try {
|
||||||
when {
|
when {
|
||||||
MANGA_NON_COMPLETED in restrictions && mangaWithNotif.status == SManga.COMPLETED ->
|
MANGA_NON_COMPLETED in restrictions && manga.status.toInt() == SManga.COMPLETED ->
|
||||||
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_completed))
|
skippedUpdates.add(manga to getString(R.string.skipped_reason_completed))
|
||||||
|
|
||||||
MANGA_HAS_UNREAD in restrictions && mangaWithNotif.unreadCount != 0 ->
|
MANGA_HAS_UNREAD in restrictions && libraryManga.unreadCount != 0L ->
|
||||||
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_caught_up))
|
skippedUpdates.add(manga to getString(R.string.skipped_reason_not_caught_up))
|
||||||
|
|
||||||
MANGA_NON_READ in restrictions && mangaWithNotif.totalChapters > 0 && !mangaWithNotif.hasStarted ->
|
MANGA_NON_READ in restrictions && libraryManga.totalChapters > 0L && !libraryManga.hasStarted ->
|
||||||
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_started))
|
skippedUpdates.add(manga to getString(R.string.skipped_reason_not_started))
|
||||||
|
|
||||||
mangaWithNotif.update_strategy != UpdateStrategy.ALWAYS_UPDATE ->
|
manga.updateStrategy != UpdateStrategy.ALWAYS_UPDATE ->
|
||||||
skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_always_update))
|
skippedUpdates.add(manga to getString(R.string.skipped_reason_not_always_update))
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
// Convert to the manga that contains new chapters
|
val newChapters = updateManga(manga, loggedServices)
|
||||||
mangaWithNotif.toDomainManga()?.let { domainManga ->
|
val newDbChapters = newChapters.map { it.toDbChapter() }
|
||||||
val newChapters = updateManga(domainManga, loggedServices)
|
|
||||||
val newDbChapters = newChapters.map { it.toDbChapter() }
|
|
||||||
|
|
||||||
if (newChapters.isNotEmpty()) {
|
if (newChapters.isNotEmpty()) {
|
||||||
val categoryIds = getCategories.await(domainManga.id).map { it.id }
|
val categoryIds = getCategories.await(manga.id).map { it.id }
|
||||||
if (domainManga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) {
|
if (manga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) {
|
||||||
downloadChapters(mangaWithNotif, newDbChapters)
|
downloadChapters(manga, newDbChapters)
|
||||||
hasDownloads.set(true)
|
hasDownloads.set(true)
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to the manga that contains new chapters
|
|
||||||
newUpdates.add(
|
|
||||||
mangaWithNotif.toDomainManga()!! to
|
|
||||||
newDbChapters
|
|
||||||
.map { it.toDomainChapter()!! }
|
|
||||||
.sortedByDescending { it.sourceOrder }
|
|
||||||
.toTypedArray(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert to the manga that contains new chapters
|
||||||
|
newUpdates.add(
|
||||||
|
manga to
|
||||||
|
newDbChapters
|
||||||
|
.map { it.toDomainChapter()!! }
|
||||||
|
.sortedByDescending { it.sourceOrder }
|
||||||
|
.toTypedArray(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,11 +467,11 @@ class LibraryUpdateService(
|
|||||||
is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error)
|
is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error)
|
||||||
else -> e.message
|
else -> e.message
|
||||||
}
|
}
|
||||||
failedUpdates.add(mangaWithNotif to errorMessage)
|
failedUpdates.add(manga to errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libraryPreferences.autoUpdateTrackers().get()) {
|
if (libraryPreferences.autoUpdateTrackers().get()) {
|
||||||
updateTrackings(mangaWithNotif, loggedServices)
|
updateTrackings(manga, loggedServices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,7 +509,7 @@ class LibraryUpdateService(
|
|||||||
// may don't like it and they could ban the user.
|
// may don't like it and they could ban the user.
|
||||||
// SY -->
|
// SY -->
|
||||||
if (manga.source == MERGED_SOURCE_ID) {
|
if (manga.source == MERGED_SOURCE_ID) {
|
||||||
val downloadingManga = runBlocking { getMergedMangaForDownloading.await(manga.id!!) }
|
val downloadingManga = runBlocking { getMergedMangaForDownloading.await(manga.id) }
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
chapters.groupBy { it.manga_id }
|
chapters.groupBy { it.manga_id }
|
||||||
.forEach {
|
.forEach {
|
||||||
@ -527,7 +523,7 @@ class LibraryUpdateService(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
downloadManager.downloadChapters(manga.toDomainManga()!!, chapters, false)
|
downloadManager.downloadChapters(manga, chapters, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -536,7 +532,7 @@ class LibraryUpdateService(
|
|||||||
* @param manga the manga to update.
|
* @param manga the manga to update.
|
||||||
* @return a pair of the inserted and removed chapters.
|
* @return a pair of the inserted and removed chapters.
|
||||||
*/
|
*/
|
||||||
private suspend fun updateManga(manga: DomainManga, loggedServices: List<TrackService>): List<DomainChapter> {
|
private suspend fun updateManga(manga: Manga, loggedServices: List<TrackService>): List<DomainChapter> {
|
||||||
val source = sourceManager.getOrStub(manga.source)
|
val source = sourceManager.getOrStub(manga.source)
|
||||||
|
|
||||||
// Update manga metadata if needed
|
// Update manga metadata if needed
|
||||||
@ -578,15 +574,16 @@ class LibraryUpdateService(
|
|||||||
private suspend fun updateCovers() {
|
private suspend fun updateCovers() {
|
||||||
val semaphore = Semaphore(5)
|
val semaphore = Semaphore(5)
|
||||||
val progressCount = AtomicInteger(0)
|
val progressCount = AtomicInteger(0)
|
||||||
val currentlyUpdatingManga = CopyOnWriteArrayList<LibraryManga>()
|
val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
|
||||||
|
|
||||||
withIOContext {
|
withIOContext {
|
||||||
mangaToUpdate.groupBy { it.source }
|
mangaToUpdate.groupBy { it.manga.source }
|
||||||
.values
|
.values
|
||||||
.map { mangaInSource ->
|
.map { mangaInSource ->
|
||||||
async {
|
async {
|
||||||
semaphore.withPermit {
|
semaphore.withPermit {
|
||||||
mangaInSource.forEach { manga ->
|
mangaInSource.forEach { libraryManga ->
|
||||||
|
val manga = libraryManga.manga
|
||||||
if (updateJob?.isActive != true) {
|
if (updateJob?.isActive != true) {
|
||||||
return@async
|
return@async
|
||||||
}
|
}
|
||||||
@ -595,14 +592,14 @@ class LibraryUpdateService(
|
|||||||
currentlyUpdatingManga,
|
currentlyUpdatingManga,
|
||||||
progressCount,
|
progressCount,
|
||||||
manga,
|
manga,
|
||||||
) { mangaWithNotif ->
|
) {
|
||||||
val source = sourceManager.get(mangaWithNotif.source) ?: return@withUpdateNotification
|
val source = sourceManager.get(manga.source) ?: return@withUpdateNotification
|
||||||
try {
|
try {
|
||||||
val networkManga = source.getMangaDetails(mangaWithNotif.copy())
|
val networkManga = source.getMangaDetails(manga.toSManga())
|
||||||
mangaWithNotif.prepUpdateCover(coverCache, networkManga, true)
|
val updatedManga = manga.prepUpdateCover(coverCache, networkManga, true)
|
||||||
mangaWithNotif.copyFrom(networkManga)
|
.copyFrom(networkManga)
|
||||||
try {
|
try {
|
||||||
updateManga.await(mangaWithNotif.toDomainManga()!!.toMangaUpdate())
|
updateManga.await(updatedManga.toMangaUpdate())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" }
|
logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" }
|
||||||
}
|
}
|
||||||
@ -629,7 +626,8 @@ class LibraryUpdateService(
|
|||||||
var progressCount = 0
|
var progressCount = 0
|
||||||
val loggedServices = trackManager.services.filter { it.isLogged }
|
val loggedServices = trackManager.services.filter { it.isLogged }
|
||||||
|
|
||||||
mangaToUpdate.forEach { manga ->
|
mangaToUpdate.forEach { libraryManga ->
|
||||||
|
val manga = libraryManga.manga
|
||||||
if (updateJob?.isActive != true) {
|
if (updateJob?.isActive != true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -643,8 +641,8 @@ class LibraryUpdateService(
|
|||||||
notifier.cancelProgressNotification()
|
notifier.cancelProgressNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateTrackings(manga: LibraryManga, loggedServices: List<TrackService>) {
|
private suspend fun updateTrackings(manga: Manga, loggedServices: List<TrackService>) {
|
||||||
getTracks.await(manga.id!!)
|
getTracks.await(manga.id)
|
||||||
.map { track ->
|
.map { track ->
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
async {
|
async {
|
||||||
@ -655,7 +653,7 @@ class LibraryUpdateService(
|
|||||||
insertTrack.await(updatedTrack.toDomainTrack()!!)
|
insertTrack.await(updatedTrack.toDomainTrack()!!)
|
||||||
|
|
||||||
if (service is EnhancedTrackService) {
|
if (service is EnhancedTrackService) {
|
||||||
val chapters = getChapterByMangaId.await(manga.id!!)
|
val chapters = getChapterByMangaId.await(manga.id)
|
||||||
syncChaptersWithTrackServiceTwoWay.await(chapters, track, service)
|
syncChaptersWithTrackServiceTwoWay.await(chapters, track, service)
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
@ -670,10 +668,10 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun withUpdateNotification(
|
private suspend fun withUpdateNotification(
|
||||||
updatingManga: CopyOnWriteArrayList<LibraryManga>,
|
updatingManga: CopyOnWriteArrayList<Manga>,
|
||||||
completed: AtomicInteger,
|
completed: AtomicInteger,
|
||||||
manga: LibraryManga,
|
manga: Manga,
|
||||||
block: suspend (LibraryManga) -> Unit,
|
block: suspend () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (updateJob?.isActive != true) {
|
if (updateJob?.isActive != true) {
|
||||||
return
|
return
|
||||||
@ -686,7 +684,7 @@ class LibraryUpdateService(
|
|||||||
mangaToUpdate.size,
|
mangaToUpdate.size,
|
||||||
)
|
)
|
||||||
|
|
||||||
block(manga)
|
block()
|
||||||
|
|
||||||
if (updateJob?.isActive != true) {
|
if (updateJob?.isActive != true) {
|
||||||
return
|
return
|
||||||
@ -723,21 +721,20 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
count++
|
count++
|
||||||
notifier.showProgressNotification(listOf(networkManga), count, size)
|
notifier.showProgressNotification(listOf(Manga.create().copy(ogTitle = networkManga.title)), count, size)
|
||||||
|
|
||||||
var dbManga = getManga.await(networkManga.url, mangaDex.id)
|
var dbManga = getManga.await(networkManga.url, mangaDex.id)
|
||||||
|
|
||||||
if (dbManga == null) {
|
if (dbManga == null) {
|
||||||
val newManga = Manga.create(
|
val newManga = Manga.create().copy(
|
||||||
networkManga.url,
|
url = networkManga.url,
|
||||||
networkManga.title,
|
ogTitle = networkManga.title,
|
||||||
mangaDex.id,
|
source = mangaDex.id,
|
||||||
|
favorite = true,
|
||||||
|
dateAdded = System.currentTimeMillis(),
|
||||||
)
|
)
|
||||||
newManga.favorite = true
|
|
||||||
newManga.date_added = System.currentTimeMillis()
|
|
||||||
newManga.id = -1
|
|
||||||
val result = runBlocking {
|
val result = runBlocking {
|
||||||
val id = insertManga.await(newManga.toDomainManga()!!)
|
val id = insertManga.await(newManga)
|
||||||
getManga.await(id!!)
|
getManga.await(id!!)
|
||||||
}
|
}
|
||||||
dbManga = result ?: return
|
dbManga = result ?: return
|
||||||
@ -768,7 +765,7 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
count++
|
count++
|
||||||
notifier.showProgressNotification(listOf(manga.toDbManga()), count, listManga.size)
|
notifier.showProgressNotification(listOf(manga), count, listManga.size)
|
||||||
|
|
||||||
// Get this manga's trackers from the database
|
// Get this manga's trackers from the database
|
||||||
val dbTracks = getTracks.await(manga.id)
|
val dbTracks = getTracks.await(manga.id)
|
||||||
|
@ -21,7 +21,6 @@ import eu.kanade.presentation.components.ChangeCategoryDialog
|
|||||||
import eu.kanade.presentation.components.DeleteLibraryMangaDialog
|
import eu.kanade.presentation.components.DeleteLibraryMangaDialog
|
||||||
import eu.kanade.presentation.library.LibraryScreen
|
import eu.kanade.presentation.library.LibraryScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
||||||
@ -115,8 +114,8 @@ class LibraryController(
|
|||||||
onClickCleanTitles = ::cleanTitles,
|
onClickCleanTitles = ::cleanTitles,
|
||||||
onClickMigrate = {
|
onClickMigrate = {
|
||||||
val selectedMangaIds = presenter.selection
|
val selectedMangaIds = presenter.selection
|
||||||
.filterNot { it.source == MERGED_SOURCE_ID }
|
.filterNot { it.manga.source == MERGED_SOURCE_ID }
|
||||||
.mapNotNull { it.id }
|
.map { it.manga.id }
|
||||||
presenter.clearSelection()
|
presenter.clearSelection()
|
||||||
if (selectedMangaIds.isNotEmpty()) {
|
if (selectedMangaIds.isNotEmpty()) {
|
||||||
PreMigrationController.navigateToMigration(
|
PreMigrationController.navigateToMigration(
|
||||||
@ -130,7 +129,7 @@ class LibraryController(
|
|||||||
},
|
},
|
||||||
onClickAddToMangaDex = ::pushToMdList,
|
onClickAddToMangaDex = ::pushToMdList,
|
||||||
onOpenReader = {
|
onOpenReader = {
|
||||||
startReading(it.toDomainManga()!!)
|
startReading(it.manga)
|
||||||
},
|
},
|
||||||
onClickSyncExh = {
|
onClickSyncExh = {
|
||||||
// TODO
|
// TODO
|
||||||
@ -296,7 +295,7 @@ class LibraryController(
|
|||||||
private fun showMangaCategoriesDialog() {
|
private fun showMangaCategoriesDialog() {
|
||||||
viewScope.launchIO {
|
viewScope.launchIO {
|
||||||
// Create a copy of selected manga
|
// Create a copy of selected manga
|
||||||
val mangaList = presenter.selection.mapNotNull { it.toDomainManga() }.toList()
|
val mangaList = presenter.selection.map { it.manga }
|
||||||
|
|
||||||
// Hide the default category because it has a different behavior than the ones from db.
|
// Hide the default category because it has a different behavior than the ones from db.
|
||||||
val categories = presenter.ogCategories.filter { it.id != 0L } // SY <--
|
val categories = presenter.ogCategories.filter { it.id != 0L } // SY <--
|
||||||
@ -318,28 +317,28 @@ class LibraryController(
|
|||||||
|
|
||||||
private fun downloadUnreadChapters() {
|
private fun downloadUnreadChapters() {
|
||||||
val mangaList = presenter.selection.toList()
|
val mangaList = presenter.selection.toList()
|
||||||
presenter.downloadUnreadChapters(mangaList.mapNotNull { it.toDomainManga() })
|
presenter.downloadUnreadChapters(mangaList.map { it.manga })
|
||||||
presenter.clearSelection()
|
presenter.clearSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun markReadStatus(read: Boolean) {
|
private fun markReadStatus(read: Boolean) {
|
||||||
val mangaList = presenter.selection.toList()
|
val mangaList = presenter.selection.toList()
|
||||||
presenter.markReadStatus(mangaList.mapNotNull { it.toDomainManga() }, read)
|
presenter.markReadStatus(mangaList.map { it.manga }, read)
|
||||||
presenter.clearSelection()
|
presenter.clearSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showDeleteMangaDialog() {
|
private fun showDeleteMangaDialog() {
|
||||||
val mangaList = presenter.selection.mapNotNull { it.toDomainManga() }.toList()
|
val mangaList = presenter.selection.map { it.manga }
|
||||||
presenter.dialog = LibraryPresenter.Dialog.DeleteManga(mangaList)
|
presenter.dialog = LibraryPresenter.Dialog.DeleteManga(mangaList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private fun cleanTitles() {
|
private fun cleanTitles() {
|
||||||
val mangas = presenter.selection.filter {
|
val mangas = presenter.selection.filter {
|
||||||
it.isEhBasedManga() ||
|
it.manga.isEhBasedManga() ||
|
||||||
it.source in nHentaiSourceIds ||
|
it.manga.source in nHentaiSourceIds ||
|
||||||
it.source == PERV_EDEN_EN_SOURCE_ID ||
|
it.manga.source == PERV_EDEN_EN_SOURCE_ID ||
|
||||||
it.source == PERV_EDEN_IT_SOURCE_ID
|
it.manga.source == PERV_EDEN_IT_SOURCE_ID
|
||||||
}
|
}
|
||||||
presenter.cleanTitles(mangas)
|
presenter.cleanTitles(mangas)
|
||||||
presenter.clearSelection()
|
presenter.clearSelection()
|
||||||
@ -347,7 +346,7 @@ class LibraryController(
|
|||||||
|
|
||||||
private fun pushToMdList() {
|
private fun pushToMdList() {
|
||||||
val mangas = presenter.selection.filter {
|
val mangas = presenter.selection.filter {
|
||||||
it.source in mangaDexSourceIds
|
it.manga.source in mangaDexSourceIds
|
||||||
}
|
}
|
||||||
presenter.syncMangaToDex(mangas)
|
presenter.syncMangaToDex(mangas)
|
||||||
presenter.clearSelection()
|
presenter.clearSelection()
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
package eu.kanade.tachiyomi.ui.library
|
package eu.kanade.tachiyomi.ui.library
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class LibraryItem(
|
class LibraryItem(
|
||||||
val manga: LibraryManga,
|
val libraryManga: LibraryManga,
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var displayMode: Long = -1
|
var displayMode: Long = -1
|
||||||
var downloadCount = -1
|
var downloadCount: Long = -1
|
||||||
var unreadCount = -1
|
var unreadCount: Long = -1
|
||||||
var isLocal = false
|
var isLocal = false
|
||||||
var sourceLanguage = ""
|
var sourceLanguage = ""
|
||||||
|
|
||||||
@ -27,12 +28,12 @@ class LibraryItem(
|
|||||||
* @return true if the manga should be included, false otherwise.
|
* @return true if the manga should be included, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun filter(constraint: String): Boolean {
|
fun filter(constraint: String): Boolean {
|
||||||
val sourceName by lazy { sourceManager.getOrStub(manga.source).name }
|
val sourceName by lazy { sourceManager.getOrStub(libraryManga.manga.source).getNameForMangaInfo(null) }
|
||||||
val genres by lazy { manga.getGenres() }
|
val genres by lazy { libraryManga.manga.genre }
|
||||||
return manga.title.contains(constraint, true) ||
|
return libraryManga.manga.title.contains(constraint, true) ||
|
||||||
(manga.author?.contains(constraint, true) ?: false) ||
|
(libraryManga.manga.author?.contains(constraint, true) ?: false) ||
|
||||||
(manga.artist?.contains(constraint, true) ?: false) ||
|
(libraryManga.manga.artist?.contains(constraint, true) ?: false) ||
|
||||||
(manga.description?.contains(constraint, true) ?: false) ||
|
(libraryManga.manga.description?.contains(constraint, true) ?: false) ||
|
||||||
if (constraint.contains(",")) {
|
if (constraint.contains(",")) {
|
||||||
constraint.split(",").all { containsSourceOrGenre(it.trim(), sourceName, genres) }
|
constraint.split(",").all { containsSourceOrGenre(it.trim(), sourceName, genres) }
|
||||||
} else {
|
} else {
|
||||||
@ -76,7 +77,7 @@ class LibraryItem(
|
|||||||
|
|
||||||
other as LibraryItem
|
other as LibraryItem
|
||||||
|
|
||||||
if (manga != other.manga) return false
|
if (libraryManga != other.libraryManga) return false
|
||||||
if (sourceManager != other.sourceManager) return false
|
if (sourceManager != other.sourceManager) return false
|
||||||
if (displayMode != other.displayMode) return false
|
if (displayMode != other.displayMode) return false
|
||||||
if (downloadCount != other.downloadCount) return false
|
if (downloadCount != other.downloadCount) return false
|
||||||
@ -88,11 +89,11 @@ class LibraryItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = manga.hashCode()
|
var result = libraryManga.hashCode()
|
||||||
result = 31 * result + sourceManager.hashCode()
|
result = 31 * result + sourceManager.hashCode()
|
||||||
result = 31 * result + displayMode.hashCode()
|
result = 31 * result + displayMode.hashCode()
|
||||||
result = 31 * result + downloadCount
|
result = 31 * result + downloadCount.toInt()
|
||||||
result = 31 * result + unreadCount
|
result = 31 * result + unreadCount.toInt()
|
||||||
result = 31 * result + isLocal.hashCode()
|
result = 31 * result + isLocal.hashCode()
|
||||||
result = 31 * result + sourceLanguage.hashCode()
|
result = 31 * result + sourceLanguage.hashCode()
|
||||||
return result
|
return result
|
||||||
|
@ -30,6 +30,7 @@ import eu.kanade.domain.chapter.model.Chapter
|
|||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||||
import eu.kanade.domain.library.model.LibraryGroup
|
import eu.kanade.domain.library.model.LibraryGroup
|
||||||
|
import eu.kanade.domain.library.model.LibraryManga
|
||||||
import eu.kanade.domain.library.model.LibrarySort
|
import eu.kanade.domain.library.model.LibrarySort
|
||||||
import eu.kanade.domain.library.model.display
|
import eu.kanade.domain.library.model.display
|
||||||
import eu.kanade.domain.library.model.sort
|
import eu.kanade.domain.library.model.sort
|
||||||
@ -52,7 +53,6 @@ import eu.kanade.presentation.library.LibraryStateImpl
|
|||||||
import eu.kanade.presentation.library.components.LibraryToolbarTitle
|
import eu.kanade.presentation.library.components.LibraryToolbarTitle
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||||
@ -286,9 +286,9 @@ class LibraryPresenter(
|
|||||||
val filterFnDownloaded: (LibraryItem) -> Boolean = downloaded@{ item ->
|
val filterFnDownloaded: (LibraryItem) -> Boolean = downloaded@{ item ->
|
||||||
if (!downloadedOnly && filterDownloaded == State.IGNORE.value) return@downloaded true
|
if (!downloadedOnly && filterDownloaded == State.IGNORE.value) return@downloaded true
|
||||||
val isDownloaded = when {
|
val isDownloaded = when {
|
||||||
item.manga.toDomainManga()!!.isLocal() -> true
|
item.libraryManga.manga.isLocal() -> true
|
||||||
item.downloadCount != -1 -> item.downloadCount > 0
|
item.downloadCount != -1L -> item.downloadCount > 0
|
||||||
else -> downloadManager.getDownloadCount(item.manga.toDomainManga()!!) > 0
|
else -> downloadManager.getDownloadCount(item.libraryManga.manga) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return@downloaded if (downloadedOnly || filterDownloaded == State.INCLUDE.value) {
|
return@downloaded if (downloadedOnly || filterDownloaded == State.INCLUDE.value) {
|
||||||
@ -300,7 +300,7 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item ->
|
val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item ->
|
||||||
if (filterUnread == State.IGNORE.value) return@unread true
|
if (filterUnread == State.IGNORE.value) return@unread true
|
||||||
val isUnread = item.manga.unreadCount != 0
|
val isUnread = item.libraryManga.unreadCount != 0L
|
||||||
|
|
||||||
return@unread if (filterUnread == State.INCLUDE.value) {
|
return@unread if (filterUnread == State.INCLUDE.value) {
|
||||||
isUnread
|
isUnread
|
||||||
@ -311,7 +311,7 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
val filterFnStarted: (LibraryItem) -> Boolean = started@{ item ->
|
val filterFnStarted: (LibraryItem) -> Boolean = started@{ item ->
|
||||||
if (filterStarted == State.IGNORE.value) return@started true
|
if (filterStarted == State.IGNORE.value) return@started true
|
||||||
val hasStarted = item.manga.hasStarted
|
val hasStarted = item.libraryManga.hasStarted
|
||||||
|
|
||||||
return@started if (filterStarted == State.INCLUDE.value) {
|
return@started if (filterStarted == State.INCLUDE.value) {
|
||||||
hasStarted
|
hasStarted
|
||||||
@ -322,7 +322,7 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item ->
|
val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item ->
|
||||||
if (filterCompleted == State.IGNORE.value) return@completed true
|
if (filterCompleted == State.IGNORE.value) return@completed true
|
||||||
val isCompleted = item.manga.status == SManga.COMPLETED
|
val isCompleted = item.libraryManga.manga.status.toInt() == SManga.COMPLETED
|
||||||
|
|
||||||
return@completed if (filterCompleted == State.INCLUDE.value) {
|
return@completed if (filterCompleted == State.INCLUDE.value) {
|
||||||
isCompleted
|
isCompleted
|
||||||
@ -334,7 +334,7 @@ class LibraryPresenter(
|
|||||||
val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item ->
|
val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item ->
|
||||||
if (isNotAnyLoggedIn) return@tracking true
|
if (isNotAnyLoggedIn) return@tracking true
|
||||||
|
|
||||||
val trackedManga = trackMap[item.manga.id ?: -1]
|
val trackedManga = trackMap[item.libraryManga.manga.id]
|
||||||
|
|
||||||
val containsExclude = loggedInServices.filterValues { it == State.EXCLUDE.value }
|
val containsExclude = loggedInServices.filterValues { it == State.EXCLUDE.value }
|
||||||
val containsInclude = loggedInServices.filterValues { it == State.INCLUDE.value }
|
val containsInclude = loggedInServices.filterValues { it == State.INCLUDE.value }
|
||||||
@ -358,7 +358,7 @@ class LibraryPresenter(
|
|||||||
// SY -->
|
// SY -->
|
||||||
val filterFnLewd: (LibraryItem) -> Boolean = lewd@{ item ->
|
val filterFnLewd: (LibraryItem) -> Boolean = lewd@{ item ->
|
||||||
if (filterLewd == State.IGNORE.value) return@lewd true
|
if (filterLewd == State.IGNORE.value) return@lewd true
|
||||||
val isLewd = item.manga.isLewd()
|
val isLewd = item.libraryManga.manga.isLewd()
|
||||||
|
|
||||||
return@lewd if (filterLewd == State.INCLUDE.value) {
|
return@lewd if (filterLewd == State.INCLUDE.value) {
|
||||||
isLewd
|
isLewd
|
||||||
@ -416,14 +416,12 @@ class LibraryPresenter(
|
|||||||
for (item in itemList) {
|
for (item in itemList) {
|
||||||
item.downloadCount = if (showDownloadBadges) {
|
item.downloadCount = if (showDownloadBadges) {
|
||||||
// SY -->
|
// SY -->
|
||||||
if (item.manga.source == MERGED_SOURCE_ID) {
|
if (item.libraryManga.manga.source == MERGED_SOURCE_ID) {
|
||||||
item.manga.id?.let { mergeMangaId ->
|
runBlocking {
|
||||||
runBlocking {
|
getMergedMangaById.await(item.libraryManga.manga.id)
|
||||||
getMergedMangaById.await(mergeMangaId)
|
}.sumOf { downloadManager.getDownloadCount(it) }.toLong()
|
||||||
}.sumOf { downloadManager.getDownloadCount(it) }
|
|
||||||
} ?: 0
|
|
||||||
} else {
|
} else {
|
||||||
/* SY <-- */ downloadManager.getDownloadCount(item.manga.toDomainManga()!!)
|
/* SY <-- */ downloadManager.getDownloadCount(item.libraryManga.manga).toLong()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Unset download count if not enabled
|
// Unset download count if not enabled
|
||||||
@ -431,21 +429,21 @@ class LibraryPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.unreadCount = if (showUnreadBadges) {
|
item.unreadCount = if (showUnreadBadges) {
|
||||||
item.manga.unreadCount
|
item.libraryManga.unreadCount
|
||||||
} else {
|
} else {
|
||||||
// Unset unread count if not enabled
|
// Unset unread count if not enabled
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
|
|
||||||
item.isLocal = if (showLocalBadges) {
|
item.isLocal = if (showLocalBadges) {
|
||||||
item.manga.toDomainManga()!!.isLocal()
|
item.libraryManga.manga.isLocal()
|
||||||
} else {
|
} else {
|
||||||
// Hide / Unset local badge if not enabled
|
// Hide / Unset local badge if not enabled
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
item.sourceLanguage = if (showLanguageBadges) {
|
item.sourceLanguage = if (showLanguageBadges) {
|
||||||
sourceManager.getOrStub(item.manga.source).lang.uppercase()
|
sourceManager.getOrStub(item.libraryManga.manga.source).lang.uppercase()
|
||||||
} else {
|
} else {
|
||||||
// Unset source language if not enabled
|
// Unset source language if not enabled
|
||||||
""
|
""
|
||||||
@ -519,51 +517,51 @@ class LibraryPresenter(
|
|||||||
val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
|
val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
|
||||||
// SY -->
|
// SY -->
|
||||||
val sort = when (groupType) {
|
val sort = when (groupType) {
|
||||||
LibraryGroup.BY_DEFAULT -> sortModes[i1.manga.category.toLong()]!!
|
LibraryGroup.BY_DEFAULT -> sortModes[i1.libraryManga.category]!!
|
||||||
else -> groupSort
|
else -> groupSort
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
when (sort.type) {
|
when (sort.type) {
|
||||||
LibrarySort.Type.Alphabetical -> {
|
LibrarySort.Type.Alphabetical -> {
|
||||||
collator.compare(i1.manga.title.lowercase(locale), i2.manga.title.lowercase(locale))
|
collator.compare(i1.libraryManga.manga.title.lowercase(locale), i2.libraryManga.manga.title.lowercase(locale))
|
||||||
}
|
}
|
||||||
LibrarySort.Type.LastRead -> {
|
LibrarySort.Type.LastRead -> {
|
||||||
val manga1LastRead = lastReadManga[i1.manga.id!!] ?: 0
|
val manga1LastRead = lastReadManga[i1.libraryManga.manga.id] ?: 0
|
||||||
val manga2LastRead = lastReadManga[i2.manga.id!!] ?: 0
|
val manga2LastRead = lastReadManga[i2.libraryManga.manga.id] ?: 0
|
||||||
manga1LastRead.compareTo(manga2LastRead)
|
manga1LastRead.compareTo(manga2LastRead)
|
||||||
}
|
}
|
||||||
LibrarySort.Type.LastUpdate -> {
|
LibrarySort.Type.LastUpdate -> {
|
||||||
i1.manga.last_update.compareTo(i2.manga.last_update)
|
i1.libraryManga.manga.lastUpdate.compareTo(i2.libraryManga.manga.lastUpdate)
|
||||||
}
|
}
|
||||||
LibrarySort.Type.UnreadCount -> when {
|
LibrarySort.Type.UnreadCount -> when {
|
||||||
// Ensure unread content comes first
|
// Ensure unread content comes first
|
||||||
i1.manga.unreadCount == i2.manga.unreadCount -> 0
|
i1.libraryManga.unreadCount == i2.libraryManga.unreadCount -> 0
|
||||||
i1.manga.unreadCount == 0 -> if (sort.isAscending) 1 else -1
|
i1.libraryManga.unreadCount == 0L -> if (sort.isAscending) 1 else -1
|
||||||
i2.manga.unreadCount == 0 -> if (sort.isAscending) -1 else 1
|
i2.libraryManga.unreadCount == 0L -> if (sort.isAscending) -1 else 1
|
||||||
else -> i1.manga.unreadCount.compareTo(i2.manga.unreadCount)
|
else -> i1.libraryManga.unreadCount.compareTo(i2.libraryManga.unreadCount)
|
||||||
}
|
}
|
||||||
LibrarySort.Type.TotalChapters -> {
|
LibrarySort.Type.TotalChapters -> {
|
||||||
i1.manga.totalChapters.compareTo(i2.manga.totalChapters)
|
i1.libraryManga.totalChapters.compareTo(i2.libraryManga.totalChapters)
|
||||||
}
|
}
|
||||||
LibrarySort.Type.LatestChapter -> {
|
LibrarySort.Type.LatestChapter -> {
|
||||||
val manga1latestChapter = latestChapterManga[i1.manga.id!!]
|
val manga1latestChapter = latestChapterManga[i1.libraryManga.manga.id]
|
||||||
?: latestChapterManga.size
|
?: latestChapterManga.size
|
||||||
val manga2latestChapter = latestChapterManga[i2.manga.id!!]
|
val manga2latestChapter = latestChapterManga[i2.libraryManga.manga.id]
|
||||||
?: latestChapterManga.size
|
?: latestChapterManga.size
|
||||||
manga1latestChapter.compareTo(manga2latestChapter)
|
manga1latestChapter.compareTo(manga2latestChapter)
|
||||||
}
|
}
|
||||||
LibrarySort.Type.ChapterFetchDate -> {
|
LibrarySort.Type.ChapterFetchDate -> {
|
||||||
val manga1chapterFetchDate = chapterFetchDateManga[i1.manga.id!!] ?: 0
|
val manga1chapterFetchDate = chapterFetchDateManga[i1.libraryManga.manga.id] ?: 0
|
||||||
val manga2chapterFetchDate = chapterFetchDateManga[i2.manga.id!!] ?: 0
|
val manga2chapterFetchDate = chapterFetchDateManga[i2.libraryManga.manga.id] ?: 0
|
||||||
manga1chapterFetchDate.compareTo(manga2chapterFetchDate)
|
manga1chapterFetchDate.compareTo(manga2chapterFetchDate)
|
||||||
}
|
}
|
||||||
LibrarySort.Type.DateAdded -> {
|
LibrarySort.Type.DateAdded -> {
|
||||||
i1.manga.date_added.compareTo(i2.manga.date_added)
|
i1.libraryManga.manga.dateAdded.compareTo(i1.libraryManga.manga.dateAdded)
|
||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
LibrarySort.Type.TagList -> {
|
LibrarySort.Type.TagList -> {
|
||||||
val manga1IndexOfTag = listOfTags.indexOfFirst { i1.manga.getGenres()?.contains(it) ?: false }
|
val manga1IndexOfTag = listOfTags.indexOfFirst { i1.libraryManga.manga.genre?.contains(it) ?: false }
|
||||||
val manga2IndexOfTag = listOfTags.indexOfFirst { i2.manga.getGenres()?.contains(it) ?: false }
|
val manga2IndexOfTag = listOfTags.indexOfFirst { i2.libraryManga.manga.genre?.contains(it) ?: false }
|
||||||
manga1IndexOfTag.compareTo(manga2IndexOfTag)
|
manga1IndexOfTag.compareTo(manga2IndexOfTag)
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
@ -601,7 +599,7 @@ class LibraryPresenter(
|
|||||||
list.map { libraryManga ->
|
list.map { libraryManga ->
|
||||||
// Display mode based on user preference: take it from global library setting or category
|
// Display mode based on user preference: take it from global library setting or category
|
||||||
LibraryItem(libraryManga)
|
LibraryItem(libraryManga)
|
||||||
}.groupBy { it.manga.category.toLong() }
|
}.groupBy { it.libraryManga.category.toLong() }
|
||||||
}
|
}
|
||||||
return combine(categoriesFlow, libraryMangasFlow) { dbCategories, libraryManga ->
|
return combine(categoriesFlow, libraryMangasFlow) { dbCategories, libraryManga ->
|
||||||
val categories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) {
|
val categories = if (libraryManga.isNotEmpty() && libraryManga.containsKey(0).not()) {
|
||||||
@ -626,13 +624,13 @@ class LibraryPresenter(
|
|||||||
LibraryGroup.UNGROUPED -> {
|
LibraryGroup.UNGROUPED -> {
|
||||||
editedCategories = listOf(Category(0, "All", 0, 0))
|
editedCategories = listOf(Category(0, "All", 0, 0))
|
||||||
mapOf(
|
mapOf(
|
||||||
0L to map.values.flatten().distinctBy { it.manga.id },
|
0L to map.values.flatten().distinctBy { it.libraryManga.manga.id },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val (items, customCategories) = getGroupedMangaItems(
|
val (items, customCategories) = getGroupedMangaItems(
|
||||||
groupType = groupType,
|
groupType = groupType,
|
||||||
libraryManga = map.values.flatten().distinctBy { it.manga.id },
|
libraryManga = map.values.flatten().distinctBy { it.libraryManga.manga.id },
|
||||||
)
|
)
|
||||||
editedCategories = customCategories
|
editedCategories = customCategories
|
||||||
items
|
items
|
||||||
@ -782,8 +780,8 @@ class LibraryPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun cleanTitles(mangas: List<DbManga>) {
|
fun cleanTitles(mangas: List<LibraryManga>) {
|
||||||
mangas.forEach { manga ->
|
mangas.forEach { (manga) ->
|
||||||
val editedTitle = manga.title.replace("\\[.*?]".toRegex(), "").trim().replace("\\(.*?\\)".toRegex(), "").trim().replace("\\{.*?\\}".toRegex(), "").trim().let {
|
val editedTitle = manga.title.replace("\\[.*?]".toRegex(), "").trim().replace("\\(.*?\\)".toRegex(), "").trim().replace("\\{.*?\\}".toRegex(), "").trim().let {
|
||||||
if (it.contains("|")) {
|
if (it.contains("|")) {
|
||||||
it.replace(".*\\|".toRegex(), "").trim()
|
it.replace(".*\\|".toRegex(), "").trim()
|
||||||
@ -795,22 +793,22 @@ class LibraryPresenter(
|
|||||||
val mangaJson = CustomMangaManager.MangaJson(
|
val mangaJson = CustomMangaManager.MangaJson(
|
||||||
id = manga.id,
|
id = manga.id,
|
||||||
title = editedTitle.nullIfBlank(),
|
title = editedTitle.nullIfBlank(),
|
||||||
author = manga.author.takeUnless { it == manga.originalAuthor },
|
author = manga.author.takeUnless { it == manga.ogAuthor },
|
||||||
artist = manga.artist.takeUnless { it == manga.originalArtist },
|
artist = manga.artist.takeUnless { it == manga.ogArtist },
|
||||||
description = manga.description.takeUnless { it == manga.originalDescription },
|
description = manga.description.takeUnless { it == manga.ogDescription },
|
||||||
genre = manga.getGenres().takeUnless { it == manga.getOriginalGenres() },
|
genre = manga.genre.takeUnless { it == manga.ogGenre },
|
||||||
status = manga.status.takeUnless { it == manga.originalStatus }?.toLong(),
|
status = manga.status.takeUnless { it == manga.ogStatus }?.toLong(),
|
||||||
)
|
)
|
||||||
|
|
||||||
customMangaManager.saveMangaInfo(mangaJson)
|
customMangaManager.saveMangaInfo(mangaJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun syncMangaToDex(mangaList: List<DbManga>) {
|
fun syncMangaToDex(mangaList: List<LibraryManga>) {
|
||||||
launchIO {
|
launchIO {
|
||||||
MdUtil.getEnabledMangaDex(unsortedPreferences, sourcePreferences, sourceManager)?.let { mdex ->
|
MdUtil.getEnabledMangaDex(unsortedPreferences, sourcePreferences, sourceManager)?.let { mdex ->
|
||||||
mangaList.forEach {
|
mangaList.forEach { (manga) ->
|
||||||
mdex.updateFollowStatus(MdUtil.getMangaId(it.url), FollowStatus.READING)
|
mdex.updateFollowStatus(MdUtil.getMangaId(manga.url), FollowStatus.READING)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -927,7 +925,7 @@ class LibraryPresenter(
|
|||||||
val count = when {
|
val count = when {
|
||||||
category == null || mangaCountVisibility.not() -> null
|
category == null || mangaCountVisibility.not() -> null
|
||||||
tabVisibility.not() -> loadedManga[category.id]?.size
|
tabVisibility.not() -> loadedManga[category.id]?.size
|
||||||
else -> loadedManga.values.flatten().distinctBy { it.manga.id }.size
|
else -> loadedManga.values.flatten().distinctBy { it.libraryManga.manga.id }.size
|
||||||
}
|
}
|
||||||
|
|
||||||
value = when (category) {
|
value = when (category) {
|
||||||
@ -983,23 +981,23 @@ class LibraryPresenter(
|
|||||||
val parsedQuery = searchEngine.parseQuery(query)
|
val parsedQuery = searchEngine.parseQuery(query)
|
||||||
val mangaWithMetaIds = getIdsOfFavoriteMangaWithMetadata.await()
|
val mangaWithMetaIds = getIdsOfFavoriteMangaWithMetadata.await()
|
||||||
val tracks = if (loggedServices.isNotEmpty()) {
|
val tracks = if (loggedServices.isNotEmpty()) {
|
||||||
getTracks.await(unfiltered.mapNotNull { it.manga.id }.distinct())
|
getTracks.await(unfiltered.map { it.libraryManga.manga.id }.distinct())
|
||||||
} else {
|
} else {
|
||||||
emptyMap()
|
emptyMap()
|
||||||
}
|
}
|
||||||
val sources = unfiltered
|
val sources = unfiltered
|
||||||
.distinctBy { it.manga.source }
|
.distinctBy { it.libraryManga.manga.source }
|
||||||
.mapNotNull { sourceManager.get(it.manga.source) }
|
.mapNotNull { sourceManager.get(it.libraryManga.manga.source) }
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
unfiltered.asFlow().cancellable().filter { item ->
|
unfiltered.asFlow().cancellable().filter { item ->
|
||||||
val mangaId = item.manga.id ?: -1
|
val mangaId = item.libraryManga.manga.id
|
||||||
val sourceId = item.manga.source
|
val sourceId = item.libraryManga.manga.source
|
||||||
if (isMetadataSource(sourceId)) {
|
if (isMetadataSource(sourceId)) {
|
||||||
if (mangaWithMetaIds.binarySearch(mangaId) < 0) {
|
if (mangaWithMetaIds.binarySearch(mangaId) < 0) {
|
||||||
// No meta? Filter using title
|
// No meta? Filter using title
|
||||||
filterManga(
|
filterManga(
|
||||||
queries = parsedQuery,
|
queries = parsedQuery,
|
||||||
manga = item.manga,
|
libraryManga = item.libraryManga,
|
||||||
tracks = tracks[mangaId],
|
tracks = tracks[mangaId],
|
||||||
source = sources[sourceId],
|
source = sources[sourceId],
|
||||||
)
|
)
|
||||||
@ -1008,7 +1006,7 @@ class LibraryPresenter(
|
|||||||
val titles = getSearchTitles.await(mangaId)
|
val titles = getSearchTitles.await(mangaId)
|
||||||
filterManga(
|
filterManga(
|
||||||
queries = parsedQuery,
|
queries = parsedQuery,
|
||||||
manga = item.manga,
|
libraryManga = item.libraryManga,
|
||||||
tracks = tracks[mangaId],
|
tracks = tracks[mangaId],
|
||||||
source = sources[sourceId],
|
source = sources[sourceId],
|
||||||
checkGenre = false,
|
checkGenre = false,
|
||||||
@ -1019,7 +1017,7 @@ class LibraryPresenter(
|
|||||||
} else {
|
} else {
|
||||||
filterManga(
|
filterManga(
|
||||||
queries = parsedQuery,
|
queries = parsedQuery,
|
||||||
manga = item.manga,
|
libraryManga = item.libraryManga,
|
||||||
tracks = tracks[mangaId],
|
tracks = tracks[mangaId],
|
||||||
source = sources[sourceId],
|
source = sources[sourceId],
|
||||||
)
|
)
|
||||||
@ -1036,15 +1034,16 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
private fun filterManga(
|
private fun filterManga(
|
||||||
queries: List<QueryComponent>,
|
queries: List<QueryComponent>,
|
||||||
manga: LibraryManga,
|
libraryManga: LibraryManga,
|
||||||
tracks: List<Track>?,
|
tracks: List<Track>?,
|
||||||
source: Source?,
|
source: Source?,
|
||||||
checkGenre: Boolean = true,
|
checkGenre: Boolean = true,
|
||||||
searchTags: List<SearchTag>? = null,
|
searchTags: List<SearchTag>? = null,
|
||||||
searchTitles: List<SearchTitle>? = null,
|
searchTitles: List<SearchTitle>? = null,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
val manga = libraryManga.manga
|
||||||
val sourceIdString = manga.source.takeUnless { it == LocalSource.ID }?.toString()
|
val sourceIdString = manga.source.takeUnless { it == LocalSource.ID }?.toString()
|
||||||
val genre = if (checkGenre) manga.getGenres().orEmpty() else emptyList()
|
val genre = if (checkGenre) manga.genre.orEmpty() else emptyList()
|
||||||
return queries.all { queryComponent ->
|
return queries.all { queryComponent ->
|
||||||
when (queryComponent.excluded) {
|
when (queryComponent.excluded) {
|
||||||
false -> when (queryComponent) {
|
false -> when (queryComponent) {
|
||||||
@ -1142,7 +1141,7 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
fun toggleSelection(manga: LibraryManga) {
|
fun toggleSelection(manga: LibraryManga) {
|
||||||
val mutableList = state.selection.toMutableList()
|
val mutableList = state.selection.toMutableList()
|
||||||
if (selection.fastAny { it.id == manga.id }) {
|
if (selection.fastAny { it.manga.id == manga.manga.id }) {
|
||||||
mutableList.remove(manga)
|
mutableList.remove(manga)
|
||||||
} else {
|
} else {
|
||||||
mutableList.add(manga)
|
mutableList.add(manga)
|
||||||
@ -1154,13 +1153,13 @@ class LibraryPresenter(
|
|||||||
val category = categories[index]
|
val category = categories[index]
|
||||||
val items = loadedManga[category.id] ?: emptyList()
|
val items = loadedManga[category.id] ?: emptyList()
|
||||||
state.selection = state.selection.toMutableList().apply {
|
state.selection = state.selection.toMutableList().apply {
|
||||||
addAll(items.filterNot { it.manga in selection }.map { it.manga })
|
addAll(items.filterNot { it.libraryManga in selection }.map { it.libraryManga })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invertSelection(index: Int) {
|
fun invertSelection(index: Int) {
|
||||||
val category = categories[index]
|
val category = categories[index]
|
||||||
val items = (loadedManga[category.id] ?: emptyList()).map { it.manga }
|
val items = (loadedManga[category.id] ?: emptyList()).map { it.libraryManga }
|
||||||
state.selection = items.filterNot { it in selection }
|
state.selection = items.filterNot { it in selection }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1189,8 +1188,8 @@ class LibraryPresenter(
|
|||||||
val manga = when (groupType) {
|
val manga = when (groupType) {
|
||||||
LibraryGroup.BY_TRACK_STATUS -> {
|
LibraryGroup.BY_TRACK_STATUS -> {
|
||||||
val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
|
val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
|
||||||
libraryManga.groupBy { libraryItem ->
|
libraryManga.groupBy { item ->
|
||||||
val status = tracks[libraryItem.manga.id]?.firstNotNullOfOrNull { track ->
|
val status = tracks[item.libraryManga.manga.id]?.firstNotNullOfOrNull { track ->
|
||||||
TrackStatus.parseTrackerStatus(track.syncId, track.status)
|
TrackStatus.parseTrackerStatus(track.syncId, track.status)
|
||||||
} ?: TrackStatus.OTHER
|
} ?: TrackStatus.OTHER
|
||||||
|
|
||||||
@ -1198,14 +1197,14 @@ class LibraryPresenter(
|
|||||||
}.mapKeys { it.key.toLong() }
|
}.mapKeys { it.key.toLong() }
|
||||||
}
|
}
|
||||||
LibraryGroup.BY_SOURCE -> {
|
LibraryGroup.BY_SOURCE -> {
|
||||||
libraryManga.groupBy { libraryItem ->
|
libraryManga.groupBy { item ->
|
||||||
libraryItem.manga.source
|
item.libraryManga.manga.source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
libraryManga.groupBy { libraryItem ->
|
libraryManga.groupBy { item ->
|
||||||
libraryItem.manga.status
|
item.libraryManga.manga.status
|
||||||
}.mapKeys { it.key.toLong() }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,26 +20,26 @@ import eu.kanade.domain.manga.model.Manga as DomainManga
|
|||||||
/**
|
/**
|
||||||
* Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache
|
* Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache
|
||||||
*/
|
*/
|
||||||
fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean) {
|
fun DomainManga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean): DomainManga {
|
||||||
// Never refresh covers if the new url is null, as the current url has possibly become invalid
|
// Never refresh covers if the new url is null, as the current url has possibly become invalid
|
||||||
val newUrl = remoteManga.thumbnail_url ?: return
|
val newUrl = remoteManga.thumbnail_url ?: return this
|
||||||
|
|
||||||
// Never refresh covers if the url is empty to avoid "losing" existing covers
|
// Never refresh covers if the url is empty to avoid "losing" existing covers
|
||||||
if (newUrl.isEmpty()) return
|
if (newUrl.isEmpty()) return this
|
||||||
|
|
||||||
if (!refreshSameUrl && thumbnail_url == newUrl) return
|
if (!refreshSameUrl && thumbnailUrl == newUrl) return this
|
||||||
|
|
||||||
val domainManga = toDomainManga()!!
|
return when {
|
||||||
when {
|
isLocal() -> {
|
||||||
domainManga.isLocal() -> {
|
this.copy(coverLastModified = Date().time)
|
||||||
cover_last_modified = Date().time
|
|
||||||
}
|
}
|
||||||
domainManga.hasCustomCover(coverCache) -> {
|
hasCustomCover(coverCache) -> {
|
||||||
coverCache.deleteFromCache(this, false)
|
coverCache.deleteFromCache(this, false)
|
||||||
|
this
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
cover_last_modified = Date().time
|
|
||||||
coverCache.deleteFromCache(this, false)
|
coverCache.deleteFromCache(this, false)
|
||||||
|
this.copy(coverLastModified = Date().time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import eu.kanade.domain.manga.interactor.GetManga
|
|||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
@ -98,17 +97,17 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
||||||
val libraryManga = getLibraryManga.await()
|
val libraryManga = getLibraryManga.await()
|
||||||
val seenManga = HashSet<Long>(libraryManga.size)
|
val seenManga = HashSet<Long>(libraryManga.size)
|
||||||
libraryManga.forEach {
|
libraryManga.forEach { (manga) ->
|
||||||
if (!it.isEhBasedManga()) return@forEach
|
if (!manga.isEhBasedManga()) return@forEach
|
||||||
|
|
||||||
if (it.id in seenManga) {
|
if (manga.id in seenManga) {
|
||||||
val inCategories = getCategories.await(it.id!!)
|
val inCategories = getCategories.await(manga.id)
|
||||||
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(it.toDomainManga()!!, inCategories, context)
|
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(manga, inCategories, context)
|
||||||
|
|
||||||
logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id))
|
logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, manga.id))
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
seenManga += it.id!!
|
seenManga += manga.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package exh.util
|
package exh.util
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import exh.source.isEhBasedManga
|
import exh.source.isEhBasedManga
|
||||||
import exh.source.nHentaiSourceIds
|
import exh.source.nHentaiSourceIds
|
||||||
@ -9,15 +9,14 @@ import uy.kohesive.injekt.api.get
|
|||||||
|
|
||||||
fun Manga.isLewd(): Boolean {
|
fun Manga.isLewd(): Boolean {
|
||||||
val sourceName = Injekt.get<SourceManager>().get(source)?.name
|
val sourceName = Injekt.get<SourceManager>().get(source)?.name
|
||||||
val currentTags = getGenres().orEmpty()
|
|
||||||
|
|
||||||
if (isEhBasedManga() || source in nHentaiSourceIds) {
|
if (isEhBasedManga() || source in nHentaiSourceIds) {
|
||||||
return currentTags.none { tag -> isNonHentaiTag(tag) }
|
return genre.orEmpty().none { tag -> isNonHentaiTag(tag) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return source in 6905L..6913L ||
|
return source in 6905L..6913L ||
|
||||||
(sourceName != null && isHentaiSource(sourceName)) ||
|
(sourceName != null && isHentaiSource(sourceName)) ||
|
||||||
currentTags.any { tag -> isHentaiTag(tag) }
|
genre.orEmpty().any { tag -> isHentaiTag(tag) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isNonHentaiTag(tag: String): Boolean {
|
private fun isNonHentaiTag(tag: String): Boolean {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user