Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co

### Other
- Merged from Aniyomi and Mihon ([@Secozzi](https://github.com/Secozzi)) ([#102](https://github.com/quickdesh/Animiru/pull/102))
- Add support for extension lib 16 ([@Secozzi](https://github.com/Secozzi)) ([#104](https://github.com/quickdesh/Animiru/pull/104))

## [v0.17.2.0] - 2024-07-27
### Fixes
Expand Down
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ Surround the new code with:
- **AM (REMOVE_TABBED_SCREENS)** --> Refactoring from Aniyomi code to Animiru!
- **AM (REMOVE_ACRA_FIREBASE)** --> Refactoring from Aniyomi code to Animiru!
- **AM (REMOVE_LIBRARIES)** --> Refactoring from Aniyomi code to Animiru!
- **AM (FILLERMARK)** --> Thank you Quickdesh!
- **AM (BROWSE)** --> Thank you Quickdesh!
- **AM (KEYBOARD_CONTROLS)** --> Thank you Quickdesh!
- **AM (NAVIGATION_PILL)** --> Thank you Quickdesh!
Expand Down
18 changes: 15 additions & 3 deletions app/src/main/java/eu/kanade/domain/DomainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Application
import eu.kanade.domain.anime.interactor.GetExcludedScanlators
import eu.kanade.domain.anime.interactor.SetAnimeViewerFlags
import eu.kanade.domain.anime.interactor.SetExcludedScanlators
import eu.kanade.domain.anime.interactor.SyncSeasonsWithSource
import eu.kanade.domain.anime.interactor.UpdateAnime
import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.episode.interactor.GetAvailableScanlators
Expand Down Expand Up @@ -53,14 +54,15 @@ import tachiyomi.data.updates.UpdatesRepositoryImpl
import tachiyomi.domain.anime.interactor.FetchInterval
import tachiyomi.domain.anime.interactor.GetAnime
import tachiyomi.domain.anime.interactor.GetAnimeByUrlAndSourceId
import tachiyomi.domain.anime.interactor.GetAnimeWithEpisodes
import tachiyomi.domain.anime.interactor.GetAnimeWithEpisodesAndSeasons
import tachiyomi.domain.anime.interactor.GetCustomAnimeInfo
import tachiyomi.domain.anime.interactor.GetDuplicateLibraryAnime
import tachiyomi.domain.anime.interactor.GetFavorites
import tachiyomi.domain.anime.interactor.GetLibraryAnime
import tachiyomi.domain.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.anime.interactor.ResetViewerFlags
import tachiyomi.domain.anime.interactor.SetAnimeEpisodeFlags
import tachiyomi.domain.anime.interactor.SetAnimeSeasonFlags
import tachiyomi.domain.anime.interactor.SetCustomAnimeInfo
import tachiyomi.domain.anime.interactor.UpdateAnimeNotes
import tachiyomi.domain.anime.repository.AnimeRepository
Expand Down Expand Up @@ -99,6 +101,9 @@ import tachiyomi.domain.history.interactor.UpsertHistory
import tachiyomi.domain.history.repository.HistoryRepository
import tachiyomi.domain.release.interactor.GetApplicationRelease
import tachiyomi.domain.release.service.ReleaseService
import tachiyomi.domain.season.interactor.GetAnimeSeasonsByParentId
import tachiyomi.domain.season.interactor.SetAnimeDefaultSeasonFlags
import tachiyomi.domain.season.interactor.ShouldUpdateDbSeason
import tachiyomi.domain.source.interactor.GetRemoteAnime
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryAnime
import tachiyomi.domain.source.repository.SourceRepository
Expand Down Expand Up @@ -138,7 +143,6 @@ class DomainModule : InjektModule {
addFactory { GetDuplicateLibraryAnime(get()) }
addFactory { GetFavorites(get()) }
addFactory { GetLibraryAnime(get()) }
addFactory { GetAnimeWithEpisodes(get(), get()) }
addFactory { GetAnimeByUrlAndSourceId(get()) }
addFactory { GetAnime(get()) }
addFactory { GetNextEpisodes(get(), get(), get()) }
Expand All @@ -156,9 +160,17 @@ class DomainModule : InjektModule {
addFactory { SetExcludedScanlators(get()) }
addFactory {
MigrateAnimeUseCase(
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
)
}
// AY -->
addFactory { GetAnimeWithEpisodesAndSeasons(get(), get()) }
addFactory { GetAnimeSeasonsByParentId(get()) }
addFactory { SetAnimeSeasonFlags(get()) }
addFactory { SetAnimeDefaultSeasonFlags(get(), get(), get()) }
addFactory { ShouldUpdateDbSeason() }
addFactory { SyncSeasonsWithSource(get(), get(), get(), get(), get()) }
// <-- AY

addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
addFactory { GetApplicationRelease(get(), get()) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// AY -->
package eu.kanade.domain.anime.interactor

import eu.kanade.tachiyomi.animesource.AnimeSource
import eu.kanade.tachiyomi.animesource.model.SAnime
import mihon.domain.anime.model.toDomainAnime
import tachiyomi.domain.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.anime.model.Anime
import tachiyomi.domain.anime.model.NoSeasonsException
import tachiyomi.domain.anime.model.toAnimeUpdate
import tachiyomi.domain.anime.repository.AnimeRepository
import tachiyomi.domain.season.interactor.GetAnimeSeasonsByParentId
import tachiyomi.domain.season.interactor.ShouldUpdateDbSeason
import tachiyomi.domain.season.service.SeasonRecognition
import tachiyomi.source.local.isLocal
import java.time.ZonedDateTime

class SyncSeasonsWithSource(
private val updateAnime: UpdateAnime,
private val animeRepository: AnimeRepository,
private val networkToLocalAnime: NetworkToLocalAnime,
private val shouldUpdateDbSeason: ShouldUpdateDbSeason,
private val getAnimeSeasonsByParentId: GetAnimeSeasonsByParentId,
) {
suspend fun await(
rawSourceSeasons: List<SAnime>,
anime: Anime,
source: AnimeSource,
manualFetch: Boolean = false,
fetchWindow: Pair<Long, Long> = Pair(0, 0),
): List<Anime> {
if (rawSourceSeasons.isEmpty() && !source.isLocal()) {
throw NoSeasonsException()
}

val now = ZonedDateTime.now()

val sourceSeasons = rawSourceSeasons
.distinctBy { it.url }
.mapIndexed { i, sAnime ->
networkToLocalAnime.invoke(sAnime.toDomainAnime(source.id))
.copy(parentId = anime.id, seasonSourceOrder = i.toLong())
}

val dbSeasons = getAnimeSeasonsByParentId.await(anime.id)

val newSeasons = mutableListOf<Anime>()
val updatedSeasons = mutableListOf<Anime>()
val removedSeasons = dbSeasons.filterNot { dbSeasons ->
sourceSeasons.any { sourceSeason ->
dbSeasons.anime.url == sourceSeason.url
}
}

for (sourceSeason in sourceSeasons) {
var season = sourceSeason

// Recognize season number for the season
val seasonNumber = SeasonRecognition.parseSeasonNumber(
anime.title,
season.title,
season.seasonNumber,
)
season = season.copy(seasonNumber = seasonNumber)

val dbSeason = dbSeasons.find { it.anime.url == season.url }?.anime
if (dbSeason == null) {
newSeasons.add(season)
} else {
if (shouldUpdateDbSeason.await(dbSeason, season)) {
val toChangeSeason = dbSeason.copy(
// AM (CUSTOM_INFORMATION) -->
ogTitle = season.title,
// <-- AM (CUSTOM_INFORMATION)
seasonNumber = season.seasonNumber,
seasonSourceOrder = season.seasonSourceOrder,
)
updatedSeasons.add(toChangeSeason)
}
}
}

// Return if there's nothing to add, delete, or update to avoid unnecessary db transactions.
if (newSeasons.isEmpty() && removedSeasons.isEmpty() && updatedSeasons.isEmpty()) {
if (manualFetch || anime.fetchInterval == 0 || anime.nextUpdate < fetchWindow.first) {
updateAnime.awaitUpdateFetchInterval(
anime,
now,
fetchWindow,
)
}
return sourceSeasons
}

if (removedSeasons.isNotEmpty()) {
val toDeleteIds = removedSeasons.map { it.id }
animeRepository.removeParentIdByIds(toDeleteIds)
}

val toUpdate = newSeasons.map { it.toAnimeUpdate() } +
updatedSeasons.map { it.toAnimeUpdate() }

if (toUpdate.isNotEmpty()) {
updateAnime.awaitAll(toUpdate)
}

updateAnime.awaitUpdateLastUpdate(anime.id)

return sourceSeasons
}
}
// <-- AY
39 changes: 39 additions & 0 deletions app/src/main/java/eu/kanade/domain/anime/interactor/UpdateAnime.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package eu.kanade.domain.anime.interactor

import eu.kanade.domain.anime.model.hasCustomBackground
import eu.kanade.domain.anime.model.hasCustomCover
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.BackgroundCache
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import tachiyomi.domain.anime.interactor.FetchInterval
Expand Down Expand Up @@ -33,6 +35,9 @@ class UpdateAnime(
remoteAnime: SAnime,
manualFetch: Boolean,
coverCache: CoverCache = Injekt.get(),
// AY -->
backgroundCache: BackgroundCache = Injekt.get(),
// <-- AY
libraryPreferences: LibraryPreferences = Injekt.get(),
downloadManager: DownloadManager = Injekt.get(),
): Boolean {
Expand Down Expand Up @@ -66,18 +71,46 @@ class UpdateAnime(
}
}

// AY -->
val backgroundLastModified =
when {
// Never refresh backgrounds if the url is empty to avoid "losing" existing backgrounds
remoteAnime.background_url.isNullOrEmpty() -> null
!manualFetch && localAnime.backgroundUrl == remoteAnime.background_url -> null
localAnime.isLocal() -> Instant.now().toEpochMilli()
localAnime.hasCustomBackground(backgroundCache) -> {
backgroundCache.deleteFromCache(localAnime, false)
null
}
else -> {
backgroundCache.deleteFromCache(localAnime, false)
Instant.now().toEpochMilli()
}
}
// <-- AY

val thumbnailUrl = remoteAnime.thumbnail_url?.takeIf { it.isNotEmpty() }

// AY -->
val backgroundUrl = remoteAnime.background_url?.takeIf { it.isNotEmpty() }
// <-- AY

val success = animeRepository.update(
AnimeUpdate(
id = localAnime.id,
title = title,
coverLastModified = coverLastModified,
// AY -->
backgroundLastModified = backgroundLastModified,
// <-- AY
author = remoteAnime.author,
artist = remoteAnime.artist,
description = remoteAnime.description,
genre = remoteAnime.getGenres(),
thumbnailUrl = thumbnailUrl,
// AY -->
backgroundUrl = backgroundUrl,
// <-- AY
status = remoteAnime.status.toLong(),
updateStrategy = remoteAnime.update_strategy,
initialized = true,
Expand Down Expand Up @@ -107,6 +140,12 @@ class UpdateAnime(
return animeRepository.update(AnimeUpdate(id = animeId, coverLastModified = Instant.now().toEpochMilli()))
}

// AY -->
suspend fun awaitUpdateBackgroundLastModified(animeId: Long): Boolean {
return animeRepository.update(AnimeUpdate(id = animeId, backgroundLastModified = Instant.now().toEpochMilli()))
}
// <-- AY

suspend fun awaitUpdateFavorite(animeId: Long, favorite: Boolean): Boolean {
val dateAdded = when (favorite) {
true -> Instant.now().toEpochMilli()
Expand Down
48 changes: 46 additions & 2 deletions app/src/main/java/eu/kanade/domain/anime/model/Anime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package eu.kanade.domain.anime.model

import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.BackgroundCache
import eu.kanade.tachiyomi.data.cache.CoverCache
import tachiyomi.core.common.preference.TriState
import tachiyomi.domain.anime.model.Anime
Expand All @@ -18,13 +19,35 @@ val Anime.downloadedFilter: TriState
else -> TriState.DISABLED
}
}

// AY -->
val Anime.seasonDownloadedFilter: TriState
get() {
if (Injekt.get<BasePreferences>().downloadedOnly().get()) return TriState.ENABLED_IS
return when (seasonDownloadedFilterRaw) {
Anime.SEASON_SHOW_DOWNLOADED -> TriState.ENABLED_IS
Anime.SEASON_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
else -> TriState.DISABLED
}
}

fun Anime.seasonsFiltered(): Boolean {
return seasonDownloadedFilter != TriState.DISABLED ||
seasonUnseenFilter != TriState.DISABLED ||
seasonStartedFilter != TriState.DISABLED ||
seasonCompletedFilter != TriState.DISABLED ||
seasonBookmarkedFilter != TriState.DISABLED ||
seasonFillermarkedFilter != TriState.DISABLED
}
// <-- AY

fun Anime.episodesFiltered(): Boolean {
return unseenFilter != TriState.DISABLED ||
downloadedFilter != TriState.DISABLED ||
bookmarkedFilter != TriState.DISABLED ||
// AM (FILLERMARK) -->
// AY -->
fillermarkedFilter != TriState.DISABLED
// <-- AM (FILLERMARK)
// <-- AY
}

fun Anime.toSAnime(): SAnime = SAnime.create().also {
Expand All @@ -36,6 +59,11 @@ fun Anime.toSAnime(): SAnime = SAnime.create().also {
it.genre = genre.orEmpty().joinToString()
it.status = status.toInt()
it.thumbnail_url = thumbnailUrl
// AY -->
it.background_url = backgroundUrl
it.fetch_type = fetchType
it.season_number = seasonNumber
// <-- AY
it.initialized = initialized
}

Expand All @@ -51,6 +79,9 @@ fun Anime.copyFrom(other: SAnime): Anime {
}
// <-- AM (CUSTOM_INFORMATION)
val thumbnailUrl = other.thumbnail_url ?: thumbnailUrl
// AY -->
val backgroundUrl = other.background_url ?: backgroundUrl
// <-- AY
return this.copy(
// AM (CUSTOM_INFORMATION) -->
ogAuthor = author,
Expand All @@ -62,11 +93,24 @@ fun Anime.copyFrom(other: SAnime): Anime {
// AM (CUSTOM_INFORMATION) -->
ogStatus = other.status.toLong(),
// <-- AM (CUSTOM_INFORMATION)
// AY -->
backgroundUrl = backgroundUrl,
// <-- AY
updateStrategy = other.update_strategy,
// AY -->
fetchType = other.fetch_type,
seasonNumber = other.season_number,
// <-- AY
initialized = other.initialized && initialized,
)
}

fun Anime.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists()
}

// AY -->
fun Anime.hasCustomBackground(backgroundCache: BackgroundCache = Injekt.get()): Boolean {
return backgroundCache.getCustomBackgroundFile(id).exists()
}
// <-- AY
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,26 @@ class SyncEpisodesWithSource(
name = episode.name,
episodeNumber = episode.episodeNumber,
scanlator = episode.scanlator,
// AY -->
summary = episode.summary,
// <-- AY
sourceOrder = episode.sourceOrder,
)
if (episode.dateUpload != 0L) {
toChangeEpisode = toChangeEpisode.copy(dateUpload = episode.dateUpload)
}
// AY -->
if (!toChangeEpisode.fillermark) {
toChangeEpisode = toChangeEpisode.copy(
fillermark = sourceEpisode.fillermark,
)
}
if (toChangeEpisode.previewUrl.isNullOrBlank()) {
toChangeEpisode = toChangeEpisode.copy(
previewUrl = sourceEpisode.previewUrl,
)
}
// <-- AY
updatedEpisodes.add(toChangeEpisode)
}
}
Expand Down
Loading