Skip to content
Open
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
21 changes: 21 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ android {
"libimagedecoder",
"libquickjs",
"libsqlite3x",
"libmpv",
"libavcodec",
"libavformat",
"libswscale",
"libavutil",
"libswresample",
"libavfilter",
"libass",
"libdav1d",
"libplacebo",
)
.map { "**/$it.so" }
}
Expand Down Expand Up @@ -345,6 +355,17 @@ dependencies {

testImplementation(kotlinx.coroutines.test)

// MPV player
implementation(libs.aniyomi.mpv)
implementation(libs.seeker)
implementation(libs.ffmpeg.kit)
implementation(libs.smart.exception.java)
implementation(libs.mediasession)
implementation(libs.truetypeparser)
implementation(libs.torrentserver)
implementation(libs.media.router)
implementation(libs.cast.play.services)

// SY -->
// Better logging (EH)
implementation(sylibs.xlog)
Expand Down
4 changes: 4 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
-keep class com.canopus.** { *; }
-keepclassmembers class com.canopus.** { *; }

# MPV native player
-keep class is.xyz.mpv.** { *; }
-keepclassmembers class is.xyz.mpv.** { *; }

# Injekt type resolution - FullTypeReference needs generic type info
-keep class * extends uy.kohesive.injekt.api.TypeReference { *; }
-keep class * extends uy.kohesive.injekt.api.FullTypeReference { *; }
Expand Down
26 changes: 25 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
<!-- Permission to save cover into Pictures. It isn't required from API 29 -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
android:maxSdkVersion="28"
tools:replace="android:maxSdkVersion" />
<!-- For access custom cover on Android 12 & below (API 32) -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
Expand Down Expand Up @@ -174,6 +175,29 @@
android:exported="false"
android:theme="@style/Theme.Tachiyomi" />

<activity
android:name=".ui.player.PlayerActivity"
android:exported="true"
android:launchMode="singleTask"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:theme="@style/Theme.Tachiyomi"
android:supportsPictureInPicture="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
</activity>

<service
android:name=".data.torrentServer.service.TorrentServerService"
android:exported="false"
android:foregroundServiceType="dataSync" />

<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="eu.kanade.tachiyomi.ui.player.cast.CastOptionsProvider" />

<activity
android:name=".ui.security.UnlockActivity"
android:exported="false"
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/eu/kanade/core/util/SourceUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package eu.kanade.core.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import tachiyomi.domain.animesource.service.AnimeSourceManager
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
Expand All @@ -11,3 +12,8 @@ import uy.kohesive.injekt.api.get
fun ifSourcesLoaded(): Boolean {
return remember { Injekt.get<SourceManager>().isInitialized }.collectAsState().value
}

@Composable
fun ifAnimeSourcesLoaded(): Boolean {
return remember { Injekt.get<AnimeSourceManager>().isInitialized }.collectAsState().value
}
57 changes: 57 additions & 0 deletions app/src/main/java/eu/kanade/domain/AnimeDomainModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package eu.kanade.domain

import tachiyomi.data.anime.AnimeRepositoryImpl
import tachiyomi.data.category.AnimeCategoryRepositoryImpl
import tachiyomi.data.episode.EpisodeRepositoryImpl
import tachiyomi.domain.anime.interactor.GetAnime
import tachiyomi.domain.anime.interactor.GetDuplicateLibraryAnime
import tachiyomi.domain.anime.interactor.GetFavoriteAnime
import tachiyomi.domain.anime.interactor.GetLibraryAnime
import tachiyomi.domain.anime.interactor.SetAnimeEpisodeFlags
import tachiyomi.domain.anime.interactor.UpdateAnime
import tachiyomi.domain.anime.repository.AnimeRepository
import tachiyomi.domain.category.interactor.CreateAnimeCategory
import tachiyomi.domain.category.interactor.DeleteAnimeCategory
import tachiyomi.domain.category.interactor.GetAnimeCategories
import tachiyomi.domain.category.interactor.SetAnimeCategories
import tachiyomi.domain.category.repository.AnimeCategoryRepository
import tachiyomi.domain.episode.interactor.GetEpisode
import tachiyomi.domain.episode.interactor.GetEpisodesByAnimeId
import tachiyomi.domain.episode.interactor.SetSeenStatus
import tachiyomi.domain.episode.interactor.ShouldUpdateDbEpisode
import tachiyomi.domain.episode.interactor.UpdateEpisode
import tachiyomi.domain.episode.repository.EpisodeRepository
import tachiyomi.domain.library.service.AnimeLibraryPreferences
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get

class AnimeDomainModule : InjektModule {

override fun InjektRegistrar.registerInjectables() {
addSingletonFactory<AnimeRepository> { AnimeRepositoryImpl(get()) }
addFactory { GetAnime(get()) }
addFactory { UpdateAnime(get()) }
addFactory { GetFavoriteAnime(get()) }
addFactory { GetLibraryAnime(get()) }
addFactory { SetAnimeEpisodeFlags(get()) }
addFactory { GetDuplicateLibraryAnime(get()) }

addSingletonFactory<EpisodeRepository> { EpisodeRepositoryImpl(get()) }
addFactory { GetEpisode(get()) }
addFactory { GetEpisodesByAnimeId(get()) }
addFactory { UpdateEpisode(get()) }
addFactory { SetSeenStatus(get()) }
addFactory { ShouldUpdateDbEpisode() }

addSingletonFactory<AnimeCategoryRepository> { AnimeCategoryRepositoryImpl(get()) }
addFactory { GetAnimeCategories(get()) }
addFactory { CreateAnimeCategory(get()) }
addFactory { DeleteAnimeCategory(get()) }
addFactory { SetAnimeCategories(get()) }

addSingletonFactory { AnimeLibraryPreferences(get()) }
}
}
38 changes: 38 additions & 0 deletions app/src/main/java/eu/kanade/domain/DomainModule.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package eu.kanade.domain

import eu.kanade.domain.animeextension.interactor.GetAnimeExtensionLanguages
import eu.kanade.domain.animeextension.interactor.GetAnimeExtensionSources
import eu.kanade.domain.animeextension.interactor.GetAnimeExtensionsByType
import eu.kanade.domain.chapter.interactor.GetAvailableScanlators
import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.animedownload.interactor.DeleteAnimeDownload
import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.extension.interactor.GetExtensionSources
Expand All @@ -25,7 +29,15 @@ import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.track.interactor.RefreshTracks
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.interactor.TrackChapter
import mihon.data.repository.AnimeExtensionRepoRepositoryImpl
import mihon.data.repository.ExtensionRepoRepositoryImpl
import mihon.domain.animeextensionrepo.interactor.CreateAnimeExtensionRepo
import mihon.domain.animeextensionrepo.interactor.DeleteAnimeExtensionRepo
import mihon.domain.animeextensionrepo.interactor.GetAnimeExtensionRepo
import mihon.domain.animeextensionrepo.interactor.GetAnimeExtensionRepoCount
import mihon.domain.animeextensionrepo.interactor.ReplaceAnimeExtensionRepo
import mihon.domain.animeextensionrepo.interactor.UpdateAnimeExtensionRepo
import mihon.domain.animeextensionrepo.repository.AnimeExtensionRepoRepository
import mihon.domain.chapter.interactor.FilterChaptersForDownload
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
Expand All @@ -37,15 +49,18 @@ import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
import mihon.domain.extensionrepo.service.ExtensionRepoService
import mihon.domain.migration.usecases.MigrateMangaUseCase
import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.data.animesource.StubAnimeSourceRepositoryImpl
import tachiyomi.data.category.CategoryRepositoryImpl
import tachiyomi.data.chapter.ChapterRepositoryImpl
import tachiyomi.data.history.AnimeHistoryRepositoryImpl
import tachiyomi.data.history.HistoryRepositoryImpl
import tachiyomi.data.manga.MangaRepositoryImpl
import tachiyomi.data.release.ReleaseServiceImpl
import tachiyomi.data.source.SourceRepositoryImpl
import tachiyomi.data.source.StubSourceRepositoryImpl
import tachiyomi.data.track.TrackRepositoryImpl
import tachiyomi.data.updates.UpdatesRepositoryImpl
import tachiyomi.domain.animesource.repository.StubAnimeSourceRepository
import tachiyomi.domain.category.interactor.CreateCategoryWithName
import tachiyomi.domain.category.interactor.DeleteCategory
import tachiyomi.domain.category.interactor.GetCategories
Expand All @@ -65,11 +80,15 @@ import tachiyomi.domain.chapter.interactor.SetMangaDefaultChapterFlags
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.history.interactor.GetAnimeHistory
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.history.interactor.GetNextChapters
import tachiyomi.domain.history.interactor.GetTotalReadDuration
import tachiyomi.domain.history.interactor.RemoveAnimeHistory
import tachiyomi.domain.history.interactor.RemoveHistory
import tachiyomi.domain.history.interactor.UpsertAnimeHistory
import tachiyomi.domain.history.interactor.UpsertHistory
import tachiyomi.domain.history.repository.AnimeHistoryRepository
import tachiyomi.domain.history.repository.HistoryRepository
import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
Expand Down Expand Up @@ -175,7 +194,13 @@ class DomainModule : InjektModule {
addFactory { RemoveHistory(get()) }
addFactory { GetTotalReadDuration(get()) }

addSingletonFactory<AnimeHistoryRepository> { AnimeHistoryRepositoryImpl(get()) }
addFactory { GetAnimeHistory(get()) }
addFactory { UpsertAnimeHistory(get()) }
addFactory { RemoveAnimeHistory(get()) }

addFactory { DeleteDownload(get(), get()) }
addFactory { DeleteAnimeDownload(get(), get()) }

addFactory { GetExtensionsByType(get(), get()) }
addFactory { GetExtensionSources(get()) }
Expand Down Expand Up @@ -205,6 +230,19 @@ class DomainModule : InjektModule {
addFactory { DeleteExtensionRepo(get()) }
addFactory { ReplaceExtensionRepo(get()) }
addFactory { UpdateExtensionRepo(get(), get()) }

addSingletonFactory<AnimeExtensionRepoRepository> { AnimeExtensionRepoRepositoryImpl(get()) }
addSingletonFactory<StubAnimeSourceRepository> { StubAnimeSourceRepositoryImpl(get()) }
addFactory { GetAnimeExtensionRepo(get()) }
addFactory { GetAnimeExtensionRepoCount(get()) }
addFactory { CreateAnimeExtensionRepo(get(), get()) }
addFactory { DeleteAnimeExtensionRepo(get()) }
addFactory { ReplaceAnimeExtensionRepo(get()) }
addFactory { UpdateAnimeExtensionRepo(get(), get()) }
addFactory { GetAnimeExtensionsByType(get(), get()) }
addFactory { GetAnimeExtensionLanguages(get(), get()) }
addFactory { GetAnimeExtensionSources(get()) }

addFactory { ToggleIncognito(get()) }
addFactory { GetIncognitoState(get(), get(), get()) }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package eu.kanade.domain.animedownload.interactor

import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
import tachiyomi.core.common.util.lang.withNonCancellableContext
import tachiyomi.domain.anime.model.Anime
import tachiyomi.domain.animesource.service.AnimeSourceManager
import tachiyomi.domain.episode.model.Episode

class DeleteAnimeDownload(
private val animeSourceManager: AnimeSourceManager,
private val animeDownloadManager: AnimeDownloadManager,
) {

suspend fun awaitAll(anime: Anime, vararg episodes: Episode) = withNonCancellableContext {
animeSourceManager.get(anime.source)?.let { source ->
animeDownloadManager.deleteEpisodes(episodes.toList(), anime, source)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package eu.kanade.domain.animeextension.interactor

import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.animeextension.AnimeExtensionManager
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine

class GetAnimeExtensionLanguages(
private val preferences: SourcePreferences,
private val animeExtensionManager: AnimeExtensionManager,
) {
fun subscribe(): Flow<List<String>> {
return combine(
preferences.enabledLanguages().changes(),
animeExtensionManager.availableExtensionsFlow,
) { enabledLanguage, availableExtensions ->
availableExtensions
.flatMap { ext ->
if (ext.sources.isEmpty()) {
listOf(ext.lang)
} else {
ext.sources.map { it.lang }
}
}
.distinct()
.sortedWith(
compareBy<String> { it !in enabledLanguage }.then(LocaleHelper.comparator),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package eu.kanade.domain.animeextension.interactor

import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.animeextension.model.AnimeExtension
import eu.kanade.tachiyomi.animesource.AnimeSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class GetAnimeExtensionSources(
private val preferences: SourcePreferences,
) {

fun subscribe(extension: AnimeExtension.Installed): Flow<List<AnimeExtensionSourceItem>> {
val isMultiSource = extension.sources.size > 1
val isMultiLangSingleSource =
isMultiSource && extension.sources.map { it.name }.distinct().size == 1

return preferences.disabledSources().changes().map { disabledSources ->
fun AnimeSource.isEnabled() = id.toString() !in disabledSources

extension.sources
.map { source ->
AnimeExtensionSourceItem(
source = source,
enabled = source.isEnabled(),
labelAsName = isMultiSource && !isMultiLangSingleSource,
)
}
}
}
}

data class AnimeExtensionSourceItem(
val source: AnimeSource,
val enabled: Boolean,
val labelAsName: Boolean,
)
Loading