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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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
|
||||
|
@ -41,7 +41,7 @@ class AnimeInfoHeaderAdapter(
|
|||
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,8 +6,8 @@ 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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
@ -163,18 +163,16 @@ 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
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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("")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
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