mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-28 17:19:00 +03:00
im going to merge after this
This commit is contained in:
parent
1c98f9181d
commit
6e7c03ee5c
86 changed files with 1406 additions and 357 deletions
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi
|
|||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
|
|
|
@ -235,8 +235,8 @@ class AnimelibUpdateNotifier(private val context: Context) {
|
|||
.centerCrop()
|
||||
.circleCrop()
|
||||
.override(
|
||||
NOTIF_ICON_SIZE,
|
||||
NOTIF_ICON_SIZE
|
||||
NOTIF_ICON_SIZE,
|
||||
NOTIF_ICON_SIZE
|
||||
)
|
||||
.submit()
|
||||
.get()
|
||||
|
|
|
@ -7,25 +7,28 @@ import android.os.IBinder
|
|||
import android.os.PowerManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateRanker.rankingScheme
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService.Companion.start
|
||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimelibAnime
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimelibAnime
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.toAnimeInfo
|
||||
import eu.kanade.tachiyomi.data.download.AnimeDownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateRanker.rankingScheme
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService.Companion.start
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.toSAnime
|
||||
import eu.kanade.tachiyomi.source.model.toSEpisode
|
||||
import eu.kanade.tachiyomi.util.episode.NoEpisodesException
|
||||
import eu.kanade.tachiyomi.util.episode.syncEpisodesWithSource
|
||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||
import eu.kanade.tachiyomi.util.shouldDownloadNewEpisodes
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||
|
@ -37,6 +40,7 @@ import kotlinx.coroutines.GlobalScope
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
|
@ -56,7 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||
*/
|
||||
class AnimelibUpdateService(
|
||||
val db: AnimeDatabaseHelper = Injekt.get(),
|
||||
val sourceManager: SourceManager = Injekt.get(),
|
||||
val sourceManager: AnimeSourceManager = Injekt.get(),
|
||||
val preferences: PreferencesHelper = Injekt.get(),
|
||||
val downloadManager: AnimeDownloadManager = Injekt.get(),
|
||||
val trackManager: TrackManager = Injekt.get(),
|
||||
|
@ -205,7 +209,7 @@ class AnimelibUpdateService(
|
|||
}
|
||||
updateJob = ioScope.launch(handler) {
|
||||
when (target) {
|
||||
Target.CHAPTERS -> updateChapterList()
|
||||
Target.CHAPTERS -> updateEpisodeList()
|
||||
Target.COVERS -> updateCovers()
|
||||
Target.TRACKING -> updateTrackings()
|
||||
}
|
||||
|
@ -262,7 +266,7 @@ class AnimelibUpdateService(
|
|||
* @param animeToUpdate the list to update
|
||||
* @return an observable delivering the progress of each update.
|
||||
*/
|
||||
suspend fun updateChapterList() {
|
||||
suspend fun updateEpisodeList() {
|
||||
val progressCount = AtomicInteger(0)
|
||||
val newUpdates = mutableListOf<Pair<AnimelibAnime, Array<Episode>>>()
|
||||
val failedUpdates = mutableListOf<Pair<Anime, String?>>()
|
||||
|
@ -279,8 +283,8 @@ class AnimelibUpdateService(
|
|||
val (newEpisodes, _) = updateAnime(anime)
|
||||
|
||||
if (newEpisodes.isNotEmpty()) {
|
||||
if (anime.shouldDownloadNewChapters(db, preferences)) {
|
||||
downloadChapters(anime, newEpisodes)
|
||||
if (anime.shouldDownloadNewEpisodes(db, preferences)) {
|
||||
downloadEpisodes(anime, newEpisodes)
|
||||
hasDownloads = true
|
||||
}
|
||||
|
||||
|
@ -315,7 +319,7 @@ class AnimelibUpdateService(
|
|||
}
|
||||
}
|
||||
|
||||
private fun downloadChapters(anime: Anime, episodes: List<Episode>) {
|
||||
private fun downloadEpisodes(anime: Anime, episodes: List<Episode>) {
|
||||
// We don't want to start downloading while the library is updating, because websites
|
||||
// may don't like it and they could ban the user.
|
||||
downloadManager.downloadEpisodes(anime, episodes, false)
|
||||
|
@ -327,7 +331,7 @@ class AnimelibUpdateService(
|
|||
* @param anime the anime to update.
|
||||
* @return a pair of the inserted and removed chapters.
|
||||
*/
|
||||
suspend fun updateAnime(anime: Anime): Pair<List<Chapter>, List<Chapter>> {
|
||||
suspend fun updateAnime(anime: Anime): Pair<List<Episode>, List<Episode>> {
|
||||
val source = sourceManager.getOrStub(anime.source)
|
||||
|
||||
// Update anime details metadata in the background
|
||||
|
@ -350,10 +354,10 @@ class AnimelibUpdateService(
|
|||
}
|
||||
}
|
||||
|
||||
val chapters = source.getChapterList(anime.toAnimeInfo())
|
||||
.map { it.toSChapter() }
|
||||
val chapters = source.getEpisodeList(anime.toAnimeInfo())
|
||||
.map { it.toSEpisode() }
|
||||
|
||||
return syncChaptersWithSource(db, chapters, anime, source)
|
||||
return syncEpisodesWithSource(db, chapters, anime, source)
|
||||
}
|
||||
|
||||
private suspend fun updateCovers() {
|
||||
|
@ -402,15 +406,15 @@ class AnimelibUpdateService(
|
|||
notifier.showProgressNotification(anime, progressCount++, animeToUpdate.size)
|
||||
|
||||
// Update the tracking details.
|
||||
db.getAnimeTracks(anime).executeAsBlocking()
|
||||
db.getTracks(anime).executeAsBlocking()
|
||||
.map { track ->
|
||||
supervisorScope {
|
||||
async {
|
||||
val service = trackManager.getService(track.sync_id)
|
||||
if (service != null && service in loggedServices) {
|
||||
try {
|
||||
val updatedTrack = service.refresh(track)
|
||||
db.insertAnimeTrack(updatedTrack).executeAsBlocking()
|
||||
val updatedTrack = service.refreshAnime(track)
|
||||
db.insertTrack(updatedTrack).executeAsBlocking()
|
||||
} catch (e: Throwable) {
|
||||
// Ignore errors and continue
|
||||
Timber.e(e)
|
||||
|
|
|
@ -3,24 +3,24 @@ package eu.kanade.tachiyomi.data.database
|
|||
import android.content.Context
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeCategoryTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeTrackTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.CategoryTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.EpisodeTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.HistoryTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeCategoryTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeTrackTypeMapping
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.queries.CategoryQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.EpisodeQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.HistoryQueries
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.queries.AnimeCategoryQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.AnimeQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.AnimeTrackQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.CategoryQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.EpisodeQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.HistoryQueries
|
||||
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,11 +11,11 @@ import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
|||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrackImpl
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_ANIME_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_FINISH_DATE
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_LAST_EPISODE_SEEN
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_LIBRARY_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_ANIME_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_MEDIA_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_SCORE
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTrackTable.COL_START_DATE
|
||||
|
|
|
@ -64,7 +64,7 @@ class AnimePutResolver : DefaultPutResolver<Anime>() {
|
|||
COL_LAST_UPDATE to obj.last_update,
|
||||
COL_INITIALIZED to obj.initialized,
|
||||
COL_VIEWER to obj.viewer,
|
||||
COL_CHAPTER_FLAGS to obj.chapter_flags,
|
||||
COL_CHAPTER_FLAGS to obj.episode_flags,
|
||||
COL_COVER_LAST_MODIFIED to obj.cover_last_modified,
|
||||
COL_DATE_ADDED to obj.date_added
|
||||
)
|
||||
|
@ -86,7 +86,7 @@ interface BaseAnimeGetResolver {
|
|||
last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE))
|
||||
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1
|
||||
viewer = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
|
||||
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
|
||||
episode_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
|
||||
cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED))
|
||||
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class EpisodePutResolver : DefaultPutResolver<Episode>() {
|
|||
override fun mapToContentValues(obj: Episode) =
|
||||
contentValuesOf(
|
||||
COL_ID to obj.id,
|
||||
COL_MANGA_ID to obj.manga_id,
|
||||
COL_MANGA_ID to obj.anime_id,
|
||||
COL_URL to obj.url,
|
||||
COL_NAME to obj.name,
|
||||
COL_READ to obj.read,
|
||||
|
@ -64,7 +64,7 @@ class EpisodeGetResolver : DefaultGetResolver<Episode>() {
|
|||
|
||||
override fun mapFromCursor(cursor: Cursor): Episode = EpisodeImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
|
||||
url = cursor.getString(cursor.getColumnIndex(COL_URL))
|
||||
name = cursor.getString(cursor.getColumnIndex(COL_NAME))
|
||||
scanlator = cursor.getString(cursor.getColumnIndex(COL_SCANLATOR))
|
||||
|
|
|
@ -32,7 +32,7 @@ open class AnimeImpl : Anime {
|
|||
|
||||
override var viewer: Int = 0
|
||||
|
||||
override var chapter_flags: Int = 0
|
||||
override var episode_flags: Int = 0
|
||||
|
||||
override var cover_last_modified: Long = 0
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ class EpisodeImpl : Episode {
|
|||
|
||||
override var id: Long? = null
|
||||
|
||||
override var manga_id: Long? = null
|
||||
override var anime_id: Long? = null
|
||||
|
||||
override lateinit var url: String
|
||||
|
||||
|
|
|
@ -4,19 +4,19 @@ import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
|||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimelibAnime
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimelibAnimeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimelibAnime
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeCoverLastModifiedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeFavoritePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeFlagsPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeLastUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeTitlePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeViewerPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimelibAnimeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeCategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
||||
|
||||
interface AnimeQueries : DbProvider {
|
||||
|
||||
|
@ -174,5 +174,4 @@ interface AnimeQueries : DbProvider {
|
|||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.data.database.queries
|
|||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
|
||||
interface CategoryQueries : DbProvider {
|
||||
|
@ -31,14 +31,14 @@ interface CategoryQueries : DbProvider {
|
|||
.prepare()
|
||||
|
||||
fun getCategoriesForAnime(anime: Anime) = db.get()
|
||||
.listOfObjects(Category::class.java)
|
||||
.withQuery(
|
||||
RawQuery.builder()
|
||||
.query(getCategoriesForMangaQuery())
|
||||
.args(anime.id)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
.listOfObjects(Category::class.java)
|
||||
.withQuery(
|
||||
RawQuery.builder()
|
||||
.query(getCategoriesForMangaQuery())
|
||||
.args(anime.id)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun insertCategory(category: Category) = db.put().`object`(category).prepare()
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@ package eu.kanade.tachiyomi.data.database.queries
|
|||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeEpisode
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeEpisodeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.EpisodeBackupPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.EpisodeKnownBackupPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.EpisodeProgressPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.EpisodeSourceOrderPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeEpisodeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.EpisodeTable
|
||||
import java.util.Date
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package eu.kanade.tachiyomi.data.database.queries
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeCategoryTable as AnimeCategory
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable as Anime
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
|
||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable as History
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeCategoryTable as AnimeCategory
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable as Anime
|
||||
|
||||
/**
|
||||
* Query to get the manga from the library, with their categories and unread count.
|
||||
|
@ -105,7 +105,7 @@ fun getLastReadMangaQuery() =
|
|||
"""
|
||||
|
||||
fun getLastReadAnimeQuery() =
|
||||
"""
|
||||
"""
|
||||
SELECT ${Anime.TABLE}.*, MAX(${History.TABLE}.${History.COL_LAST_READ}) AS max
|
||||
FROM ${Anime.TABLE}
|
||||
JOIN ${Chapter.TABLE}
|
||||
|
@ -128,7 +128,7 @@ fun getTotalChapterMangaQuery() =
|
|||
"""
|
||||
|
||||
fun getTotalChapterAnimeQuery() =
|
||||
"""
|
||||
"""
|
||||
SELECT ${Anime.TABLE}.*
|
||||
FROM ${Anime.TABLE}
|
||||
JOIN ${Anime.TABLE}
|
||||
|
@ -148,7 +148,7 @@ fun getLatestChapterMangaQuery() =
|
|||
"""
|
||||
|
||||
fun getLatestChapterAnimeQuery() =
|
||||
"""
|
||||
"""
|
||||
SELECT ${Anime.TABLE}.*, MAX(${Chapter.TABLE}.${Chapter.COL_DATE_UPLOAD}) AS max
|
||||
FROM ${Anime.TABLE}
|
||||
JOIN ${Chapter.TABLE}
|
||||
|
@ -168,7 +168,7 @@ fun getChapterFetchDateMangaQuery() =
|
|||
"""
|
||||
|
||||
fun getChapterFetchDateAnimeQuery() =
|
||||
"""
|
||||
"""
|
||||
SELECT ${Anime.TABLE}.*, MAX(${Chapter.TABLE}.${Chapter.COL_DATE_FETCH}) AS max
|
||||
FROM ${Anime.TABLE}
|
||||
JOIN ${Chapter.TABLE}
|
||||
|
@ -192,7 +192,7 @@ fun getCategoriesForMangaQuery() =
|
|||
* Query to get the categories for an anime.
|
||||
*/
|
||||
fun getCategoriesForAnimeQuery() =
|
||||
"""
|
||||
"""
|
||||
SELECT ${Category.TABLE}.* FROM ${Category.TABLE}
|
||||
JOIN ${AnimeCategory.TABLE} ON ${Category.TABLE}.${Category.COL_ID} =
|
||||
${AnimeCategory.TABLE}.${AnimeCategory.COL_CATEGORY_ID}
|
||||
|
|
|
@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.data.database.resolvers
|
|||
|
||||
import android.database.Cursor
|
||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.mappers.EpisodeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.mappers.AnimeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.mappers.EpisodeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeEpisode
|
||||
|
||||
class AnimeEpisodeGetResolver : DefaultGetResolver<AnimeEpisode>() {
|
||||
|
|
|
@ -37,6 +37,6 @@ class AnimeFlagsPutResolver(private val updateAll: Boolean = false) : PutResolve
|
|||
|
||||
fun mapToContentValues(anime: Anime) =
|
||||
contentValuesOf(
|
||||
AnimeTable.COL_CHAPTER_FLAGS to anime.chapter_flags
|
||||
AnimeTable.COL_CHAPTER_FLAGS to anime.episode_flags
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class AnimeSourceOrderPutResolver : PutResolver<Episode>() {
|
|||
fun mapToUpdateQuery(episode: Episode) = UpdateQuery.builder()
|
||||
.table(EpisodeTable.TABLE)
|
||||
.where("${EpisodeTable.COL_URL} = ? AND ${EpisodeTable.COL_MANGA_ID} = ?")
|
||||
.whereArgs(episode.url, episode.manga_id)
|
||||
.whereArgs(episode.url, episode.anime_id)
|
||||
.build()
|
||||
|
||||
fun mapToContentValues(episode: Episode) =
|
||||
|
|
|
@ -22,7 +22,7 @@ class EpisodeSourceOrderPutResolver : PutResolver<Episode>() {
|
|||
fun mapToUpdateQuery(episode: Episode) = UpdateQuery.builder()
|
||||
.table(EpisodeTable.TABLE)
|
||||
.where("${EpisodeTable.COL_URL} = ? AND ${EpisodeTable.COL_MANGA_ID} = ?")
|
||||
.whereArgs(episode.url, episode.manga_id)
|
||||
.whereArgs(episode.url, episode.anime_id)
|
||||
.build()
|
||||
|
||||
fun mapToContentValues(episode: Episode) =
|
||||
|
|
|
@ -3,10 +3,10 @@ package eu.kanade.tachiyomi.data.download
|
|||
import android.content.Context
|
||||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit
|
|||
class AnimeDownloadCache(
|
||||
private val context: Context,
|
||||
private val provider: AnimeDownloadProvider,
|
||||
private val sourceManager: SourceManager,
|
||||
private val sourceManager: AnimeSourceManager,
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
) {
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ import android.content.Context
|
|||
import com.hippo.unifile.UniFile
|
||||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownloadQueue
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.AnimeSource
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import rx.Observable
|
||||
|
@ -26,7 +26,7 @@ import uy.kohesive.injekt.injectLazy
|
|||
*/
|
||||
class AnimeDownloadManager(private val context: Context) {
|
||||
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val sourceManager: AnimeSourceManager by injectLazy()
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
/**
|
||||
|
@ -137,7 +137,7 @@ class AnimeDownloadManager(private val context: Context) {
|
|||
* @param episode the downloaded episode.
|
||||
* @return an observable containing the list of pages from the episode.
|
||||
*/
|
||||
fun buildPageList(source: Source, anime: Anime, episode: Episode): Observable<List<Page>> {
|
||||
fun buildPageList(source: AnimeSource, anime: Anime, episode: Episode): Observable<List<Page>> {
|
||||
return buildPageList(provider.findEpisodeDir(episode, anime, source))
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ class AnimeDownloadManager(private val context: Context) {
|
|||
* @param anime the anime of the episodes.
|
||||
* @param source the source of the episodes.
|
||||
*/
|
||||
fun deleteEpisodes(episodes: List<Episode>, anime: Anime, source: Source): List<Episode> {
|
||||
fun deleteEpisodes(episodes: List<Episode>, anime: Anime, source: AnimeSource): List<Episode> {
|
||||
val filteredEpisodes = getEpisodesToDelete(episodes)
|
||||
launchIO {
|
||||
removeFromDownloadQueue(filteredEpisodes)
|
||||
|
@ -249,7 +249,7 @@ class AnimeDownloadManager(private val context: Context) {
|
|||
* @param anime the anime to delete.
|
||||
* @param source the source of the anime.
|
||||
*/
|
||||
fun deleteAnime(anime: Anime, source: Source) {
|
||||
fun deleteAnime(anime: Anime, source: AnimeSource) {
|
||||
launchIO {
|
||||
downloader.queue.remove(anime)
|
||||
provider.findAnimeDir(anime, source)?.delete()
|
||||
|
@ -286,7 +286,7 @@ class AnimeDownloadManager(private val context: Context) {
|
|||
* @param oldEpisode the existing episode with the old name.
|
||||
* @param newEpisode the target episode with the new name.
|
||||
*/
|
||||
fun renameEpisode(source: Source, anime: Anime, oldEpisode: Episode, newEpisode: Episode) {
|
||||
fun renameEpisode(source: AnimeSource, anime: Anime, oldEpisode: Episode, newEpisode: Episode) {
|
||||
val oldNames = provider.getValidEpisodeDirNames(oldEpisode)
|
||||
val newName = provider.getEpisodeDirName(newEpisode)
|
||||
val animeDir = provider.getAnimeDir(anime, source)
|
||||
|
|
|
@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.data.download
|
|||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
|
|
@ -4,10 +4,10 @@ import android.content.Context
|
|||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.AnimeSource
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
@ -48,7 +48,7 @@ class AnimeDownloadProvider(private val context: Context) {
|
|||
* @param anime the anime to query.
|
||||
* @param source the source of the anime.
|
||||
*/
|
||||
internal fun getAnimeDir(anime: Anime, source: Source): UniFile {
|
||||
internal fun getAnimeDir(anime: Anime, source: AnimeSource): UniFile {
|
||||
try {
|
||||
return downloadsDir
|
||||
.createDirectory(getSourceDirName(source))
|
||||
|
@ -64,7 +64,7 @@ class AnimeDownloadProvider(private val context: Context) {
|
|||
*
|
||||
* @param source the source to query.
|
||||
*/
|
||||
fun findSourceDir(source: Source): UniFile? {
|
||||
fun findSourceDir(source: AnimeSource): UniFile? {
|
||||
return downloadsDir.findFile(getSourceDirName(source), true)
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ class AnimeDownloadProvider(private val context: Context) {
|
|||
* @param anime the anime to query.
|
||||
* @param source the source of the anime.
|
||||
*/
|
||||
fun findAnimeDir(anime: Anime, source: Source): UniFile? {
|
||||
fun findAnimeDir(anime: Anime, source: AnimeSource): UniFile? {
|
||||
val sourceDir = findSourceDir(source)
|
||||
return sourceDir?.findFile(getAnimeDirName(anime))
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class AnimeDownloadProvider(private val context: Context) {
|
|||
* @param anime the anime of the episode.
|
||||
* @param source the source of the episode.
|
||||
*/
|
||||
fun findEpisodeDir(episode: Episode, anime: Anime, source: Source): UniFile? {
|
||||
fun findEpisodeDir(episode: Episode, anime: Anime, source: AnimeSource): UniFile? {
|
||||
val animeDir = findAnimeDir(anime, source)
|
||||
return getValidEpisodeDirNames(episode).asSequence()
|
||||
.mapNotNull { animeDir?.findFile(it) }
|
||||
|
@ -100,7 +100,7 @@ class AnimeDownloadProvider(private val context: Context) {
|
|||
* @param anime the anime of the episode.
|
||||
* @param source the source of the episode.
|
||||
*/
|
||||
fun findEpisodeDirs(episodes: List<Episode>, anime: Anime, source: Source): List<UniFile> {
|
||||
fun findEpisodeDirs(episodes: List<Episode>, anime: Anime, source: AnimeSource): List<UniFile> {
|
||||
val animeDir = findAnimeDir(anime, source) ?: return emptyList()
|
||||
return episodes.mapNotNull { episode ->
|
||||
getValidEpisodeDirNames(episode).asSequence()
|
||||
|
@ -114,7 +114,7 @@ class AnimeDownloadProvider(private val context: Context) {
|
|||
*
|
||||
* @param source the source to query.
|
||||
*/
|
||||
fun getSourceDirName(source: Source): String {
|
||||
fun getSourceDirName(source: AnimeSource): String {
|
||||
return source.toString()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.data.download
|
|||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
@ -20,7 +20,7 @@ import uy.kohesive.injekt.injectLazy
|
|||
*/
|
||||
class AnimeDownloadStore(
|
||||
context: Context,
|
||||
private val sourceManager: SourceManager
|
||||
private val sourceManager: AnimeSourceManager
|
||||
) {
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@ class AnimeDownloadStore(
|
|||
private val preferences = context.getSharedPreferences("active_downloads", Context.MODE_PRIVATE)
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
private val db: AnimeDatabaseHelper by injectLazy()
|
||||
|
||||
/**
|
||||
* Counter used to keep the queue order.
|
||||
|
@ -73,7 +73,7 @@ class AnimeDownloadStore(
|
|||
* @param download the download.
|
||||
*/
|
||||
private fun getKey(download: AnimeDownload): String {
|
||||
return download.chapter.id!!.toString()
|
||||
return download.episode.id!!.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,9 +92,9 @@ class AnimeDownloadStore(
|
|||
val anime = cachedAnime.getOrPut(animeId) {
|
||||
db.getAnime(animeId).executeAsBlocking()
|
||||
} ?: continue
|
||||
val source = sourceManager.get(anime.source) as? HttpSource ?: continue
|
||||
val chapter = db.getChapter(chapterId).executeAsBlocking() ?: continue
|
||||
downloads.add(AnimeDownload(source, anime, chapter))
|
||||
val source = sourceManager.get(anime.source) as? AnimeHttpSource ?: continue
|
||||
val episode = db.getEpisode(chapterId).executeAsBlocking() ?: continue
|
||||
downloads.add(AnimeDownload(source, anime, episode))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ class AnimeDownloadStore(
|
|||
* @param download the download to serialize.
|
||||
*/
|
||||
private fun serialize(download: AnimeDownload): String {
|
||||
val obj = AnimeDownloadObject(download.anime.id!!, download.chapter.id!!, counter++)
|
||||
val obj = AnimeDownloadObject(download.anime.id!!, download.episode.id!!, counter++)
|
||||
return json.encodeToString(obj)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ import com.jakewharton.rxrelay.BehaviorRelay
|
|||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.EpisodeCache
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownloadQueue
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList
|
||||
|
@ -50,7 +50,7 @@ class AnimeDownloader(
|
|||
private val context: Context,
|
||||
private val provider: AnimeDownloadProvider,
|
||||
private val cache: AnimeDownloadCache,
|
||||
private val sourceManager: SourceManager
|
||||
private val sourceManager: AnimeSourceManager
|
||||
) {
|
||||
|
||||
private val episodeCache: EpisodeCache by injectLazy()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package eu.kanade.tachiyomi.data.download.model
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
|
||||
import rx.subjects.PublishSubject
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package eu.kanade.tachiyomi.data.download.model
|
||||
|
||||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.download.AnimeDownloadStore
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import rx.Observable
|
||||
|
|
|
@ -9,18 +9,20 @@ import android.net.Uri
|
|||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService
|
||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherActivity
|
||||
|
@ -34,8 +36,6 @@ import uy.kohesive.injekt.api.get
|
|||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
|
||||
/**
|
||||
* Global [BroadcastReceiver] that runs on UI thread
|
||||
|
@ -445,11 +445,11 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||
*/
|
||||
internal fun openEpisodePendingActivity(context: Context, anime: Anime, groupId: Int): PendingIntent {
|
||||
val newIntent =
|
||||
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_MANGA)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.putExtra(AnimeController.MANGA_EXTRA, anime.id)
|
||||
.putExtra("notificationId", anime.id.hashCode())
|
||||
.putExtra("groupId", groupId)
|
||||
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_MANGA)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.putExtra(AnimeController.MANGA_EXTRA, anime.id)
|
||||
.putExtra("notificationId", anime.id.hashCode())
|
||||
.putExtra("groupId", groupId)
|
||||
return PendingIntent.getActivity(context, anime.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
|
@ -460,10 +460,10 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||
* @param anime anime of episode
|
||||
*/
|
||||
internal fun markAsReadPendingBroadcast(
|
||||
context: Context,
|
||||
anime: Anime,
|
||||
episodes: Array<Episode>,
|
||||
groupId: Int
|
||||
context: Context,
|
||||
anime: Anime,
|
||||
episodes: Array<Episode>,
|
||||
groupId: Int
|
||||
): PendingIntent {
|
||||
val newIntent = Intent(context, NotificationReceiver::class.java).apply {
|
||||
action = ACTION_MARK_AS_READ
|
||||
|
@ -474,7 +474,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||
}
|
||||
return PendingIntent.getBroadcast(context, anime.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns [PendingIntent] that starts a reader activity containing chapter.
|
||||
*
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.preference.PreferenceManager
|
|||
import com.tfcporciuncula.flow.FlowSharedPreferences
|
||||
import com.tfcporciuncula.flow.Preference
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DisplayMode
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
||||
|
|
|
@ -4,8 +4,8 @@ import androidx.annotation.CallSuper
|
|||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
|
@ -51,6 +51,8 @@ abstract class TrackService(val id: Int) {
|
|||
|
||||
abstract suspend fun add(track: Track): Track
|
||||
|
||||
abstract suspend fun addAnime(track: AnimeTrack): AnimeTrack
|
||||
|
||||
abstract suspend fun update(track: Track): Track
|
||||
|
||||
abstract suspend fun updateAnime(track: AnimeTrack): AnimeTrack
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
|
@ -130,10 +131,32 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
}
|
||||
|
||||
override fun displayScore(track: AnimeTrack): String {
|
||||
val score = track.score
|
||||
|
||||
return when (scorePreference.get()) {
|
||||
POINT_5 -> when (score) {
|
||||
0f -> "0 ★"
|
||||
else -> "${((score + 10) / 20).toInt()} ★"
|
||||
}
|
||||
POINT_3 -> when {
|
||||
score == 0f -> "0"
|
||||
score <= 35 -> "😦"
|
||||
score <= 60 -> "😐"
|
||||
else -> "😊"
|
||||
}
|
||||
else -> track.toAnilistScore()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun add(track: Track): Track {
|
||||
return api.addLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun addAnime(track: AnimeTrack): AnimeTrack {
|
||||
return api.addLibAnime(track)
|
||||
}
|
||||
|
||||
override suspend fun update(track: Track): Track {
|
||||
// If user was using API v1 fetch library_id
|
||||
if (track.library_id == null || track.library_id!! == 0L) {
|
||||
|
@ -145,6 +168,17 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
|
|||
return api.updateLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun updateAnime(track: AnimeTrack): AnimeTrack {
|
||||
// If user was using API v1 fetch library_id
|
||||
if (track.library_id == null || track.library_id!! == 0L) {
|
||||
val libManga = api.findLibAnime(track, getUsername().toInt())
|
||||
?: throw Exception("$track not found on user library")
|
||||
track.library_id = libManga.library_id
|
||||
}
|
||||
|
||||
return api.updateLibAnime(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track): Track {
|
||||
val remoteTrack = api.findLibManga(track, getUsername().toInt())
|
||||
return if (remoteTrack != null) {
|
||||
|
@ -159,6 +193,20 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun bindAnime(track: AnimeTrack): AnimeTrack {
|
||||
val remoteTrack = api.findLibAnime(track, getUsername().toInt())
|
||||
return if (remoteTrack != null) {
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.library_id = remoteTrack.library_id
|
||||
updateAnime(track)
|
||||
} else {
|
||||
// Set default fields if it's not found in the list
|
||||
track.status = READING
|
||||
track.score = 0F
|
||||
addAnime(track)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<TrackSearch> {
|
||||
return api.search(query)
|
||||
}
|
||||
|
@ -170,6 +218,13 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
|
|||
return track
|
||||
}
|
||||
|
||||
override suspend fun refreshAnime(track: AnimeTrack): AnimeTrack {
|
||||
val remoteTrack = api.getLibAnime(track, getUsername().toInt())
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.total_episodes = remoteTrack.total_episodes
|
||||
return track
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String) = login(password)
|
||||
|
||||
suspend fun login(token: String) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.core.net.toUri
|
|||
import com.afollestad.date.dayOfMonth
|
||||
import com.afollestad.date.month
|
||||
import com.afollestad.date.year
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
|
@ -100,6 +101,74 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun addLibAnime(track: AnimeTrack): AnimeTrack {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
||||
| id
|
||||
| status
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val payload = buildJsonObject {
|
||||
put("query", query)
|
||||
putJsonObject("variables") {
|
||||
put("mangaId", track.media_id)
|
||||
put("progress", track.last_episode_seen)
|
||||
put("status", track.toAnilistStatus())
|
||||
}
|
||||
}
|
||||
authClient.newCall(
|
||||
POST(
|
||||
apiUrl,
|
||||
body = payload.toString().toRequestBody(jsonMime)
|
||||
)
|
||||
)
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let {
|
||||
track.library_id =
|
||||
it["data"]!!.jsonObject["SaveMediaListEntry"]!!.jsonObject["id"]!!.jsonPrimitive.long
|
||||
track
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibAnime(track: AnimeTrack): AnimeTrack {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|mutation UpdateManga(
|
||||
|${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus,
|
||||
|${'$'}score: Int, ${'$'}startedAt: FuzzyDateInput, ${'$'}completedAt: FuzzyDateInput
|
||||
|) {
|
||||
|SaveMediaListEntry(
|
||||
|id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status,
|
||||
|scoreRaw: ${'$'}score, startedAt: ${'$'}startedAt, completedAt: ${'$'}completedAt
|
||||
|) {
|
||||
|id
|
||||
|status
|
||||
|progress
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val payload = buildJsonObject {
|
||||
put("query", query)
|
||||
putJsonObject("variables") {
|
||||
put("listId", track.library_id)
|
||||
put("progress", track.last_episode_seen)
|
||||
put("status", track.toAnilistStatus())
|
||||
put("score", track.score.toInt())
|
||||
put("startedAt", createDate(track.started_watching_date))
|
||||
put("completedAt", createDate(track.finished_watching_date))
|
||||
}
|
||||
}
|
||||
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
|
||||
.await()
|
||||
track
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun search(search: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|
@ -267,10 +336,81 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun findLibAnime(track: AnimeTrack, userid: Int): AnimeTrack? {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
||||
|Page {
|
||||
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
||||
|id
|
||||
|status
|
||||
|scoreRaw: score(format: POINT_100)
|
||||
|progress
|
||||
|startedAt {
|
||||
|year
|
||||
|month
|
||||
|day
|
||||
|}
|
||||
|completedAt {
|
||||
|year
|
||||
|month
|
||||
|day
|
||||
|}
|
||||
|media {
|
||||
|id
|
||||
|title {
|
||||
|romaji
|
||||
|}
|
||||
|coverImage {
|
||||
|large
|
||||
|}
|
||||
|type
|
||||
|status
|
||||
|chapters
|
||||
|description
|
||||
|startDate {
|
||||
|year
|
||||
|month
|
||||
|day
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val payload = buildJsonObject {
|
||||
put("query", query)
|
||||
putJsonObject("variables") {
|
||||
put("id", userid)
|
||||
put("manga_id", track.media_id)
|
||||
}
|
||||
}
|
||||
authClient.newCall(
|
||||
POST(
|
||||
apiUrl,
|
||||
body = payload.toString().toRequestBody(jsonMime)
|
||||
)
|
||||
)
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let { response ->
|
||||
val data = response["data"]!!.jsonObject
|
||||
val page = data["Page"]!!.jsonObject
|
||||
val media = page["mediaList"]!!.jsonArray
|
||||
val entries = media.map { jsonToALUserAnime(it.jsonObject) }
|
||||
entries.firstOrNull()?.toTrack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getLibManga(track: Track, userid: Int): Track {
|
||||
return findLibManga(track, userid) ?: throw Exception("Could not find manga")
|
||||
}
|
||||
|
||||
suspend fun getLibAnime(track: AnimeTrack, userid: Int): AnimeTrack {
|
||||
return findLibAnime(track, userid) ?: throw Exception("Could not find anime")
|
||||
}
|
||||
|
||||
fun createOAuth(token: String): OAuth {
|
||||
return OAuth(token, "Bearer", System.currentTimeMillis() + 31536000000, 31536000000)
|
||||
}
|
||||
|
@ -334,6 +474,18 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
)
|
||||
}
|
||||
|
||||
private fun jsonToALUserAnime(struct: JsonObject): ALUserAnime {
|
||||
return ALUserAnime(
|
||||
struct["id"]!!.jsonPrimitive.long,
|
||||
struct["status"]!!.jsonPrimitive.content,
|
||||
struct["scoreRaw"]!!.jsonPrimitive.int,
|
||||
struct["progress"]!!.jsonPrimitive.int,
|
||||
parseDate(struct, "startedAt"),
|
||||
parseDate(struct, "completedAt"),
|
||||
jsonToALManga(struct["media"]!!.jsonObject)
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseDate(struct: JsonObject, dateKey: String): Long {
|
||||
return try {
|
||||
val date = Calendar.getInstance()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.data.track.anilist
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
|
@ -102,6 +103,38 @@ data class ALUserManga(
|
|||
}
|
||||
}
|
||||
|
||||
data class ALUserAnime(
|
||||
val library_id: Long,
|
||||
val list_status: String,
|
||||
val score_raw: Int,
|
||||
val chapters_read: Int,
|
||||
val start_date_fuzzy: Long,
|
||||
val completed_date_fuzzy: Long,
|
||||
val manga: ALManga
|
||||
) {
|
||||
|
||||
fun toTrack() = AnimeTrack.create(TrackManager.ANILIST).apply {
|
||||
media_id = manga.media_id
|
||||
status = toTrackStatus()
|
||||
score = score_raw.toFloat()
|
||||
started_watching_date = start_date_fuzzy
|
||||
finished_watching_date = completed_date_fuzzy
|
||||
last_episode_seen = chapters_read
|
||||
library_id = this@ALUserAnime.library_id
|
||||
total_episodes = manga.total_chapters
|
||||
}
|
||||
|
||||
fun toTrackStatus() = when (list_status) {
|
||||
"CURRENT" -> Anilist.READING
|
||||
"COMPLETED" -> Anilist.COMPLETED
|
||||
"PAUSED" -> Anilist.PAUSED
|
||||
"DROPPED" -> Anilist.DROPPED
|
||||
"PLANNING" -> Anilist.PLANNING
|
||||
"REPEATING" -> Anilist.REPEATING
|
||||
else -> throw NotImplementedError("Unknown status: $list_status")
|
||||
}
|
||||
}
|
||||
|
||||
fun Track.toAnilistStatus() = when (status) {
|
||||
Anilist.READING -> "CURRENT"
|
||||
Anilist.COMPLETED -> "COMPLETED"
|
||||
|
@ -112,6 +145,16 @@ fun Track.toAnilistStatus() = when (status) {
|
|||
else -> throw NotImplementedError("Unknown status: $status")
|
||||
}
|
||||
|
||||
fun AnimeTrack.toAnilistStatus() = when (status) {
|
||||
Anilist.READING -> "CURRENT"
|
||||
Anilist.COMPLETED -> "COMPLETED"
|
||||
Anilist.PAUSED -> "PAUSED"
|
||||
Anilist.DROPPED -> "DROPPED"
|
||||
Anilist.PLANNING -> "PLANNING"
|
||||
Anilist.REPEATING -> "REPEATING"
|
||||
else -> throw NotImplementedError("Unknown status: $status")
|
||||
}
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().get()) {
|
||||
|
@ -139,3 +182,29 @@ fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().get())
|
|||
"POINT_10_DECIMAL" -> (score / 10).toString()
|
||||
else -> throw NotImplementedError("Unknown score type")
|
||||
}
|
||||
|
||||
fun AnimeTrack.toAnilistScore(): String = when (preferences.anilistScoreType().get()) {
|
||||
// 10 point
|
||||
"POINT_10" -> (score.toInt() / 10).toString()
|
||||
// 100 point
|
||||
"POINT_100" -> score.toInt().toString()
|
||||
// 5 stars
|
||||
"POINT_5" -> when {
|
||||
score == 0f -> "0"
|
||||
score < 30 -> "1"
|
||||
score < 50 -> "2"
|
||||
score < 70 -> "3"
|
||||
score < 90 -> "4"
|
||||
else -> "5"
|
||||
}
|
||||
// Smiley
|
||||
"POINT_3" -> when {
|
||||
score == 0f -> "0"
|
||||
score <= 35 -> ":("
|
||||
score <= 60 -> ":|"
|
||||
else -> ":)"
|
||||
}
|
||||
// 10 point decimal
|
||||
"POINT_10_DECIMAL" -> (score / 10).toString()
|
||||
else -> throw NotImplementedError("Unknown score type")
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
|
@ -31,14 +32,26 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
|||
return track.score.toInt().toString()
|
||||
}
|
||||
|
||||
override fun displayScore(track: AnimeTrack): String {
|
||||
return track.score.toInt().toString()
|
||||
}
|
||||
|
||||
override suspend fun add(track: Track): Track {
|
||||
return api.addLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun addAnime(track: AnimeTrack): AnimeTrack {
|
||||
return api.addLibAnime(track)
|
||||
}
|
||||
|
||||
override suspend fun update(track: Track): Track {
|
||||
return api.updateLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun updateAnime(track: AnimeTrack): AnimeTrack {
|
||||
return api.updateLibAnime(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track): Track {
|
||||
val statusTrack = api.statusLibManga(track)
|
||||
val remoteTrack = api.findLibManga(track)
|
||||
|
@ -59,6 +72,26 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun bindAnime(track: AnimeTrack): AnimeTrack {
|
||||
val statusTrack = api.statusLibAnime(track)
|
||||
val remoteTrack = api.findLibAnime(track)
|
||||
return if (remoteTrack != null && statusTrack != null) {
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.library_id = remoteTrack.library_id
|
||||
track.status = statusTrack.status
|
||||
track.score = statusTrack.score
|
||||
track.last_episode_seen = statusTrack.last_episode_seen
|
||||
track.total_episodes = remoteTrack.total_episodes
|
||||
refreshAnime(track)
|
||||
} else {
|
||||
// Set default fields if it's not found in the list
|
||||
track.status = READING
|
||||
track.score = 0F
|
||||
addAnime(track)
|
||||
updateAnime(track)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<TrackSearch> {
|
||||
return api.search(query)
|
||||
}
|
||||
|
@ -72,6 +105,15 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
|||
return track
|
||||
}
|
||||
|
||||
override suspend fun refreshAnime(track: AnimeTrack): AnimeTrack {
|
||||
val remoteStatusTrack = api.statusLibAnime(track)
|
||||
track.copyPersonalFrom(remoteStatusTrack!!)
|
||||
api.findLibAnime(track)?.let { remoteTrack ->
|
||||
track.total_episodes = remoteTrack.total_episodes
|
||||
}
|
||||
return track
|
||||
}
|
||||
|
||||
override fun getLogo() = R.drawable.ic_tracker_bangumi
|
||||
|
||||
override fun getLogoColor() = Color.rgb(240, 145, 153)
|
||||
|
|
|
@ -2,8 +2,10 @@ package eu.kanade.tachiyomi.data.track.bangumi
|
|||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
|
@ -43,6 +45,18 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun addLibAnime(track: AnimeTrack): AnimeTrack {
|
||||
return withIOContext {
|
||||
val body = FormBody.Builder()
|
||||
.add("rating", track.score.toInt().toString())
|
||||
.add("status", track.toBangumiStatus())
|
||||
.build()
|
||||
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body))
|
||||
.await()
|
||||
track
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
// read status update
|
||||
|
@ -68,6 +82,31 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibAnime(track: AnimeTrack): AnimeTrack {
|
||||
return withIOContext {
|
||||
// read status update
|
||||
val sbody = FormBody.Builder()
|
||||
.add("rating", track.score.toInt().toString())
|
||||
.add("status", track.toBangumiStatus())
|
||||
.build()
|
||||
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody))
|
||||
.await()
|
||||
|
||||
// chapter update
|
||||
val body = FormBody.Builder()
|
||||
.add("watched_eps", track.last_episode_seen.toString())
|
||||
.build()
|
||||
authClient.newCall(
|
||||
POST(
|
||||
"$apiUrl/subject/${track.media_id}/update/watched_eps",
|
||||
body = body
|
||||
)
|
||||
).await()
|
||||
|
||||
track
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun search(search: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
|
||||
|
@ -114,6 +153,28 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
|||
}
|
||||
}
|
||||
|
||||
private fun jsonToSearchAnime(obj: JsonObject): AnimeTrackSearch {
|
||||
val coverUrl = if (obj["images"] is JsonObject) {
|
||||
obj["images"]?.jsonObject?.get("common")?.jsonPrimitive?.contentOrNull ?: ""
|
||||
} else {
|
||||
// Sometimes JsonNull
|
||||
""
|
||||
}
|
||||
val totalChapters = if (obj["eps_count"] != null) {
|
||||
obj["eps_count"]!!.jsonPrimitive.int
|
||||
} else {
|
||||
0
|
||||
}
|
||||
return AnimeTrackSearch.create(TrackManager.BANGUMI).apply {
|
||||
media_id = obj["id"]!!.jsonPrimitive.int
|
||||
title = obj["name_cn"]!!.jsonPrimitive.content
|
||||
cover_url = coverUrl
|
||||
summary = obj["name"]!!.jsonPrimitive.content
|
||||
tracking_url = obj["url"]!!.jsonPrimitive.content
|
||||
total_episodes = totalChapters
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findLibManga(track: Track): Track? {
|
||||
return withIOContext {
|
||||
authClient.newCall(GET("$apiUrl/subject/${track.media_id}"))
|
||||
|
@ -123,6 +184,15 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun findLibAnime(track: AnimeTrack): AnimeTrack? {
|
||||
return withIOContext {
|
||||
authClient.newCall(GET("$apiUrl/subject/${track.media_id}"))
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let { jsonToSearchAnime(it) }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun statusLibManga(track: Track): Track? {
|
||||
return withIOContext {
|
||||
val urlUserRead = "$apiUrl/collection/${track.media_id}"
|
||||
|
@ -151,6 +221,34 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun statusLibAnime(track: AnimeTrack): AnimeTrack? {
|
||||
return withIOContext {
|
||||
val urlUserRead = "$apiUrl/collection/${track.media_id}"
|
||||
val requestUserRead = Request.Builder()
|
||||
.url(urlUserRead)
|
||||
.cacheControl(CacheControl.FORCE_NETWORK)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
// TODO: get user readed chapter here
|
||||
var response = authClient.newCall(requestUserRead).await()
|
||||
var responseBody = response.body?.string().orEmpty()
|
||||
if (responseBody.isEmpty()) {
|
||||
throw Exception("Null Response")
|
||||
}
|
||||
if (responseBody.contains("\"code\":400")) {
|
||||
null
|
||||
} else {
|
||||
json.decodeFromString<Collection>(responseBody).let {
|
||||
track.status = it.status?.id!!
|
||||
track.last_episode_seen = it.ep_status!!
|
||||
track.score = it.rating!!
|
||||
track
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun accessToken(code: String): OAuth {
|
||||
return withIOContext {
|
||||
client.newCall(accessTokenRequest(code))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.data.track.bangumi
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
|
||||
fun Track.toBangumiStatus() = when (status) {
|
||||
|
@ -11,6 +12,15 @@ fun Track.toBangumiStatus() = when (status) {
|
|||
else -> throw NotImplementedError("Unknown status: $status")
|
||||
}
|
||||
|
||||
fun AnimeTrack.toBangumiStatus() = when (status) {
|
||||
Bangumi.READING -> "do"
|
||||
Bangumi.COMPLETED -> "collect"
|
||||
Bangumi.ON_HOLD -> "on_hold"
|
||||
Bangumi.DROPPED -> "dropped"
|
||||
Bangumi.PLANNING -> "wish"
|
||||
else -> throw NotImplementedError("Unknown status: $status")
|
||||
}
|
||||
|
||||
fun toTrackStatus(status: String) = when (status) {
|
||||
"do" -> Bangumi.READING
|
||||
"collect" -> Bangumi.COMPLETED
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
|
@ -67,14 +68,27 @@ class Kitsu(private val context: Context, id: Int) : TrackService(id) {
|
|||
return df.format(track.score)
|
||||
}
|
||||
|
||||
override fun displayScore(track: AnimeTrack): String {
|
||||
val df = DecimalFormat("0.#")
|
||||
return df.format(track.score)
|
||||
}
|
||||
|
||||
override suspend fun add(track: Track): Track {
|
||||
return api.addLibManga(track, getUserId())
|
||||
}
|
||||
|
||||
override suspend fun addAnime(track: AnimeTrack): AnimeTrack {
|
||||
return api.addLibAnime(track, getUserId())
|
||||
}
|
||||
|
||||
override suspend fun update(track: Track): Track {
|
||||
return api.updateLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun updateAnime(track: AnimeTrack): AnimeTrack {
|
||||
return api.updateLibAnime(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track): Track {
|
||||
val remoteTrack = api.findLibManga(track, getUserId())
|
||||
return if (remoteTrack != null) {
|
||||
|
@ -88,6 +102,19 @@ class Kitsu(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun bindAnime(track: AnimeTrack): AnimeTrack {
|
||||
val remoteTrack = api.findLibAnime(track, getUserId())
|
||||
return if (remoteTrack != null) {
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.media_id = remoteTrack.media_id
|
||||
updateAnime(track)
|
||||
} else {
|
||||
track.status = READING
|
||||
track.score = 0F
|
||||
addAnime(track)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<TrackSearch> {
|
||||
return api.search(query)
|
||||
}
|
||||
|
@ -99,6 +126,13 @@ class Kitsu(private val context: Context, id: Int) : TrackService(id) {
|
|||
return track
|
||||
}
|
||||
|
||||
override suspend fun refreshAnime(track: AnimeTrack): AnimeTrack {
|
||||
val remoteTrack = api.getLibAnime(track)
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.total_episodes = remoteTrack.total_episodes
|
||||
return track
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String) {
|
||||
val token = api.login(username, password)
|
||||
interceptor.newAuth(token)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.data.track.kitsu
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
|
@ -74,6 +75,51 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun addLibAnime(track: AnimeTrack, userId: String): AnimeTrack {
|
||||
return withIOContext {
|
||||
val data = buildJsonObject {
|
||||
putJsonObject("data") {
|
||||
put("type", "libraryEntries")
|
||||
putJsonObject("attributes") {
|
||||
put("status", track.toKitsuStatus())
|
||||
put("progress", track.last_episode_seen)
|
||||
}
|
||||
putJsonObject("relationships") {
|
||||
putJsonObject("user") {
|
||||
putJsonObject("data") {
|
||||
put("id", userId)
|
||||
put("type", "users")
|
||||
}
|
||||
}
|
||||
putJsonObject("media") {
|
||||
putJsonObject("data") {
|
||||
put("id", track.media_id)
|
||||
put("type", "manga")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authClient.newCall(
|
||||
POST(
|
||||
"${baseUrl}library-entries",
|
||||
headers = headersOf(
|
||||
"Content-Type",
|
||||
"application/vnd.api+json"
|
||||
),
|
||||
body = data.toString().toRequestBody("application/vnd.api+json".toMediaType())
|
||||
)
|
||||
)
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let {
|
||||
track.media_id = it["data"]!!.jsonObject["id"]!!.jsonPrimitive.int
|
||||
track
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
val data = buildJsonObject {
|
||||
|
@ -108,6 +154,40 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibAnime(track: AnimeTrack): AnimeTrack {
|
||||
return withIOContext {
|
||||
val data = buildJsonObject {
|
||||
putJsonObject("data") {
|
||||
put("type", "libraryEntries")
|
||||
put("id", track.media_id)
|
||||
putJsonObject("attributes") {
|
||||
put("status", track.toKitsuStatus())
|
||||
put("progress", track.last_episode_seen)
|
||||
put("ratingTwenty", track.toKitsuScore())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authClient.newCall(
|
||||
Request.Builder()
|
||||
.url("${baseUrl}library-entries/${track.media_id}")
|
||||
.headers(
|
||||
headersOf(
|
||||
"Content-Type",
|
||||
"application/vnd.api+json"
|
||||
)
|
||||
)
|
||||
.patch(data.toString().toRequestBody("application/vnd.api+json".toMediaType()))
|
||||
.build()
|
||||
)
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let {
|
||||
track
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun search(query: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
authClient.newCall(GET(algoliaKeyUrl))
|
||||
|
@ -170,6 +250,27 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun findLibAnime(track: AnimeTrack, userId: String): AnimeTrack? {
|
||||
return withIOContext {
|
||||
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
||||
.encodedQuery("filter[manga_id]=${track.media_id}&filter[user_id]=$userId")
|
||||
.appendQueryParameter("include", "manga")
|
||||
.build()
|
||||
authClient.newCall(GET(url.toString()))
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let {
|
||||
val data = it["data"]!!.jsonArray
|
||||
if (data.size > 0) {
|
||||
val manga = it["included"]!!.jsonArray[0].jsonObject
|
||||
KitsuLibAnime(data[0].jsonObject, manga).toTrack()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
||||
|
@ -191,6 +292,27 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getLibAnime(track: AnimeTrack): AnimeTrack {
|
||||
return withIOContext {
|
||||
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
||||
.encodedQuery("filter[id]=${track.media_id}")
|
||||
.appendQueryParameter("include", "manga")
|
||||
.build()
|
||||
authClient.newCall(GET(url.toString()))
|
||||
.await()
|
||||
.parseAs<JsonObject>()
|
||||
.let {
|
||||
val data = it["data"]!!.jsonArray
|
||||
if (data.size > 0) {
|
||||
val anime = it["included"]!!.jsonArray[0].jsonObject
|
||||
KitsuLibAnime(data[0].jsonObject, anime).toTrack()
|
||||
} else {
|
||||
throw Exception("Could not find manga")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun login(username: String, password: String): OAuth {
|
||||
return withIOContext {
|
||||
val formBody: RequestBody = FormBody.Builder()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package eu.kanade.tachiyomi.data.track.kitsu
|
||||
|
||||
import androidx.annotation.CallSuper
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
|
@ -88,6 +90,44 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
|
|||
}
|
||||
}
|
||||
|
||||
class KitsuLibAnime(obj: JsonObject, anime: JsonObject) {
|
||||
val id = anime["id"]!!.jsonPrimitive.int
|
||||
private val canonicalTitle = anime["attributes"]!!.jsonObject["canonicalTitle"]!!.jsonPrimitive.content
|
||||
private val episodeCount = anime["attributes"]!!.jsonObject["episodeCount"]?.jsonPrimitive?.intOrNull
|
||||
val type = anime["attributes"]!!.jsonObject["animeType"]?.jsonPrimitive?.contentOrNull.orEmpty()
|
||||
val original = anime["attributes"]!!.jsonObject["posterImage"]!!.jsonObject["original"]!!.jsonPrimitive.content
|
||||
private val synopsis = anime["attributes"]!!.jsonObject["synopsis"]!!.jsonPrimitive.content
|
||||
private val startDate = anime["attributes"]!!.jsonObject["startDate"]?.jsonPrimitive?.contentOrNull.orEmpty()
|
||||
private val libraryId = obj["id"]!!.jsonPrimitive.int
|
||||
val status = obj["attributes"]!!.jsonObject["status"]!!.jsonPrimitive.content
|
||||
private val ratingTwenty = obj["attributes"]!!.jsonObject["ratingTwenty"]?.jsonPrimitive?.contentOrNull
|
||||
val progress = obj["attributes"]!!.jsonObject["progress"]!!.jsonPrimitive.int
|
||||
|
||||
fun toTrack() = AnimeTrackSearch.create(TrackManager.KITSU).apply {
|
||||
media_id = libraryId
|
||||
title = canonicalTitle
|
||||
total_episodes = episodeCount ?: 0
|
||||
cover_url = original
|
||||
summary = synopsis
|
||||
tracking_url = KitsuApi.mangaUrl(media_id)
|
||||
publishing_status = this@KitsuLibAnime.status
|
||||
publishing_type = type
|
||||
start_date = startDate
|
||||
status = toTrackStatus()
|
||||
score = ratingTwenty?.let { it.toInt() / 2f } ?: 0f
|
||||
last_episode_seen = progress
|
||||
}
|
||||
|
||||
private fun toTrackStatus() = when (status) {
|
||||
"current" -> Kitsu.READING
|
||||
"completed" -> Kitsu.COMPLETED
|
||||
"on_hold" -> Kitsu.ON_HOLD
|
||||
"dropped" -> Kitsu.DROPPED
|
||||
"planned" -> Kitsu.PLAN_TO_READ
|
||||
else -> throw Exception("Unknown status")
|
||||
}
|
||||
}
|
||||
|
||||
fun Track.toKitsuStatus() = when (status) {
|
||||
Kitsu.READING -> "current"
|
||||
Kitsu.COMPLETED -> "completed"
|
||||
|
@ -100,3 +140,16 @@ fun Track.toKitsuStatus() = when (status) {
|
|||
fun Track.toKitsuScore(): String? {
|
||||
return if (score > 0) (score * 2).toInt().toString() else null
|
||||
}
|
||||
|
||||
fun AnimeTrack.toKitsuStatus() = when (status) {
|
||||
Kitsu.READING -> "current"
|
||||
Kitsu.COMPLETED -> "completed"
|
||||
Kitsu.ON_HOLD -> "on_hold"
|
||||
Kitsu.DROPPED -> "dropped"
|
||||
Kitsu.PLAN_TO_READ -> "planned"
|
||||
else -> throw Exception("Unknown status")
|
||||
}
|
||||
|
||||
fun AnimeTrack.toKitsuScore(): String? {
|
||||
return if (score > 0) (score * 2).toInt().toString() else null
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package eu.kanade.tachiyomi.data.track.model
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
|
||||
class AnimeTrackSearch : AnimeTrack {
|
||||
|
||||
override var id: Long? = null
|
||||
|
||||
override var anime_id: Long = 0
|
||||
|
||||
override var sync_id: Int = 0
|
||||
|
||||
override var media_id: Int = 0
|
||||
|
||||
override var library_id: Long? = null
|
||||
|
||||
override lateinit var title: String
|
||||
|
||||
override var last_episode_seen: Int = 0
|
||||
|
||||
override var total_episodes: Int = 0
|
||||
|
||||
override var score: Float = 0f
|
||||
|
||||
override var status: Int = 0
|
||||
|
||||
override var started_watching_date: Long = 0
|
||||
|
||||
override var finished_watching_date: Long = 0
|
||||
|
||||
override lateinit var tracking_url: String
|
||||
|
||||
var cover_url: String = ""
|
||||
|
||||
var summary: String = ""
|
||||
|
||||
var publishing_status: String = ""
|
||||
|
||||
var publishing_type: String = ""
|
||||
|
||||
var start_date: String = ""
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
|
||||
other as AnimeTrack
|
||||
|
||||
if (anime_id != other.anime_id) return false
|
||||
if (sync_id != other.sync_id) return false
|
||||
return media_id == other.media_id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = (anime_id xor anime_id.ushr(32)).toInt()
|
||||
result = 31 * result + sync_id
|
||||
result = 31 * result + media_id
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(serviceId: Int): AnimeTrackSearch = AnimeTrackSearch().apply {
|
||||
sync_id = serviceId
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import android.graphics.Color
|
|||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
|
@ -66,6 +67,10 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) {
|
|||
return track.score.toInt().toString()
|
||||
}
|
||||
|
||||
override fun displayScore(track: AnimeTrack): String {
|
||||
return track.score.toInt().toString()
|
||||
}
|
||||
|
||||
override suspend fun add(track: Track): Track {
|
||||
track.status = READING
|
||||
track.score = 0F
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.Color
|
|||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
|
@ -40,6 +41,10 @@ class Shikimori(private val context: Context, id: Int) : TrackService(id) {
|
|||
return track.score.toInt().toString()
|
||||
}
|
||||
|
||||
override fun displayScore(track: AnimeTrack): String {
|
||||
return track.score.toInt().toString()
|
||||
}
|
||||
|
||||
override suspend fun add(track: Track): Track {
|
||||
return api.addLibManga(track, getUsername())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package eu.kanade.tachiyomi.source
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import rx.Observable
|
||||
|
||||
interface AnimeCatalogueSource : AnimeSource {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package eu.kanade.tachiyomi.source
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
||||
import rx.Observable
|
||||
import tachiyomi.source.model.EpisodeInfo
|
||||
import tachiyomi.source.model.AnimeInfo
|
||||
import tachiyomi.source.model.EpisodeInfo
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package eu.kanade.tachiyomi.source
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
|
||||
import rx.Observable
|
||||
|
||||
open class AnimeSourceManager(private val context: Context) {
|
||||
|
||||
private val sourcesMap = mutableMapOf<Long, AnimeSource>()
|
||||
|
||||
private val stubSourcesMap = mutableMapOf<Long, StubSource>()
|
||||
|
||||
init {
|
||||
createInternalSources().forEach { registerSource(it) }
|
||||
}
|
||||
|
||||
open fun get(sourceKey: Long): AnimeSource? {
|
||||
return sourcesMap[sourceKey]
|
||||
}
|
||||
|
||||
fun getOrStub(sourceKey: Long): AnimeSource {
|
||||
return sourcesMap[sourceKey] ?: stubSourcesMap.getOrPut(sourceKey) {
|
||||
StubSource(sourceKey)
|
||||
}
|
||||
}
|
||||
|
||||
fun getOnlineSources() = sourcesMap.values.filterIsInstance<AnimeHttpSource>()
|
||||
|
||||
fun getCatalogueSources() = sourcesMap.values.filterIsInstance<AnimeCatalogueSource>()
|
||||
|
||||
internal fun registerSource(source: AnimeSource) {
|
||||
if (!sourcesMap.containsKey(source.id)) {
|
||||
sourcesMap[source.id] = source
|
||||
}
|
||||
if (!stubSourcesMap.containsKey(source.id)) {
|
||||
stubSourcesMap[source.id] = StubSource(source.id)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun unregisterSource(source: AnimeSource) {
|
||||
sourcesMap.remove(source.id)
|
||||
}
|
||||
|
||||
private fun createInternalSources(): List<AnimeSource> = listOf(
|
||||
LocalAnimeSource(context)
|
||||
)
|
||||
|
||||
inner class StubSource(override val id: Long) : AnimeSource {
|
||||
|
||||
override val name: String
|
||||
get() = id.toString()
|
||||
|
||||
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
||||
return Observable.error(getSourceNotInstalledException())
|
||||
}
|
||||
|
||||
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
||||
return Observable.error(getSourceNotInstalledException())
|
||||
}
|
||||
|
||||
override fun fetchPageList(episode: SEpisode): Observable<List<Page>> {
|
||||
return Observable.error(getSourceNotInstalledException())
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
|
||||
private fun getSourceNotInstalledException(): Exception {
|
||||
return Exception(context.getString(R.string.source_not_installed, id.toString()))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,4 +43,4 @@ interface CatalogueSource : Source {
|
|||
* Returns the list of filters for the source.
|
||||
*/
|
||||
fun getFilterList(): FilterList
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@ import android.content.Context
|
|||
import com.github.junrar.Archive
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.util.episode.EpisodeRecognition
|
||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.AnimeFile
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import rx.Observable
|
||||
import timber.log.Timber
|
||||
|
|
|
@ -74,13 +74,13 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||
|
||||
val time = if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L
|
||||
var mangaDirs = baseDirs
|
||||
.asSequence()
|
||||
.mapNotNull { it.listFiles()?.toList() }
|
||||
.flatten()
|
||||
.filter { it.isDirectory }
|
||||
.filterNot { it.name.startsWith('.') }
|
||||
.filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time }
|
||||
.distinctBy { it.name }
|
||||
.asSequence()
|
||||
.mapNotNull { it.listFiles()?.toList() }
|
||||
.flatten()
|
||||
.filter { it.isDirectory }
|
||||
.filterNot { it.name.startsWith('.') }
|
||||
.filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time }
|
||||
.distinctBy { it.name }
|
||||
|
||||
val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state
|
||||
when (state?.index) {
|
||||
|
@ -144,61 +144,61 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
getBaseDirectories(context)
|
||||
.asSequence()
|
||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||
.flatten()
|
||||
.firstOrNull { it.extension == "json" }
|
||||
?.apply {
|
||||
val reader = this.inputStream().bufferedReader()
|
||||
val json = JsonParser.parseReader(reader).asJsonObject
|
||||
.asSequence()
|
||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||
.flatten()
|
||||
.firstOrNull { it.extension == "json" }
|
||||
?.apply {
|
||||
val reader = this.inputStream().bufferedReader()
|
||||
val json = JsonParser.parseReader(reader).asJsonObject
|
||||
|
||||
manga.title = json["title"]?.asString ?: manga.title
|
||||
manga.author = json["author"]?.asString ?: manga.author
|
||||
manga.artist = json["artist"]?.asString ?: manga.artist
|
||||
manga.description = json["description"]?.asString ?: manga.description
|
||||
manga.genre = json["genre"]?.asJsonArray?.joinToString(", ") { it.asString }
|
||||
?: manga.genre
|
||||
manga.status = json["status"]?.asInt ?: manga.status
|
||||
}
|
||||
manga.title = json["title"]?.asString ?: manga.title
|
||||
manga.author = json["author"]?.asString ?: manga.author
|
||||
manga.artist = json["artist"]?.asString ?: manga.artist
|
||||
manga.description = json["description"]?.asString ?: manga.description
|
||||
manga.genre = json["genre"]?.asJsonArray?.joinToString(", ") { it.asString }
|
||||
?: manga.genre
|
||||
manga.status = json["status"]?.asInt ?: manga.status
|
||||
}
|
||||
|
||||
return Observable.just(manga)
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val chapters = getBaseDirectories(context)
|
||||
.asSequence()
|
||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||
.flatten()
|
||||
.filter { it.isDirectory || isSupportedFile(it.extension) }
|
||||
.map { chapterFile ->
|
||||
SChapter.create().apply {
|
||||
url = "${manga.url}/${chapterFile.name}"
|
||||
name = if (chapterFile.isDirectory) {
|
||||
chapterFile.name
|
||||
} else {
|
||||
chapterFile.nameWithoutExtension
|
||||
}
|
||||
date_upload = chapterFile.lastModified()
|
||||
|
||||
val format = getFormat(this)
|
||||
if (format is Format.Epub) {
|
||||
EpubFile(format.file).use { epub ->
|
||||
epub.fillChapterMetadata(this)
|
||||
}
|
||||
}
|
||||
|
||||
val chapNameCut = stripMangaTitle(name, manga.title)
|
||||
if (chapNameCut.isNotEmpty()) name = chapNameCut
|
||||
ChapterRecognition.parseChapterNumber(this, manga)
|
||||
.asSequence()
|
||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||
.flatten()
|
||||
.filter { it.isDirectory || isSupportedFile(it.extension) }
|
||||
.map { chapterFile ->
|
||||
SChapter.create().apply {
|
||||
url = "${manga.url}/${chapterFile.name}"
|
||||
name = if (chapterFile.isDirectory) {
|
||||
chapterFile.name
|
||||
} else {
|
||||
chapterFile.nameWithoutExtension
|
||||
}
|
||||
}
|
||||
.sortedWith(
|
||||
Comparator { c1, c2 ->
|
||||
val c = c2.chapter_number.compareTo(c1.chapter_number)
|
||||
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
|
||||
date_upload = chapterFile.lastModified()
|
||||
|
||||
val format = getFormat(this)
|
||||
if (format is Format.Epub) {
|
||||
EpubFile(format.file).use { epub ->
|
||||
epub.fillChapterMetadata(this)
|
||||
}
|
||||
)
|
||||
.toList()
|
||||
}
|
||||
|
||||
val chapNameCut = stripMangaTitle(name, manga.title)
|
||||
if (chapNameCut.isNotEmpty()) name = chapNameCut
|
||||
ChapterRecognition.parseChapterNumber(this, manga)
|
||||
}
|
||||
}
|
||||
.sortedWith(
|
||||
Comparator { c1, c2 ->
|
||||
val c = c2.chapter_number.compareTo(c1.chapter_number)
|
||||
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
|
||||
}
|
||||
)
|
||||
.toList()
|
||||
|
||||
return Observable.just(chapters)
|
||||
}
|
||||
|
@ -276,16 +276,16 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||
return when (val format = getFormat(chapter)) {
|
||||
is Format.Directory -> {
|
||||
val entry = format.file.listFiles()
|
||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, it.inputStream()) }
|
||||
}
|
||||
is Format.Zip -> {
|
||||
ZipFile(format.file).use { zip ->
|
||||
val entry = zip.entries().toList()
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, zip.getInputStream(it)) }
|
||||
}
|
||||
|
@ -293,8 +293,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||
is Format.Rar -> {
|
||||
Archive(format.file).use { archive ->
|
||||
val entry = archive.fileHeaders
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, archive.getInputStream(it)) }
|
||||
}
|
||||
|
@ -302,8 +302,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||
is Format.Epub -> {
|
||||
EpubFile(format.file).use { epub ->
|
||||
val entry = epub.getImagesFromPages()
|
||||
.firstOrNull()
|
||||
?.let { epub.getEntry(it) }
|
||||
.firstOrNull()
|
||||
?.let { epub.getEntry(it) }
|
||||
|
||||
entry?.let { updateCover(context, manga, epub.getInputStream(it)) }
|
||||
}
|
||||
|
@ -321,4 +321,4 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||
data class Rar(val file: File) : Format()
|
||||
data class Epub(val file: File) : Format()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ interface Source : tachiyomi.source.Source {
|
|||
@Suppress("DEPRECATION")
|
||||
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> {
|
||||
return fetchChapterList(manga.toSManga()).awaitSingle()
|
||||
.map { it.toChapterInfo() }
|
||||
.map { it.toChapterInfo() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,10 +85,10 @@ interface Source : tachiyomi.source.Source {
|
|||
@Suppress("DEPRECATION")
|
||||
override suspend fun getPageList(chapter: ChapterInfo): List<tachiyomi.source.model.Page> {
|
||||
return fetchPageList(chapter.toSChapter()).awaitSingle()
|
||||
.map { it.toPageUrl() }
|
||||
.map { it.toPageUrl() }
|
||||
}
|
||||
}
|
||||
|
||||
fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this)
|
||||
|
||||
fun Source.getPreferenceKey(): String = "source_$id"
|
||||
fun Source.getPreferenceKey(): String = "source_$id"
|
||||
|
|
|
@ -4,12 +4,12 @@ import eu.kanade.tachiyomi.network.GET
|
|||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.network.newCallWithProgress
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.AnimeCatalogueSource
|
||||
import eu.kanade.tachiyomi.source.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
@ -23,7 +23,7 @@ import java.security.MessageDigest
|
|||
/**
|
||||
* A simple implementation for sources from a website.
|
||||
*/
|
||||
abstract class AnimeHttpSource : CatalogueSource {
|
||||
abstract class AnimeHttpSource : AnimeCatalogueSource {
|
||||
|
||||
/**
|
||||
* Network service.
|
||||
|
@ -147,7 +147,7 @@ abstract class AnimeHttpSource : CatalogueSource {
|
|||
*
|
||||
* @param page the page number to retrieve.
|
||||
*/
|
||||
override fun fetchLatestAnimeUpdates(page: Int): Observable<AnimesPage> {
|
||||
override fun fetchLatestUpdates(page: Int): Observable<AnimesPage> {
|
||||
return client.newCall(latestUpdatesRequest(page))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
|
@ -240,7 +240,7 @@ abstract class AnimeHttpSource : CatalogueSource {
|
|||
*
|
||||
* @param episode the episode whose page list has to be fetched.
|
||||
*/
|
||||
override fun fetchAnimePageList(episode: SEpisode): Observable<List<Page>> {
|
||||
override fun fetchPageList(episode: SEpisode): Observable<List<Page>> {
|
||||
return client.newCall(pageListRequest(episode))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
|
|
|
@ -32,18 +32,32 @@ import eu.davidea.flexibleadapter.SelectableAdapter
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.AnimeControllerBinding
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.AnimeSource
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.AnimeEpisodesHeaderAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.DeleteEpisodesDialog
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.DownloadCustomEpisodesDialog
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodeItem
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodesAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodesSettingsSheet
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.base.BaseEpisodesAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.info.AnimeInfoHeaderAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackItem
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackSearchDialog
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackSheet
|
||||
import eu.kanade.tachiyomi.ui.animelib.AnimelibController
|
||||
import eu.kanade.tachiyomi.ui.animelib.ChangeAnimeCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.animelib.ChangeAnimeCoverDialog
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FabController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController
|
||||
|
@ -52,24 +66,10 @@ import eu.kanade.tachiyomi.ui.browse.migration.search.AnimeSearchController
|
|||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
|
||||
import eu.kanade.tachiyomi.ui.animelib.ChangeAnimeCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.animelib.ChangeAnimeCoverDialog
|
||||
import eu.kanade.tachiyomi.ui.animelib.AnimelibController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodeItem
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodesAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodesSettingsSheet
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.DeleteEpisodesDialog
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.DownloadCustomEpisodesDialog
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.AnimeEpisodesHeaderAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.base.BaseEpisodesAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.info.AnimeInfoHeaderAdapter
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackItem
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackSearchDialog
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackSheet
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherActivity
|
||||
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
|
||||
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherActivity
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.episode.NoEpisodesException
|
||||
import eu.kanade.tachiyomi.util.hasCustomCover
|
||||
|
@ -111,7 +111,7 @@ class AnimeController :
|
|||
) {
|
||||
this.anime = anime
|
||||
if (anime != null) {
|
||||
source = Injekt.get<SourceManager>().getOrStub(anime.source)
|
||||
source = Injekt.get<AnimeSourceManager>().getOrStub(anime.source)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ class AnimeController :
|
|||
var anime: Anime? = null
|
||||
private set
|
||||
|
||||
var source: Source? = null
|
||||
var source: AnimeSource? = null
|
||||
private set
|
||||
|
||||
private val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false)
|
||||
|
@ -401,7 +401,7 @@ class AnimeController :
|
|||
* @param anime anime object containing information about anime.
|
||||
* @param source the source of the anime.
|
||||
*/
|
||||
fun onNextAnimeInfo(anime: Anime, source: Source) {
|
||||
fun onNextAnimeInfo(anime: Anime, source: AnimeSource) {
|
||||
if (anime.initialized) {
|
||||
// Update view.
|
||||
animeInfoAdapter?.update(anime, source)
|
||||
|
@ -943,7 +943,7 @@ class AnimeController :
|
|||
}
|
||||
|
||||
private fun downloadEpisodes(episodes: List<EpisodeItem>) {
|
||||
if (source is SourceManager.StubSource) {
|
||||
if (source is AnimeSourceManager.StubSource) {
|
||||
activity?.toast(R.string.loader_not_implemented_error)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,24 +6,24 @@ import android.os.Bundle
|
|||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.toAnimeInfo
|
||||
import eu.kanade.tachiyomi.data.download.AnimeDownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.model.AnimeDownload
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.toSEpisode
|
||||
import eu.kanade.tachiyomi.source.LocalAnimeSource
|
||||
import eu.kanade.tachiyomi.source.AnimeSource
|
||||
import eu.kanade.tachiyomi.source.model.toSAnime
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.source.model.toSEpisode
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.EpisodeItem
|
||||
import eu.kanade.tachiyomi.ui.anime.track.TrackItem
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.*
|
||||
import eu.kanade.tachiyomi.util.episode.EpisodeSettingsHelper
|
||||
import eu.kanade.tachiyomi.util.episode.syncEpisodesWithSource
|
||||
|
@ -46,7 +46,7 @@ import java.util.Date
|
|||
|
||||
class AnimePresenter(
|
||||
val anime: Anime,
|
||||
val source: Source,
|
||||
val source: AnimeSource,
|
||||
val preferences: PreferencesHelper = Injekt.get(),
|
||||
private val db: AnimeDatabaseHelper = Injekt.get(),
|
||||
private val trackManager: TrackManager = Injekt.get(),
|
||||
|
@ -275,7 +275,7 @@ class AnimePresenter(
|
|||
.fromCallable {
|
||||
context.contentResolver.openInputStream(data)?.use {
|
||||
if (anime.isLocal()) {
|
||||
LocalSource.updateCover(context, anime, it)
|
||||
LocalAnimeSource.updateCover(context, anime, it)
|
||||
anime.updateCoverLastModified(db)
|
||||
} else if (anime.favorite) {
|
||||
coverCache.setCustomCoverToCache(anime, it)
|
||||
|
@ -544,7 +544,7 @@ class AnimePresenter(
|
|||
}
|
||||
|
||||
private fun downloadNewEpisodes(episodes: List<Episode>) {
|
||||
if (episodes.isEmpty() || !anime.shouldDownloadNewChapters(db, preferences)) return
|
||||
if (episodes.isEmpty() || !anime.shouldDownloadNewEpisodes(db, preferences)) return
|
||||
|
||||
downloadEpisodes(episodes)
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
|||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.ui.anime.episode.base.BaseEpisodeItem
|
||||
|
||||
class EpisodeItem(episode: Episode, val anime: Anime) :
|
||||
|
@ -23,10 +23,10 @@ class EpisodeItem(episode: Episode, val anime: Anime) :
|
|||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||
holder: EpisodeHolder,
|
||||
position: Int,
|
||||
payloads: List<Any?>?
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||
holder: EpisodeHolder,
|
||||
position: Int,
|
||||
payloads: List<Any?>?
|
||||
) {
|
||||
holder.bind(this, anime)
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import java.text.DecimalFormat
|
|||
import java.text.DecimalFormatSymbols
|
||||
|
||||
class EpisodesAdapter(
|
||||
controller: AnimeController,
|
||||
context: Context
|
||||
controller: AnimeController,
|
||||
context: Context
|
||||
) : BaseEpisodesAdapter<EpisodeItem>(controller) {
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
|
|
@ -14,9 +14,9 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Stat
|
|||
import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog
|
||||
|
||||
class EpisodesSettingsSheet(
|
||||
private val router: Router,
|
||||
private val presenter: AnimePresenter,
|
||||
onGroupClickListener: (ExtendedNavigationView.Group) -> Unit
|
||||
private val router: Router,
|
||||
private val presenter: AnimePresenter,
|
||||
onGroupClickListener: (ExtendedNavigationView.Group) -> Unit
|
||||
) : TabbedBottomSheetDialog(router.activity!!) {
|
||||
|
||||
val filters: Filter
|
||||
|
|
|
@ -10,13 +10,13 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.glide.AnimeThumbnail
|
||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.glide.toAnimeThumbnail
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.databinding.AnimeInfoHeaderBinding
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.AnimeSource
|
||||
import eu.kanade.tachiyomi.source.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
|
@ -33,15 +33,15 @@ import uy.kohesive.injekt.api.get
|
|||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class AnimeInfoHeaderAdapter(
|
||||
private val controller: AnimeController,
|
||||
private val fromSource: Boolean
|
||||
private val controller: AnimeController,
|
||||
private val fromSource: Boolean
|
||||
) :
|
||||
RecyclerView.Adapter<AnimeInfoHeaderAdapter.HeaderViewHolder>() {
|
||||
|
||||
private val trackManager: TrackManager by injectLazy()
|
||||
|
||||
private var anime: Anime = controller.presenter.anime
|
||||
private var source: Source = controller.presenter.source
|
||||
private var source: AnimeSource = controller.presenter.source
|
||||
private var trackCount: Int = 0
|
||||
|
||||
private lateinit var binding: AnimeInfoHeaderBinding
|
||||
|
@ -66,7 +66,7 @@ class AnimeInfoHeaderAdapter(
|
|||
* @param anime anime object containing information about anime.
|
||||
* @param source the source of the anime.
|
||||
*/
|
||||
fun update(anime: Anime, source: Source) {
|
||||
fun update(anime: Anime, source: AnimeSource) {
|
||||
this.anime = anime
|
||||
this.source = source
|
||||
|
||||
|
@ -199,7 +199,7 @@ class AnimeInfoHeaderAdapter(
|
|||
* @param anime anime object containing information about anime.
|
||||
* @param source the source of the anime.
|
||||
*/
|
||||
private fun setAnimeInfo(anime: Anime, source: Source?) {
|
||||
private fun setAnimeInfo(anime: Anime, source: AnimeSource?) {
|
||||
// Update full title TextView.
|
||||
binding.animeFullTitle.text = if (anime.title.isBlank()) {
|
||||
view.context.getString(R.string.unknown)
|
||||
|
@ -227,7 +227,7 @@ class AnimeInfoHeaderAdapter(
|
|||
if (animeSource != null) {
|
||||
text = animeSource
|
||||
setOnClickListener {
|
||||
val sourceManager = Injekt.get<SourceManager>()
|
||||
val sourceManager = Injekt.get<AnimeSourceManager>()
|
||||
controller.performSearch(sourceManager.getOrStub(source.id).name)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -7,7 +7,7 @@ import com.afollestad.materialdialogs.MaterialDialog
|
|||
import com.afollestad.materialdialogs.datetime.datePicker
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -34,7 +34,7 @@ class SetTrackWatchingDatesDialog<T> : DialogController
|
|||
|
||||
@Suppress("unused")
|
||||
constructor(bundle: Bundle) : super(bundle) {
|
||||
val track = bundle.getSerializable(KEY_ITEM_TRACK) as Track
|
||||
val track = bundle.getSerializable(KEY_ITEM_TRACK) as AnimeTrack
|
||||
val service = Injekt.get<TrackManager>().getService(track.sync_id)!!
|
||||
item = TrackItem(track, service)
|
||||
dateToUpdate = ReadingDate.Start
|
||||
|
@ -61,8 +61,8 @@ class SetTrackWatchingDatesDialog<T> : DialogController
|
|||
return Calendar.getInstance().apply {
|
||||
item.track?.let {
|
||||
val date = when (dateToUpdate) {
|
||||
ReadingDate.Start -> it.started_reading_date
|
||||
ReadingDate.Finish -> it.finished_reading_date
|
||||
ReadingDate.Start -> it.started_watching_date
|
||||
ReadingDate.Finish -> it.finished_watching_date
|
||||
}
|
||||
if (date != 0L) {
|
||||
timeInMillis = date
|
||||
|
|
|
@ -27,7 +27,7 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
|
|||
true
|
||||
}
|
||||
binding.trackStatus.setOnClickListener { listener.onStatusClick(bindingAdapterPosition) }
|
||||
binding.trackChapters.setOnClickListener { listener.onChaptersClick(bindingAdapterPosition) }
|
||||
binding.trackChapters.setOnClickListener { listener.onEpisodesClick(bindingAdapterPosition) }
|
||||
binding.trackScore.setOnClickListener { listener.onScoreClick(bindingAdapterPosition) }
|
||||
binding.trackStartDate.setOnClickListener { listener.onStartDateClick(bindingAdapterPosition) }
|
||||
binding.trackFinishDate.setOnClickListener { listener.onFinishDateClick(bindingAdapterPosition) }
|
||||
|
@ -45,16 +45,16 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
|
|||
binding.trackDetails.isVisible = track != null
|
||||
if (track != null) {
|
||||
binding.trackTitle.text = track.title
|
||||
binding.trackChapters.text = "${track.last_chapter_read}/" +
|
||||
if (track.total_chapters > 0) track.total_chapters else "-"
|
||||
binding.trackChapters.text = "${track.last_episode_seen}/" +
|
||||
if (track.total_episodes > 0) track.total_episodes else "-"
|
||||
binding.trackStatus.text = item.service.getStatus(track.status)
|
||||
binding.trackScore.text = if (track.score == 0f) "-" else item.service.displayScore(track)
|
||||
|
||||
if (item.service.supportsReadingDates) {
|
||||
binding.trackStartDate.text =
|
||||
if (track.started_reading_date != 0L) dateFormat.format(track.started_reading_date) else "-"
|
||||
if (track.started_watching_date != 0L) dateFormat.format(track.started_watching_date) else "-"
|
||||
binding.trackFinishDate.text =
|
||||
if (track.finished_reading_date != 0L) dateFormat.format(track.finished_reading_date) else "-"
|
||||
if (track.finished_watching_date != 0L) dateFormat.format(track.finished_watching_date) else "-"
|
||||
} else {
|
||||
binding.bottomDivider.isVisible = false
|
||||
binding.vertDivider3.isVisible = false
|
||||
|
|
|
@ -8,12 +8,12 @@ import androidx.core.view.isVisible
|
|||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.TrackSearchItemBinding
|
||||
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.AnimeTrackSearchItemBinding
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
|
||||
class TrackSearchAdapter(context: Context) :
|
||||
ArrayAdapter<TrackSearch>(context, R.layout.track_search_item, mutableListOf<TrackSearch>()) {
|
||||
ArrayAdapter<AnimeTrackSearch>(context, R.layout.track_search_item, mutableListOf<AnimeTrackSearch>()) {
|
||||
|
||||
override fun getView(position: Int, view: View?, parent: ViewGroup): View {
|
||||
var v = view
|
||||
|
@ -32,7 +32,7 @@ class TrackSearchAdapter(context: Context) :
|
|||
return v
|
||||
}
|
||||
|
||||
fun setItems(syncs: List<TrackSearch>) {
|
||||
fun setItems(syncs: List<AnimeTrackSearch>) {
|
||||
setNotifyOnChange(false)
|
||||
clear()
|
||||
addAll(syncs)
|
||||
|
@ -41,9 +41,9 @@ class TrackSearchAdapter(context: Context) :
|
|||
|
||||
class TrackSearchHolder(private val view: View) {
|
||||
|
||||
private val binding = TrackSearchItemBinding.bind(view)
|
||||
private val binding = AnimeTrackSearchItemBinding.bind(view)
|
||||
|
||||
fun onSetValues(track: TrackSearch) {
|
||||
fun onSetValues(track: AnimeTrackSearch) {
|
||||
binding.trackSearchTitle.text = track.title
|
||||
binding.trackSearchSummary.text = track.summary
|
||||
GlideApp.with(view.context).clear(binding.trackSearchCover)
|
||||
|
|
|
@ -9,13 +9,13 @@ import androidx.core.view.isVisible
|
|||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.TrackSearchDialogBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.AnimeTrackSearchDialogBinding
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
@ -28,11 +28,11 @@ import java.util.concurrent.TimeUnit
|
|||
|
||||
class TrackSearchDialog : DialogController {
|
||||
|
||||
private var binding: TrackSearchDialogBinding? = null
|
||||
private var binding: AnimeTrackSearchDialogBinding? = null
|
||||
|
||||
private var adapter: TrackSearchAdapter? = null
|
||||
|
||||
private var selectedItem: Track? = null
|
||||
private var selectedItem: AnimeTrack? = null
|
||||
|
||||
private val service: TrackService
|
||||
|
||||
|
@ -52,7 +52,7 @@ class TrackSearchDialog : DialogController {
|
|||
}
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = TrackSearchDialogBinding.inflate(LayoutInflater.from(activity!!))
|
||||
binding = AnimeTrackSearchDialogBinding.inflate(LayoutInflater.from(activity!!))
|
||||
val dialog = MaterialDialog(activity!!)
|
||||
.customView(view = binding!!.root)
|
||||
.positiveButton(android.R.string.ok) { onPositiveButtonClick() }
|
||||
|
@ -109,7 +109,7 @@ class TrackSearchDialog : DialogController {
|
|||
trackController.presenter.trackingSearch(query, service)
|
||||
}
|
||||
|
||||
fun onSearchResults(results: List<TrackSearch>) {
|
||||
fun onSearchResults(results: List<AnimeTrackSearch>) {
|
||||
selectedItem = null
|
||||
val binding = binding ?: return
|
||||
binding.progress.isVisible = false
|
||||
|
|
|
@ -6,14 +6,14 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.databinding.TrackControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
|
||||
|
||||
class TrackSheet(
|
||||
val controller: AnimeController,
|
||||
val anime: Anime
|
||||
val controller: AnimeController,
|
||||
val anime: Anime
|
||||
) : BaseBottomSheetDialog(controller.activity!!),
|
||||
TrackAdapter.OnClickListener,
|
||||
SetTrackStatusDialog.Listener,
|
||||
|
|
|
@ -7,8 +7,8 @@ import eu.kanade.tachiyomi.data.database.models.AnimeCategory
|
|||
import eu.kanade.tachiyomi.data.database.models.toAnimeInfo
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.model.toSEpisode
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.AnimeMigrationFlags
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalAnimeSearchCardItem
|
||||
|
@ -80,15 +80,15 @@ class AnimeSearchPresenter(
|
|||
) {
|
||||
val flags = preferences.migrateFlags().get()
|
||||
val migrateEpisodes =
|
||||
AnimeMigrationFlags.hasEpisodes(
|
||||
AnimeMigrationFlags.hasEpisodes(
|
||||
flags
|
||||
)
|
||||
val migrateCategories =
|
||||
AnimeMigrationFlags.hasCategories(
|
||||
AnimeMigrationFlags.hasCategories(
|
||||
flags
|
||||
)
|
||||
val migrateTracks =
|
||||
AnimeMigrationFlags.hasTracks(
|
||||
AnimeMigrationFlags.hasTracks(
|
||||
flags
|
||||
)
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.AnimeSourceItem
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
||||
|
||||
class AnimeSourceSearchController(
|
||||
bundle: Bundle
|
||||
|
@ -28,7 +28,7 @@ class AnimeSourceSearchController(
|
|||
newAnime = item.anime
|
||||
val searchController = router.backstack.findLast { it.controller().javaClass == AnimeSearchController::class.java }?.controller() as AnimeSearchController?
|
||||
val dialog =
|
||||
AnimeSearchController.MigrationDialog(oldAnime, newAnime, this)
|
||||
AnimeSearchController.MigrationDialog(oldAnime, newAnime, this)
|
||||
dialog.targetController = searchController
|
||||
dialog.showDialog(router)
|
||||
return true
|
||||
|
|
|
@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.database.models.Anime
|
|||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.glide.toAnimeThumbnail
|
||||
import eu.kanade.tachiyomi.databinding.AnimeSourceComfortableGridItemBinding
|
||||
import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding
|
||||
import eu.kanade.tachiyomi.widget.StateImageViewTarget
|
||||
|
||||
/**
|
||||
|
@ -19,7 +18,7 @@ import eu.kanade.tachiyomi.widget.StateImageViewTarget
|
|||
* @constructor creates a new catalogue holder.
|
||||
*/
|
||||
class AnimeSourceComfortableGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
||||
AnimeSourceHolder<AnimeSourceComfortableGridItemBinding>(view, adapter) {
|
||||
AnimeSourceHolder<AnimeSourceComfortableGridItemBinding>(view, adapter) {
|
||||
|
||||
override val binding = AnimeSourceComfortableGridItemBinding.bind(view)
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.database.models.Anime
|
|||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.glide.toAnimeThumbnail
|
||||
import eu.kanade.tachiyomi.databinding.AnimeSourceComfortableGridItemBinding
|
||||
import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding
|
||||
import eu.kanade.tachiyomi.widget.StateImageViewTarget
|
||||
|
||||
/**
|
||||
|
@ -19,7 +18,7 @@ import eu.kanade.tachiyomi.widget.StateImageViewTarget
|
|||
* @constructor creates a new catalogue holder.
|
||||
*/
|
||||
open class AnimeSourceGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
||||
AnimeSourceHolder<AnimeSourceComfortableGridItemBinding>(view, adapter) {
|
||||
AnimeSourceHolder<AnimeSourceComfortableGridItemBinding>(view, adapter) {
|
||||
|
||||
override val binding = AnimeSourceComfortableGridItemBinding.bind(view)
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import eu.kanade.tachiyomi.data.database.models.Anime
|
|||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.data.glide.toAnimeThumbnail
|
||||
import eu.kanade.tachiyomi.databinding.AnimeSourceListItemBinding
|
||||
import eu.kanade.tachiyomi.databinding.SourceListItemBinding
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
|
||||
/**
|
||||
|
@ -23,7 +22,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
|
|||
* @constructor creates a new catalogue holder.
|
||||
*/
|
||||
class AnimeSourceListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
|
||||
AnimeSourceHolder<AnimeSourceListItemBinding>(view, adapter) {
|
||||
AnimeSourceHolder<AnimeSourceListItemBinding>(view, adapter) {
|
||||
|
||||
override val binding = AnimeSourceListItemBinding.bind(view)
|
||||
|
||||
|
|
|
@ -15,12 +15,11 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.GlobalAnimeSearchControllerBinding
|
||||
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,8 +9,8 @@ import eu.kanade.tachiyomi.extension.ExtensionManager
|
|||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.toSAnime
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.view.Menu
|
|||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
|
@ -22,6 +21,7 @@ import dev.chrisbanes.insetter.applyInsetter
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.databinding.LibraryControllerBinding
|
||||
|
@ -32,7 +32,7 @@ import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
|||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.drop
|
||||
|
@ -45,14 +45,14 @@ import uy.kohesive.injekt.Injekt
|
|||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class LibraryController(
|
||||
bundle: Bundle? = null,
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
bundle: Bundle? = null,
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
) : SearchableNucleusController<LibraryControllerBinding, LibraryPresenter>(bundle),
|
||||
RootController,
|
||||
TabbedController,
|
||||
ActionMode.Callback,
|
||||
ChangeMangaCategoriesDialog.Listener,
|
||||
DeleteLibraryMangasDialog.Listener {
|
||||
RootController,
|
||||
TabbedController,
|
||||
ActionMode.Callback,
|
||||
ChangeMangaCategoriesDialog.Listener,
|
||||
DeleteLibraryMangasDialog.Listener {
|
||||
|
||||
/**
|
||||
* Position of the active category.
|
||||
|
@ -163,34 +163,32 @@ class LibraryController(
|
|||
return LibraryPresenter()
|
||||
}
|
||||
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
binding = LibraryControllerBinding.inflate(inflater)
|
||||
override fun createBinding(inflater: LayoutInflater) = LibraryControllerBinding.inflate(inflater)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
binding.actionToolbar.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin(bottom = true)
|
||||
}
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
adapter = LibraryAdapter(this)
|
||||
binding.libraryPager.adapter = adapter
|
||||
binding.libraryPager.pageSelections()
|
||||
.onEach {
|
||||
preferences.lastUsedCategory().set(it)
|
||||
activeCategory = it
|
||||
updateTitle()
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
.onEach {
|
||||
preferences.lastUsedCategory().set(it)
|
||||
activeCategory = it
|
||||
updateTitle()
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
|
||||
getColumnsPreferenceForCurrentOrientation().asImmediateFlow { mangaPerRow = it }
|
||||
.drop(1)
|
||||
// Set again the adapter to recalculate the covers height
|
||||
.onEach { reattachAdapter() }
|
||||
.launchIn(viewScope)
|
||||
.drop(1)
|
||||
// Set again the adapter to recalculate the covers height
|
||||
.onEach { reattachAdapter() }
|
||||
.launchIn(viewScope)
|
||||
|
||||
if (selectedMangas.isNotEmpty()) {
|
||||
createActionModeIfNeeded()
|
||||
|
@ -207,12 +205,12 @@ class LibraryController(
|
|||
}
|
||||
|
||||
binding.btnGlobalSearch.clicks()
|
||||
.onEach {
|
||||
router.pushController(
|
||||
GlobalSearchController(presenter.query).withFadeTransaction()
|
||||
)
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
.onEach {
|
||||
router.pushController(
|
||||
GlobalSearchController(presenter.query).withFadeTransaction()
|
||||
)
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
|
||||
(activity as? MainActivity)?.fixViewToBottom(binding.actionToolbar)
|
||||
}
|
||||
|
@ -287,8 +285,8 @@ class LibraryController(
|
|||
// Set the categories
|
||||
adapter.categories = categories
|
||||
adapter.itemsPerCategory = adapter.categories
|
||||
.map { (it.id ?: -1) to (mangaMap[it.id]?.size ?: 0) }
|
||||
.toMap()
|
||||
.map { (it.id ?: -1) to (mangaMap[it.id]?.size ?: 0) }
|
||||
.toMap()
|
||||
|
||||
// Restore active category.
|
||||
binding.libraryPager.setCurrentItem(activeCat, false)
|
||||
|
@ -366,8 +364,8 @@ class LibraryController(
|
|||
if (actionMode == null) {
|
||||
actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
|
||||
binding.actionToolbar.show(
|
||||
actionMode!!,
|
||||
R.menu.library_selection
|
||||
actionMode!!,
|
||||
R.menu.library_selection
|
||||
) { onActionItemClicked(it!!) }
|
||||
(activity as? MainActivity)?.showBottomNav(visible = false, collapse = true)
|
||||
}
|
||||
|
@ -395,7 +393,7 @@ class LibraryController(
|
|||
if (presenter.query.isNotEmpty()) {
|
||||
binding.btnGlobalSearch.isVisible = true
|
||||
binding.btnGlobalSearch.text =
|
||||
resources?.getString(R.string.action_global_search_query, presenter.query)
|
||||
resources?.getString(R.string.action_global_search_query, presenter.query)
|
||||
} else {
|
||||
binding.btnGlobalSearch.isVisible = false
|
||||
}
|
||||
|
@ -417,7 +415,7 @@ class LibraryController(
|
|||
when (item.itemId) {
|
||||
R.id.action_search -> expandActionViewFromInteraction = true
|
||||
R.id.action_filter -> showSettingsSheet()
|
||||
R.id.action_update_animelib -> {
|
||||
R.id.action_update_library -> {
|
||||
activity?.let {
|
||||
if (LibraryUpdateService.start(it)) {
|
||||
it.toast(R.string.updating_library)
|
||||
|
@ -487,7 +485,7 @@ class LibraryController(
|
|||
// Notify the presenter a manga is being opened.
|
||||
presenter.onOpenManga()
|
||||
|
||||
router.pushController(AnimeController(manga).withFadeTransaction())
|
||||
router.pushController(MangaController(manga).withFadeTransaction())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -533,11 +531,11 @@ class LibraryController(
|
|||
|
||||
// Get indexes of the common categories to preselect.
|
||||
val commonCategoriesIndexes = presenter.getCommonCategories(mangas)
|
||||
.map { categories.indexOf(it) }
|
||||
.toTypedArray()
|
||||
.map { categories.indexOf(it) }
|
||||
.toTypedArray()
|
||||
|
||||
ChangeMangaCategoriesDialog(this, mangas, categories, commonCategoriesIndexes)
|
||||
.showDialog(router)
|
||||
.showDialog(router)
|
||||
}
|
||||
|
||||
private fun downloadUnreadChapters() {
|
||||
|
@ -585,4 +583,4 @@ class LibraryController(
|
|||
performSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,8 +33,8 @@ import uy.kohesive.injekt.api.get
|
|||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MangaInfoHeaderAdapter(
|
||||
private val controller: AnimeController,
|
||||
private val fromSource: Boolean
|
||||
private val controller: AnimeController,
|
||||
private val fromSource: Boolean
|
||||
) :
|
||||
RecyclerView.Adapter<AnimeInfoHeaderAdapter.HeaderViewHolder>() {
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ import androidx.preference.PreferenceScreen
|
|||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService.Target
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService.Target
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE
|
||||
|
|
|
@ -26,26 +26,26 @@ import androidx.core.view.setPadding
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.data.preference.toggle
|
||||
import eu.kanade.tachiyomi.databinding.WatcherActivityBinding
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherPresenter.SetAsCoverResult.AddToLibraryFirst
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherPresenter.SetAsCoverResult.Error
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherPresenter.SetAsCoverResult.Success
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherEpisode
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.watcher.setting.WatcherSettingsSheet
|
||||
import eu.kanade.tachiyomi.ui.watcher.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.ui.watcher.setting.WatcherSettingsSheet
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.BaseViewer
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.pager.L2RPagerViewer
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.pager.R2LPagerViewer
|
||||
|
|
|
@ -7,8 +7,8 @@ import com.jakewharton.rxrelay.BehaviorRelay
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
|
@ -17,9 +17,9 @@ import eu.kanade.tachiyomi.source.SourceManager
|
|||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.ui.watcher.loader.EpisodeLoader
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherEpisode
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.util.isLocal
|
||||
import eu.kanade.tachiyomi.util.lang.byteSize
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
|
|
|
@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.ui.watcher.viewer
|
|||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
|
||||
/**
|
||||
* Interface for implementing a viewer.
|
||||
|
|
|
@ -13,8 +13,8 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.ui.watcher.WatcherActivity
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.EpisodeTransition
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.InsertPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.BaseViewer
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.ViewerNavigation.NavigationRegion
|
||||
import kotlinx.coroutines.MainScope
|
||||
|
|
|
@ -4,9 +4,9 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.EpisodeTransition
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.InsertPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherEpisode
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.hasMissingEpisodes
|
||||
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
||||
import timber.log.Timber
|
||||
|
|
|
@ -6,9 +6,9 @@ import android.widget.LinearLayout
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.EpisodeTransition
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherEpisode
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.hasMissingEpisodes
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,8 +12,8 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.recyclerview.widget.WebtoonLayoutManager
|
||||
import eu.kanade.tachiyomi.ui.watcher.WatcherActivity
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.EpisodeTransition
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.ViewerEpisodes
|
||||
import eu.kanade.tachiyomi.ui.watcher.model.WatcherPage
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.BaseViewer
|
||||
import eu.kanade.tachiyomi.ui.watcher.viewer.ViewerNavigation.NavigationRegion
|
||||
import kotlinx.coroutines.MainScope
|
||||
|
|
|
@ -52,10 +52,10 @@ fun Anime.updateCoverLastModified(db: AnimeDatabaseHelper) {
|
|||
db.updateAnimeCoverLastModified(this).executeAsBlocking()
|
||||
}
|
||||
|
||||
fun Anime.shouldDownloadNewChapters(db: AnimeDatabaseHelper, prefs: PreferencesHelper): Boolean {
|
||||
fun Anime.shouldDownloadNewEpisodes(db: AnimeDatabaseHelper, prefs: PreferencesHelper): Boolean {
|
||||
if (!favorite) return false
|
||||
|
||||
// Boolean to determine if user wants to automatically download new chapters.
|
||||
// Boolean to determine if user wants to automatically download new episodes.
|
||||
val downloadNew = prefs.downloadNew().get()
|
||||
if (!downloadNew) return false
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package eu.kanade.tachiyomi.util.episode
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
|
||||
/**
|
||||
* -R> = regex conversion.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package eu.kanade.tachiyomi.util.episode
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Episode
|
||||
import eu.kanade.tachiyomi.data.download.AnimeDownloadManager
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.AnimeSource
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -25,7 +25,7 @@ fun syncEpisodesWithSource(
|
|||
db: AnimeDatabaseHelper,
|
||||
rawSourceEpisodes: List<SEpisode>,
|
||||
anime: Anime,
|
||||
source: Source
|
||||
source: AnimeSource
|
||||
): Pair<List<Episode>, List<Episode>> {
|
||||
if (rawSourceEpisodes.isEmpty()) {
|
||||
throw NoEpisodesException()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package eu.kanade.tachiyomi.util.storage
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import eu.kanade.tachiyomi.source.model.SAnime
|
||||
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import java.io.Closeable
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package tachiyomi.source
|
||||
|
||||
import tachiyomi.source.model.EpisodeInfo
|
||||
import tachiyomi.source.model.AnimeInfo
|
||||
import tachiyomi.source.model.EpisodeInfo
|
||||
import tachiyomi.source.model.Page
|
||||
|
||||
/**
|
||||
|
@ -51,5 +51,4 @@ interface AnimeSource {
|
|||
fun getRegex(): Regex {
|
||||
return Regex("")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package tachiyomi.source.model
|
||||
|
||||
data class EpisodeInfo(
|
||||
var key: String,
|
||||
var name: String,
|
||||
var dateUpload: Long = 0,
|
||||
var number: Float = -1f,
|
||||
var scanlator: String = ""
|
||||
var key: String,
|
||||
var name: String,
|
||||
var dateUpload: Long = 0,
|
||||
var number: Float = -1f,
|
||||
var scanlator: String = ""
|
||||
)
|
||||
|
|
62
app/src/main/res/layout/anime_track_search_dialog.xml
Normal file
62
app/src/main/res/layout/anime_track_search_dialog.xml
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:endIconMode="clear_text"
|
||||
app:hintEnabled="false">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/track_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title"
|
||||
android:inputType="text" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/track_search_list"
|
||||
style="@style/Theme.Widget.CardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
android:clipToPadding="false"
|
||||
android:divider="@null"
|
||||
android:dividerHeight="10dp"
|
||||
android:footerDividersEnabled="true"
|
||||
android:headerDividersEnabled="true"
|
||||
android:listSelector="@drawable/list_item_selector"
|
||||
android:scrollbars="none"
|
||||
android:visibility="invisible"
|
||||
tools:listitem="@layout/track_search_item"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
137
app/src/main/res/layout/anime_track_search_item.xml
Normal file
137
app/src/main/res/layout/anime_track_search_item.xml
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/Theme.Widget.CardView.Item"
|
||||
android:layout_margin="0dp"
|
||||
android:padding="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="216dp"
|
||||
android:background="@drawable/list_item_selector"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/track_search_cover"
|
||||
android:layout_width="135dp"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/description_cover"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="3"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_cover"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="One Piece" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:maxLines="1"
|
||||
android:text="@string/track_type"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_cover"
|
||||
app:layout_constraintTop_toBottomOf="@id/track_search_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_type_result"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_type"
|
||||
app:layout_constraintTop_toBottomOf="@id/track_search_title"
|
||||
tools:text="Manga" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:maxLines="1"
|
||||
android:text="@string/track_start_date"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_cover"
|
||||
app:layout_constraintTop_toBottomOf="@id/track_search_type" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_start_result"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/track_search_type"
|
||||
tools:text="2018-10-01" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:maxLines="1"
|
||||
android:text="@string/track_status"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_cover"
|
||||
app:layout_constraintTop_toBottomOf="@id/track_search_start" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_status_result"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_status"
|
||||
app:layout_constraintTop_toBottomOf="@id/track_search_start"
|
||||
tools:text="Ongoing" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_search_summary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="7"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@id/track_search_cover"
|
||||
app:layout_constraintTop_toBottomOf="@+id/track_search_status"
|
||||
app:layout_constraintVertical_bias="0.333"
|
||||
tools:text="This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits " />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
20
app/src/main/res/layout/animelib_category.xml
Normal file
20
app/src/main/res/layout/animelib_category.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<eu.kanade.tachiyomi.ui.animelib.AnimelibCategoryView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialFastScroll
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
app:fastScrollerBubbleEnabled="false" />
|
||||
|
||||
</eu.kanade.tachiyomi.ui.animelib.AnimelibCategoryView>
|
44
app/src/main/res/layout/animelib_controller.xml
Normal file
44
app/src/main/res/layout/animelib_controller.xml
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_global_search"
|
||||
style="@style/Theme.Widget.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:visibility="gone"
|
||||
tools:text="Search"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/animelib_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<eu.kanade.tachiyomi.widget.ActionToolbar
|
||||
android:id="@+id/action_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:layout_dodgeInsetEdges="bottom" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
Loading…
Reference in a new issue