mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-28 09:15:12 +03:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
737c5c9889
29 changed files with 124 additions and 62 deletions
5
.github/workflows/build_pull_request.yml
vendored
5
.github/workflows/build_pull_request.yml
vendored
|
@ -28,9 +28,6 @@ jobs:
|
||||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||||
|
|
||||||
- name: Build app
|
- name: Build app
|
||||||
uses: gradle/gradle-command-action@v1
|
uses: gradle/gradle-command-action@v2
|
||||||
with:
|
with:
|
||||||
arguments: assembleStandardRelease
|
arguments: assembleStandardRelease
|
||||||
distributions-cache-enabled: true
|
|
||||||
dependencies-cache-enabled: true
|
|
||||||
configuration-cache-enabled: true
|
|
||||||
|
|
5
.github/workflows/build_push.yml
vendored
5
.github/workflows/build_push.yml
vendored
|
@ -34,12 +34,9 @@ jobs:
|
||||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||||
|
|
||||||
- name: Build app
|
- name: Build app
|
||||||
uses: gradle/gradle-command-action@v1
|
uses: gradle/gradle-command-action@v2
|
||||||
with:
|
with:
|
||||||
arguments: assembleStandardRelease
|
arguments: assembleStandardRelease
|
||||||
distributions-cache-enabled: true
|
|
||||||
dependencies-cache-enabled: true
|
|
||||||
configuration-cache-enabled: true
|
|
||||||
|
|
||||||
# Sign APK and create release for tags
|
# Sign APK and create release for tags
|
||||||
|
|
||||||
|
|
2
.github/workflows/issue_moderator.yml
vendored
2
.github/workflows/issue_moderator.yml
vendored
|
@ -9,6 +9,6 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Moderate issues
|
- name: Moderate issues
|
||||||
uses: tachiyomiorg/issue-moderator-action@v1.1
|
uses: tachiyomiorg/issue-moderator-action@v1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
@ -149,10 +149,10 @@ dependencies {
|
||||||
|
|
||||||
// AndroidX libraries
|
// AndroidX libraries
|
||||||
implementation("androidx.annotation:annotation:1.3.0")
|
implementation("androidx.annotation:annotation:1.3.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.4.0-rc01")
|
implementation("androidx.appcompat:appcompat:1.4.0")
|
||||||
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha04")
|
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha04")
|
||||||
implementation("androidx.browser:browser:1.4.0")
|
implementation("androidx.browser:browser:1.4.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.1")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.2")
|
||||||
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0-beta01")
|
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0-beta01")
|
||||||
implementation("androidx.core:core-ktx:1.7.0")
|
implementation("androidx.core:core-ktx:1.7.0")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
|
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
|
||||||
|
|
|
@ -41,23 +41,32 @@ interface AnimeSource : tachiyomi.animesource.AnimeSource {
|
||||||
*
|
*
|
||||||
* @param anime the anime to update.
|
* @param anime the anime to update.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use getAnimeDetails instead")
|
@Deprecated(
|
||||||
fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> = Observable.empty()
|
"Use the 1.x API instead",
|
||||||
|
ReplaceWith("getAnimeDetails")
|
||||||
|
)
|
||||||
|
fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with all the available episodes for an anime.
|
* Returns an observable with all the available episodes for an anime.
|
||||||
*
|
*
|
||||||
* @param anime the anime to update.
|
* @param anime the anime to update.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use getEpisodeList instead")
|
@Deprecated(
|
||||||
fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> = Observable.empty()
|
"Use the 1.x API instead",
|
||||||
|
ReplaceWith("getEpisodeList")
|
||||||
|
)
|
||||||
|
fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with a list of video for the episode of an anime.
|
* Returns an observable with a list of video for the episode of an anime.
|
||||||
*
|
*
|
||||||
* @param episode the episode to get the link for.
|
* @param episode the episode to get the link for.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use getVideoList instead")
|
@Deprecated(
|
||||||
|
"Use the 1.x API instead",
|
||||||
|
ReplaceWith("getVideoList")
|
||||||
|
)
|
||||||
fun fetchVideoList(episode: SEpisode): Observable<List<Video>> = Observable.empty()
|
fun fetchVideoList(episode: SEpisode): Observable<List<Video>> = Observable.empty()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||||
import eu.kanade.tachiyomi.animesource.model.toEpisodeInfo
|
import eu.kanade.tachiyomi.animesource.model.toEpisodeInfo
|
||||||
import eu.kanade.tachiyomi.animesource.model.toSAnime
|
import eu.kanade.tachiyomi.animesource.model.toSAnime
|
||||||
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.util.episode.EpisodeRecognition
|
import eu.kanade.tachiyomi.util.episode.EpisodeRecognition
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
|
@ -30,7 +31,7 @@ import java.io.InputStream
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource {
|
class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource, UnmeteredSource {
|
||||||
companion object {
|
companion object {
|
||||||
const val ID = 0L
|
const val ID = 0L
|
||||||
const val HELP_URL = "https://aniyomi.jmir.xyz/help/guides/local-anime/"
|
const val HELP_URL = "https://aniyomi.jmir.xyz/help/guides/local-anime/"
|
||||||
|
|
|
@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.util.episode.NoEpisodesException
|
import eu.kanade.tachiyomi.util.episode.NoEpisodesException
|
||||||
import eu.kanade.tachiyomi.util.episode.syncEpisodesWithSource
|
import eu.kanade.tachiyomi.util.episode.syncEpisodesWithSource
|
||||||
import eu.kanade.tachiyomi.util.episode.syncEpisodesWithTrackServiceTwoWay
|
import eu.kanade.tachiyomi.util.episode.syncEpisodesWithTrackServiceTwoWay
|
||||||
|
@ -267,7 +268,10 @@ class AnimelibUpdateService(
|
||||||
.sortedWith(rankingScheme[selectedScheme])
|
.sortedWith(rankingScheme[selectedScheme])
|
||||||
|
|
||||||
// Warn when excessively checking a single source
|
// Warn when excessively checking a single source
|
||||||
val maxUpdatesFromSource = animeToUpdate.groupBy { it.source }.maxOfOrNull { it.value.size } ?: 0
|
val maxUpdatesFromSource = animeToUpdate
|
||||||
|
.groupBy { it.source }
|
||||||
|
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
||||||
|
.maxOfOrNull { it.value.size } ?: 0
|
||||||
if (maxUpdatesFromSource > ANIME_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
if (maxUpdatesFromSource > ANIME_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||||
toast(R.string.notification_size_warning, Toast.LENGTH_LONG)
|
toast(R.string.notification_size_warning, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.data.database.models.Episode
|
||||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
||||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownloadQueue
|
import eu.kanade.tachiyomi.data.download.model.AnimeDownloadQueue
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||||
import eu.kanade.tachiyomi.util.lang.plusAssign
|
import eu.kanade.tachiyomi.util.lang.plusAssign
|
||||||
|
@ -297,7 +298,10 @@ class AnimeDownloader(
|
||||||
|
|
||||||
// Start downloader if needed
|
// Start downloader if needed
|
||||||
if (autoStart && wasEmpty) {
|
if (autoStart && wasEmpty) {
|
||||||
val maxDownloadsFromSource = queue.groupBy { it.source }.maxOf { it.value.size }
|
val maxDownloadsFromSource = queue
|
||||||
|
.groupBy { it.source }
|
||||||
|
.filterKeys { it !is UnmeteredSource }
|
||||||
|
.maxOf { it.value.size }
|
||||||
if (maxDownloadsFromSource > EPISODES_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
if (maxDownloadsFromSource > EPISODES_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||||
withUIContext {
|
withUIContext {
|
||||||
context.toast(R.string.download_queue_size_warning, Toast.LENGTH_LONG)
|
context.toast(R.string.download_queue_size_warning, Toast.LENGTH_LONG)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList
|
import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList
|
||||||
|
@ -266,7 +267,10 @@ class Downloader(
|
||||||
|
|
||||||
// Start downloader if needed
|
// Start downloader if needed
|
||||||
if (autoStart && wasEmpty) {
|
if (autoStart && wasEmpty) {
|
||||||
val maxDownloadsFromSource = queue.groupBy { it.source }.maxOf { it.value.size }
|
val maxDownloadsFromSource = queue
|
||||||
|
.groupBy { it.source }
|
||||||
|
.filterKeys { it !is UnmeteredSource }
|
||||||
|
.maxOf { it.value.size }
|
||||||
if (maxDownloadsFromSource > CHAPTERS_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
if (maxDownloadsFromSource > CHAPTERS_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||||
withUIContext {
|
withUIContext {
|
||||||
context.toast(R.string.download_queue_size_warning, Toast.LENGTH_LONG)
|
context.toast(R.string.download_queue_size_warning, Toast.LENGTH_LONG)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.model.toSChapter
|
import eu.kanade.tachiyomi.source.model.toSChapter
|
||||||
import eu.kanade.tachiyomi.source.model.toSManga
|
import eu.kanade.tachiyomi.source.model.toSManga
|
||||||
|
@ -267,7 +268,10 @@ class LibraryUpdateService(
|
||||||
.sortedWith(rankingScheme[selectedScheme])
|
.sortedWith(rankingScheme[selectedScheme])
|
||||||
|
|
||||||
// Warn when excessively checking a single source
|
// Warn when excessively checking a single source
|
||||||
val maxUpdatesFromSource = mangaToUpdate.groupBy { it.source }.maxOfOrNull { it.value.size } ?: 0
|
val maxUpdatesFromSource = mangaToUpdate
|
||||||
|
.groupBy { it.source }
|
||||||
|
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
||||||
|
.maxOfOrNull { it.value.size } ?: 0
|
||||||
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||||
toast(R.string.notification_size_warning, Toast.LENGTH_LONG)
|
toast(R.string.notification_size_warning, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||||
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.network.interceptor.RateLimitInterceptor
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
import eu.kanade.tachiyomi.network.jsonMime
|
import eu.kanade.tachiyomi.network.jsonMime
|
||||||
import eu.kanade.tachiyomi.network.parseAs
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
|
@ -27,13 +27,13 @@ import kotlinx.serialization.json.putJsonObject
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.concurrent.TimeUnit.MINUTES
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|
|
||||||
private val authClient = client.newBuilder()
|
private val authClient = client.newBuilder()
|
||||||
.addInterceptor(interceptor)
|
.addInterceptor(interceptor)
|
||||||
.addInterceptor(RateLimitInterceptor(85, 1, MINUTES))
|
.rateLimit(permits = 85, period = 1, unit = TimeUnit.MINUTES)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
suspend fun addLibManga(track: Track): Track {
|
suspend fun addLibManga(track: Track): Track {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
@ -15,8 +16,10 @@ import eu.kanade.tachiyomi.extension.util.AnimeExtensionInstallReceiver
|
||||||
import eu.kanade.tachiyomi.extension.util.AnimeExtensionInstaller
|
import eu.kanade.tachiyomi.extension.util.AnimeExtensionInstaller
|
||||||
import eu.kanade.tachiyomi.extension.util.AnimeExtensionLoader
|
import eu.kanade.tachiyomi.extension.util.AnimeExtensionLoader
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
|
import logcat.LogPriority
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -160,7 +163,8 @@ class AnimeExtensionManager(
|
||||||
val extensions: List<AnimeExtension.Available> = try {
|
val extensions: List<AnimeExtension.Available> = try {
|
||||||
api.findExtensions()
|
api.findExtensions()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
context.toast(e.message)
|
logcat(LogPriority.ERROR, e)
|
||||||
|
context.toast(R.string.extension_api_error)
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||||
|
@ -15,8 +16,10 @@ import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
|
import logcat.LogPriority
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -160,7 +163,8 @@ class ExtensionManager(
|
||||||
val extensions: List<Extension.Available> = try {
|
val extensions: List<Extension.Available> = try {
|
||||||
api.findExtensions()
|
api.findExtensions()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
context.toast(e.message)
|
logcat(LogPriority.ERROR, e)
|
||||||
|
context.toast(R.string.extension_api_error)
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,19 @@ internal class AnimeExtensionGithubApi {
|
||||||
|
|
||||||
suspend fun findExtensions(): List<AnimeExtension.Available> {
|
suspend fun findExtensions(): List<AnimeExtension.Available> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
networkService.client
|
val extensions = networkService.client
|
||||||
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
||||||
.await()
|
.await()
|
||||||
.parseAs<List<AnimeExtensionJsonObject>>()
|
.parseAs<List<AnimeExtensionJsonObject>>()
|
||||||
.toExtensions()
|
.toExtensions()
|
||||||
|
|
||||||
|
// Sanity check - a small number of extensions probably means something broke
|
||||||
|
// with the repo generator
|
||||||
|
if (extensions.size < 10) {
|
||||||
|
throw Exception()
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,19 @@ internal class ExtensionGithubApi {
|
||||||
|
|
||||||
suspend fun findExtensions(): List<Extension.Available> {
|
suspend fun findExtensions(): List<Extension.Available> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
networkService.client
|
val extensions = networkService.client
|
||||||
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
||||||
.await()
|
.await()
|
||||||
.parseAs<List<ExtensionJsonObject>>()
|
.parseAs<List<ExtensionJsonObject>>()
|
||||||
.toExtensions()
|
.toExtensions()
|
||||||
|
|
||||||
|
// Sanity check - a small number of extensions probably means something broke
|
||||||
|
// with the repo generator
|
||||||
|
if (extensions.size < 100) {
|
||||||
|
throw Exception()
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ internal object AnimeExtensionLoader {
|
||||||
private const val METADATA_SOURCE_FACTORY = "tachiyomi.animeextension.factory"
|
private const val METADATA_SOURCE_FACTORY = "tachiyomi.animeextension.factory"
|
||||||
private const val METADATA_NSFW = "tachiyomi.animeextension.nsfw"
|
private const val METADATA_NSFW = "tachiyomi.animeextension.nsfw"
|
||||||
const val LIB_VERSION_MIN = 12
|
const val LIB_VERSION_MIN = 12
|
||||||
const val LIB_VERSION_MAX = 12
|
const val LIB_VERSION_MAX = 13
|
||||||
|
|
||||||
private const val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
private const val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ internal object ExtensionLoader {
|
||||||
private const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory"
|
private const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory"
|
||||||
private const val METADATA_NSFW = "tachiyomi.extension.nsfw"
|
private const val METADATA_NSFW = "tachiyomi.extension.nsfw"
|
||||||
const val LIB_VERSION_MIN = 1.2
|
const val LIB_VERSION_MIN = 1.2
|
||||||
const val LIB_VERSION_MAX = 1.2
|
const val LIB_VERSION_MAX = 1.3
|
||||||
|
|
||||||
private const val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
private const val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.network.interceptor
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -17,10 +18,16 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param period {Long} The limiting duration. Defaults to 1.
|
* @param period {Long} The limiting duration. Defaults to 1.
|
||||||
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
||||||
*/
|
*/
|
||||||
class RateLimitInterceptor(
|
fun OkHttpClient.Builder.rateLimit(
|
||||||
|
permits: Int,
|
||||||
|
period: Long = 1,
|
||||||
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
|
) = addInterceptor(RateLimitInterceptor(permits, period, unit))
|
||||||
|
|
||||||
|
private class RateLimitInterceptor(
|
||||||
private val permits: Int,
|
private val permits: Int,
|
||||||
private val period: Long = 1,
|
period: Long,
|
||||||
private val unit: TimeUnit = TimeUnit.SECONDS
|
unit: TimeUnit,
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
private val requestQueue = ArrayList<Long>(permits)
|
private val requestQueue = ArrayList<Long>(permits)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.network.interceptor
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -19,11 +20,18 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param period {Long} The limiting duration. Defaults to 1.
|
* @param period {Long} The limiting duration. Defaults to 1.
|
||||||
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
||||||
*/
|
*/
|
||||||
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
|
httpUrl: HttpUrl,
|
||||||
|
permits: Int,
|
||||||
|
period: Long = 1,
|
||||||
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
|
) = addInterceptor(SpecificHostRateLimitInterceptor(httpUrl, permits, period, unit))
|
||||||
|
|
||||||
class SpecificHostRateLimitInterceptor(
|
class SpecificHostRateLimitInterceptor(
|
||||||
private val httpUrl: HttpUrl,
|
httpUrl: HttpUrl,
|
||||||
private val permits: Int,
|
private val permits: Int,
|
||||||
private val period: Long = 1,
|
period: Long,
|
||||||
private val unit: TimeUnit = TimeUnit.SECONDS
|
unit: TimeUnit,
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
private val requestQueue = ArrayList<Long>(permits)
|
private val requestQueue = ArrayList<Long>(permits)
|
||||||
|
|
|
@ -38,7 +38,8 @@ import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class LocalSource(private val context: Context) : CatalogueSource {
|
class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSource {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ID = 0L
|
const val ID = 0L
|
||||||
const val HELP_URL = "https://tachiyomi.org/help/guides/local-manga/"
|
const val HELP_URL = "https://tachiyomi.org/help/guides/local-manga/"
|
||||||
|
|
|
@ -40,23 +40,33 @@ interface Source : tachiyomi.source.Source {
|
||||||
*
|
*
|
||||||
* @param manga the manga to update.
|
* @param manga the manga to update.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use getMangaDetails instead")
|
@Deprecated(
|
||||||
fun fetchMangaDetails(manga: SManga): Observable<SManga> = Observable.empty()
|
"Use the 1.x API instead",
|
||||||
|
ReplaceWith("getMangaDetails")
|
||||||
|
)
|
||||||
|
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with all the available chapters for a manga.
|
* Returns an observable with all the available chapters for a manga.
|
||||||
*
|
*
|
||||||
* @param manga the manga to update.
|
* @param manga the manga to update.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use getChapterList instead")
|
@Deprecated(
|
||||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = Observable.empty()
|
"Use the 1.x API instead",
|
||||||
|
ReplaceWith("getChapterList")
|
||||||
|
)
|
||||||
|
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
|
// TODO: remove direct usages on this method
|
||||||
/**
|
/**
|
||||||
* Returns an observable with the list of pages a chapter has.
|
* Returns an observable with the list of pages a chapter has.
|
||||||
*
|
*
|
||||||
* @param chapter the chapter.
|
* @param chapter the chapter.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use getPageList instead")
|
@Deprecated(
|
||||||
|
"Use the 1.x API instead",
|
||||||
|
ReplaceWith("getPageList")
|
||||||
|
)
|
||||||
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty()
|
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package eu.kanade.tachiyomi.source
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source that explicitly doesn't require traffic considerations.
|
||||||
|
*
|
||||||
|
* This typically applies for self-hosted sources.
|
||||||
|
*/
|
||||||
|
interface UnmeteredSource
|
|
@ -222,7 +222,7 @@ open class GlobalAnimeSearchPresenter(
|
||||||
Observable.from(first)
|
Observable.from(first)
|
||||||
.filter { it.thumbnail_url == null && !it.initialized }
|
.filter { it.thumbnail_url == null && !it.initialized }
|
||||||
.map { Pair(it, source) }
|
.map { Pair(it, source) }
|
||||||
.concatMap { runAsObservable({ getAnimeDetails(it.first, it.second) }) }
|
.concatMap { runAsObservable { getAnimeDetails(it.first, it.second) } }
|
||||||
.map { Pair(source as AnimeCatalogueSource, it) }
|
.map { Pair(source as AnimeCatalogueSource, it) }
|
||||||
}
|
}
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
|
|
|
@ -222,7 +222,7 @@ open class GlobalSearchPresenter(
|
||||||
Observable.from(first)
|
Observable.from(first)
|
||||||
.filter { it.thumbnail_url == null && !it.initialized }
|
.filter { it.thumbnail_url == null && !it.initialized }
|
||||||
.map { Pair(it, source) }
|
.map { Pair(it, source) }
|
||||||
.concatMap { runAsObservable({ getMangaDetails(it.first, it.second) }) }
|
.concatMap { runAsObservable { getMangaDetails(it.first, it.second) } }
|
||||||
.map { Pair(source as CatalogueSource, it) }
|
.map { Pair(source as CatalogueSource, it) }
|
||||||
}
|
}
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
|
|
|
@ -60,8 +60,8 @@ internal fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subsc
|
||||||
invokeOnCancellation { sub.unsubscribe() }
|
invokeOnCancellation { sub.unsubscribe() }
|
||||||
|
|
||||||
fun <T> runAsObservable(
|
fun <T> runAsObservable(
|
||||||
|
backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE,
|
||||||
block: suspend () -> T,
|
block: suspend () -> T,
|
||||||
backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE
|
|
||||||
): Observable<T> {
|
): Observable<T> {
|
||||||
return Observable.create(
|
return Observable.create(
|
||||||
{ emitter ->
|
{ emitter ->
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:shape="rectangle">
|
|
||||||
<corners android:radius="@dimen/card_radius" />
|
|
||||||
<solid android:color="?attr/colorSurface" />
|
|
||||||
</shape>
|
|
|
@ -301,6 +301,7 @@
|
||||||
<string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any login credentials stored in Tachiyomi or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string>
|
<string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any login credentials stored in Tachiyomi or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string>
|
||||||
<string name="obsolete_extension_message">This extension is no longer available.</string>
|
<string name="obsolete_extension_message">This extension is no longer available.</string>
|
||||||
<string name="unofficial_extension_message">This extension is not from the official Tachiyomi extensions list.</string>
|
<string name="unofficial_extension_message">This extension is not from the official Tachiyomi extensions list.</string>
|
||||||
|
<string name="extension_api_error">Failed to get extensions list</string>
|
||||||
<string name="ext_version_info">Version: %1$s</string>
|
<string name="ext_version_info">Version: %1$s</string>
|
||||||
<string name="ext_language_info">Language: %1$s</string>
|
<string name="ext_language_info">Language: %1$s</string>
|
||||||
<string name="ext_nsfw_short">18+</string>
|
<string name="ext_nsfw_short">18+</string>
|
||||||
|
|
|
@ -102,15 +102,6 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style name="Widget.Tachiyomi.Snackbar" parent="Widget.Material3.Snackbar">
|
|
||||||
<item name="android:background">@drawable/snackbar_background</item>
|
|
||||||
<item name="actionTextColorAlpha">1</item>
|
|
||||||
</style>
|
|
||||||
<style name="Widget.Tachiyomi.Snackbar.TextView" parent="Widget.Material3.Snackbar.TextView">
|
|
||||||
<item name="android:textColor">?attr/colorOnSurface</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
<style name="Widget.Tachiyomi.Chip.Action" parent="Widget.Material3.Chip.Suggestion">
|
<style name="Widget.Tachiyomi.Chip.Action" parent="Widget.Material3.Chip.Suggestion">
|
||||||
<item name="chipBackgroundColor">?attr/chipBackgroundColor</item>
|
<item name="chipBackgroundColor">?attr/chipBackgroundColor</item>
|
||||||
<item name="android:textColor">?attr/chipTextColor</item>
|
<item name="android:textColor">?attr/chipTextColor</item>
|
||||||
|
|
|
@ -74,8 +74,6 @@
|
||||||
<item name="chipStyle">@style/Widget.Tachiyomi.Chip.Action</item>
|
<item name="chipStyle">@style/Widget.Tachiyomi.Chip.Action</item>
|
||||||
<item name="chipTextColor">?android:attr/textColorPrimary</item>
|
<item name="chipTextColor">?android:attr/textColorPrimary</item>
|
||||||
<item name="chipBackgroundColor">?android:attr/divider</item>
|
<item name="chipBackgroundColor">?android:attr/divider</item>
|
||||||
<item name="snackbarStyle">@style/Widget.Tachiyomi.Snackbar</item>
|
|
||||||
<item name="snackbarTextViewStyle">@style/Widget.Tachiyomi.Snackbar.TextView</item>
|
|
||||||
<item name="textInputStyle">@style/Widget.Material3.TextInputLayout.OutlinedBox</item>
|
<item name="textInputStyle">@style/Widget.Material3.TextInputLayout.OutlinedBox</item>
|
||||||
<item name="appBarLayoutStyle">@style/Widget.Material3.AppBarLayout</item>
|
<item name="appBarLayoutStyle">@style/Widget.Material3.AppBarLayout</item>
|
||||||
<item name="toolbarStyle">@style/Widget.Material3.Toolbar.Surface</item>
|
<item name="toolbarStyle">@style/Widget.Material3.Toolbar.Surface</item>
|
||||||
|
|
Loading…
Reference in a new issue