diff --git a/server/build.gradle.kts b/server/build.gradle.kts index fdbb1d1e..b0cefb11 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { implementation("com.h2database:h2:1.4.200") // Exposed Migrations - implementation("com.github.Suwayomi:exposed-migrations:3.1.2") + implementation("com.github.Suwayomi:exposed-migrations:3.1.4") // tray icon implementation("com.dorkbox:SystemTray:4.1") diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeCatalogueSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeCatalogueSource.kt deleted file mode 100644 index 67d99256..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeCatalogueSource.kt +++ /dev/null @@ -1,46 +0,0 @@ -package eu.kanade.tachiyomi.animesource - -import eu.kanade.tachiyomi.animesource.model.AnimeFilterList -import eu.kanade.tachiyomi.animesource.model.AnimesPage -import rx.Observable - -interface AnimeCatalogueSource : AnimeSource { - - /** - * An ISO 639-1 compliant language code (two letters in lower case). - */ - override val lang: String - - /** - * Whether the source has support for latest updates. - */ - val supportsLatest: Boolean - - /** - * Returns an observable containing a page with a list of anime. - * - * @param page the page number to retrieve. - */ - fun fetchPopularAnime(page: Int): Observable - - /** - * Returns an observable containing a page with a list of anime. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable - - /** - * Returns an observable containing a page with a list of latest anime updates. - * - * @param page the page number to retrieve. - */ - fun fetchLatestUpdates(page: Int): Observable - - /** - * Returns the list of filters for the source. - */ - fun getFilterList(): AnimeFilterList -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt deleted file mode 100644 index 59274a59..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt +++ /dev/null @@ -1,81 +0,0 @@ -package eu.kanade.tachiyomi.animesource - -import eu.kanade.tachiyomi.animesource.model.SAnime -import eu.kanade.tachiyomi.animesource.model.SEpisode -import eu.kanade.tachiyomi.animesource.model.Video -import rx.Observable - -/** - * A basic interface for creating a source. It could be an online source, a local source, etc... - */ -interface AnimeSource { - - /** - * Id for the source. Must be unique. - */ - val id: Long - - /** - * Name of the source. - */ - val name: String - - val lang: String - get() = "" - - /** - * Returns an observable with the updated details for a anime. - * - * @param anime the anime to update. - */ -// @Deprecated("Use getAnimeDetails instead") - fun fetchAnimeDetails(anime: SAnime): Observable - - /** - * Returns an observable with all the available episodes for an anime. - * - * @param anime the anime to update. - */ -// @Deprecated("Use getEpisodeList instead") - fun fetchEpisodeList(anime: SAnime): Observable> - - /** - * Returns an observable with a list of video for the episode of an anime. - * - * @param episode the episode to get the link for. - */ -// @Deprecated("Use getEpisodeList instead") - fun fetchVideoList(episode: SEpisode): Observable> - -// /** -// * [1.x API] Get the updated details for a anime. -// */ -// @Suppress("DEPRECATION") -// override suspend fun getAnimeDetails(anime: AnimeInfo): AnimeInfo { -// val sAnime = anime.toSAnime() -// val networkAnime = fetchAnimeDetails(sAnime).awaitSingle() -// sAnime.copyFrom(networkAnime) -// return sAnime.toAnimeInfo() -// } - -// /** -// * [1.x API] Get all the available episodes for a anime. -// */ -// @Suppress("DEPRECATION") -// override suspend fun getEpisodeList(anime: AnimeInfo): List { -// return fetchEpisodeList(anime.toSAnime()).awaitSingle() -// .map { it.toEpisodeInfo() } -// } - -// /** -// * [1.x API] Get a link for the episode of an anime. -// */ -// @Suppress("DEPRECATION") -// override suspend fun getEpisodeLink(episode: EpisodeInfo): String { -// return fetchEpisodeLink(episode.toSEpisode()).awaitSingle() -// } -} - -// fun AnimeSource.icon(): Drawable? = Injekt.get().getAppIconForSource(this) - -fun AnimeSource.getPreferenceKey(): String = "source_$id" diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeSourceFactory.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeSourceFactory.kt deleted file mode 100644 index ca5755b6..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/AnimeSourceFactory.kt +++ /dev/null @@ -1,12 +0,0 @@ -package eu.kanade.tachiyomi.animesource - -/** - * A factory for creating sources at runtime. - */ -interface AnimeSourceFactory { - /** - * Create a new copy of the sources - * @return The created sources - */ - fun createSources(): List -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/ConfigurableAnimeSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/ConfigurableAnimeSource.kt deleted file mode 100644 index 5d4e872f..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/ConfigurableAnimeSource.kt +++ /dev/null @@ -1,8 +0,0 @@ -package eu.kanade.tachiyomi.animesource - -import androidx.preference.PreferenceScreen - -interface ConfigurableAnimeSource : AnimeSource { - - fun setupPreferenceScreen(screen: PreferenceScreen) -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimeFilter.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimeFilter.kt deleted file mode 100644 index e19c2b79..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimeFilter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -sealed class AnimeFilter(val name: String, var state: T) { - open class Header(name: String) : AnimeFilter(name, 0) - open class Separator(name: String = "") : AnimeFilter(name, 0) - abstract class Select(name: String, val values: Array, state: Int = 0) : AnimeFilter(name, state) - abstract class Text(name: String, state: String = "") : AnimeFilter(name, state) - abstract class CheckBox(name: String, state: Boolean = false) : AnimeFilter(name, state) - abstract class TriState(name: String, state: Int = STATE_IGNORE) : AnimeFilter(name, state) { - fun isIgnored() = state == STATE_IGNORE - fun isIncluded() = state == STATE_INCLUDE - fun isExcluded() = state == STATE_EXCLUDE - - companion object { - const val STATE_IGNORE = 0 - const val STATE_INCLUDE = 1 - const val STATE_EXCLUDE = 2 - } - } - - abstract class Group(name: String, state: List) : AnimeFilter>(name, state) - - abstract class Sort(name: String, val values: Array, state: Selection? = null) : - AnimeFilter(name, state) { - data class Selection(val index: Int, val ascending: Boolean) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is AnimeFilter<*>) return false - - return name == other.name && state == other.state - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + (state?.hashCode() ?: 0) - return result - } -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimeFilterList.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimeFilterList.kt deleted file mode 100644 index 7747104b..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimeFilterList.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -data class AnimeFilterList(val list: List>) : List> by list { - - constructor(vararg fs: AnimeFilter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList()) -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimesPage.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimesPage.kt deleted file mode 100644 index 948b02ee..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/AnimesPage.kt +++ /dev/null @@ -1,3 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -data class AnimesPage(val animes: List, val hasNextPage: Boolean) diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SAnime.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SAnime.kt deleted file mode 100644 index 2c37a009..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SAnime.kt +++ /dev/null @@ -1,93 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -// import tachiyomi.animesource.model.AnimeInfo -import java.io.Serializable - -interface SAnime : Serializable { - - var url: String - - var title: String - - var artist: String? - - var author: String? - - var description: String? - - var genre: String? - - var status: Int - - var thumbnail_url: String? - - var initialized: Boolean - - fun copyFrom(other: SAnime) { - title = other.title - - if (other.author != null) { - author = other.author - } - - if (other.artist != null) { - artist = other.artist - } - - if (other.description != null) { - description = other.description - } - - if (other.genre != null) { - genre = other.genre - } - - if (other.thumbnail_url != null) { - thumbnail_url = other.thumbnail_url - } - - status = other.status - - if (!initialized) { - initialized = other.initialized - } - } - - companion object { - const val UNKNOWN = 0 - const val ONGOING = 1 - const val COMPLETED = 2 - const val LICENSED = 3 - - fun create(): SAnime { - return SAnimeImpl() - } - } -} - -// fun SAnime.toAnimeInfo(): AnimeInfo { -// return AnimeInfo( -// key = this.url, -// title = this.title, -// artist = this.artist ?: "", -// author = this.author ?: "", -// description = this.description ?: "", -// genres = this.genre?.split(", ") ?: emptyList(), -// status = this.status, -// cover = this.thumbnail_url ?: "" -// ) -// } - -// fun AnimeInfo.toSAnime(): SAnime { -// val animeInfo = this -// return SAnime.create().apply { -// url = animeInfo.key -// title = animeInfo.title -// artist = animeInfo.artist -// author = animeInfo.author -// description = animeInfo.description -// genre = animeInfo.genres.joinToString(", ") -// status = animeInfo.status -// thumbnail_url = animeInfo.cover -// } -// } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SAnimeImpl.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SAnimeImpl.kt deleted file mode 100644 index 14b63219..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SAnimeImpl.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -class SAnimeImpl : SAnime { - - override lateinit var url: String - - override lateinit var title: String - - override var artist: String? = null - - override var author: String? = null - - override var description: String? = null - - override var genre: String? = null - - override var status: Int = 0 - - override var thumbnail_url: String? = null - - override var initialized: Boolean = false -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SEpisode.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SEpisode.kt deleted file mode 100644 index b1a11099..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SEpisode.kt +++ /dev/null @@ -1,52 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -// import tachiyomi.animesource.model.EpisodeInfo -import java.io.Serializable - -interface SEpisode : Serializable { - - var url: String - - var name: String - - var date_upload: Long - - var episode_number: Float - - var scanlator: String? - - fun copyFrom(other: SEpisode) { - name = other.name - url = other.url - date_upload = other.date_upload - episode_number = other.episode_number - scanlator = other.scanlator - } - - companion object { - fun create(): SEpisode { - return SEpisodeImpl() - } - } -} - -// fun SEpisode.toEpisodeInfo(): EpisodeInfo { -// return EpisodeInfo( -// dateUpload = this.date_upload, -// key = this.url, -// name = this.name, -// number = this.episode_number, -// scanlator = this.scanlator ?: "" -// ) -// } -// -// fun EpisodeInfo.toSEpisode(): SEpisode { -// val episode = this -// return SEpisode.create().apply { -// url = episode.key -// name = episode.name -// date_upload = episode.dateUpload -// episode_number = episode.number -// scanlator = episode.scanlator -// } -// } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SEpisodeImpl.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SEpisodeImpl.kt deleted file mode 100644 index 873749f2..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/SEpisodeImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -class SEpisodeImpl : SEpisode { - - override lateinit var url: String - - override lateinit var name: String - - override var date_upload: Long = 0 - - override var episode_number: Float = -1f - - override var scanlator: String? = null -} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/Video.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/Video.kt deleted file mode 100644 index ddca7205..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/model/Video.kt +++ /dev/null @@ -1,73 +0,0 @@ -package eu.kanade.tachiyomi.animesource.model - -import android.net.Uri -import eu.kanade.tachiyomi.network.ProgressListener -import rx.subjects.Subject -// import tachiyomi.animesource.model.VideoUrl - -open class Video( - val url: String = "", - val quality: String = "", - var videoUrl: String? = null, - @Transient var uri: Uri? = null // Deprecated but can't be deleted due to extensions -) : ProgressListener { - - @Transient - @Volatile - var status: Int = 0 - set(value) { - field = value - statusSubject?.onNext(value) - statusCallback?.invoke(this) - } - - @Transient - @Volatile - var progress: Int = 0 - set(value) { - field = value - statusCallback?.invoke(this) - } - - @Transient - private var statusSubject: Subject? = null - - @Transient - private var statusCallback: ((Video) -> Unit)? = null - - override fun update(bytesRead: Long, contentLength: Long, done: Boolean) { - progress = if (contentLength > 0) { - (100 * bytesRead / contentLength).toInt() - } else { - -1 - } - } - - fun setStatusSubject(subject: Subject?) { - this.statusSubject = subject - } - - fun setStatusCallback(f: ((Video) -> Unit)?) { - statusCallback = f - } - - companion object { - const val QUEUE = 0 - const val LOAD_VIDEO = 1 - const val DOWNLOAD_IMAGE = 2 - const val READY = 3 - const val ERROR = 4 - } -} - -// fun Video.toVideoUrl(): VideoUrl { -// return VideoUrl( -// url = this.videoUrl ?: this.url -// ) -// } -// -// fun VideoUrl.toVideo(index: Int): Video { -// return Video( -// videoUrl = this.url -// ) -// } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/online/AnimeHttpSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/online/AnimeHttpSource.kt deleted file mode 100644 index 44c95975..00000000 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/animesource/online/AnimeHttpSource.kt +++ /dev/null @@ -1,376 +0,0 @@ -package eu.kanade.tachiyomi.animesource.online - -import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource -import eu.kanade.tachiyomi.animesource.model.AnimeFilterList -import eu.kanade.tachiyomi.animesource.model.AnimesPage -import eu.kanade.tachiyomi.animesource.model.SAnime -import eu.kanade.tachiyomi.animesource.model.SEpisode -import eu.kanade.tachiyomi.animesource.model.Video -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 okhttp3.Headers -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import rx.Observable -import uy.kohesive.injekt.injectLazy -import java.net.URI -import java.net.URISyntaxException -import java.security.MessageDigest - -/** - * A simple implementation for sources from a website. - */ -abstract class AnimeHttpSource : AnimeCatalogueSource { - - /** - * Network service. - */ - protected val network: NetworkHelper by injectLazy() - -// /** -// * Preferences that a source may need. -// */ -// val preferences: SharedPreferences by lazy { -// Injekt.get().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) -// } - - /** - * Base url of the website without the trailing slash, like: http://mysite.com - */ - abstract val baseUrl: String - - /** - * Version id used to generate the source id. If the site completely changes and urls are - * incompatible, you may increase this value and it'll be considered as a new source. - */ - open val versionId = 1 - - /** - * Id of the source. By default it uses a generated id using the first 16 characters (64 bits) - * of the MD5 of the string: sourcename/language/versionId - * Note the generated id sets the sign bit to 0. - */ - override val id by lazy { - val key = "${name.lowercase()}/$lang/$versionId" - val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) - (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE - } - - /** - * Headers used for requests. - */ - val headers: Headers by lazy { headersBuilder().build() } - - /** - * Default network client for doing requests. - */ - open val client: OkHttpClient - get() = network.client - - /** - * Headers builder for requests. Implementations can override this method for custom headers. - */ - protected open fun headersBuilder() = Headers.Builder().apply { - add("User-Agent", DEFAULT_USER_AGENT) - } - - /** - * Visible name of the source. - */ - override fun toString() = "$name (${lang.uppercase()})" - - /** - * Returns an observable containing a page with a list of anime. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - */ - override fun fetchPopularAnime(page: Int): Observable { - return client.newCall(popularAnimeRequest(page)) - .asObservableSuccess() - .map { response -> - popularAnimeParse(response) - } - } - - /** - * Returns the request for the popular anime given the page. - * - * @param page the page number to retrieve. - */ - protected abstract fun popularAnimeRequest(page: Int): Request - - /** - * Parses the response from the site and returns a [AnimesPage] object. - * - * @param response the response from the site. - */ - protected abstract fun popularAnimeParse(response: Response): AnimesPage - - /** - * Returns an observable containing a page with a list of anime. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { - return client.newCall(searchAnimeRequest(page, query, filters)) - .asObservableSuccess() - .map { response -> - searchAnimeParse(response) - } - } - - /** - * Returns the request for the search anime given the page. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - protected abstract fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request - - /** - * Parses the response from the site and returns a [AnimesPage] object. - * - * @param response the response from the site. - */ - protected abstract fun searchAnimeParse(response: Response): AnimesPage - - /** - * Returns an observable containing a page with a list of latest anime updates. - * - * @param page the page number to retrieve. - */ - override fun fetchLatestUpdates(page: Int): Observable { - return client.newCall(latestUpdatesRequest(page)) - .asObservableSuccess() - .map { response -> - latestUpdatesParse(response) - } - } - - /** - * Returns the request for latest anime given the page. - * - * @param page the page number to retrieve. - */ - protected abstract fun latestUpdatesRequest(page: Int): Request - - /** - * Parses the response from the site and returns a [AnimesPage] object. - * - * @param response the response from the site. - */ - protected abstract fun latestUpdatesParse(response: Response): AnimesPage - - /** - * Returns an observable with the updated details for a anime. Normally it's not needed to - * override this method. - * - * @param anime the anime to be updated. - */ - override fun fetchAnimeDetails(anime: SAnime): Observable { - return client.newCall(animeDetailsRequest(anime)) - .asObservableSuccess() - .map { response -> - animeDetailsParse(response).apply { initialized = true } - } - } - - /** - * Returns the request for the details of a anime. Override only if it's needed to change the - * url, send different headers or request method like POST. - * - * @param anime the anime to be updated. - */ - open fun animeDetailsRequest(anime: SAnime): Request { - return GET(baseUrl + anime.url, headers) - } - - /** - * Parses the response from the site and returns the details of a anime. - * - * @param response the response from the site. - */ - protected abstract fun animeDetailsParse(response: Response): SAnime - - /** - * Returns an observable with the updated episode list for a anime. Normally it's not needed to - * override this method. If a anime is licensed an empty episode list observable is returned - * - * @param anime the anime to look for episodes. - */ - override fun fetchEpisodeList(anime: SAnime): Observable> { - return if (anime.status != SAnime.LICENSED) { - client.newCall(episodeListRequest(anime)) - .asObservableSuccess() - .map { response -> - episodeListParse(response) - } - } else { - Observable.error(Exception("Licensed - No episodes to show")) - } - } - - /** - * Returns the request for updating the episode list. Override only if it's needed to override - * the url, send different headers or request method like POST. - * - * @param anime the anime to look for episodes. - */ - protected open fun episodeListRequest(anime: SAnime): Request { - return GET(baseUrl + anime.url, headers) - } - - /** - * Parses the response from the site and returns a list of episodes. - * - * @param response the response from the site. - */ - protected abstract fun episodeListParse(response: Response): List - - /** - * Returns an observable with the page list for a chapter. - * - * @param chapter the chapter whose page list has to be fetched. - */ - override fun fetchVideoList(episode: SEpisode): Observable> { - return client.newCall(videoListRequest(episode)) - .asObservableSuccess() - .map { response -> - videoListParse(response) - } - } - - /** - * Returns the request for getting the episode link. Override only if it's needed to override - * the url, send different headers or request method like POST. - * - * @param episode the episode to look for links. - */ - protected open fun videoListRequest(episode: SEpisode): Request { - return GET(baseUrl + episode.url, headers) - } - - /** - * Parses the response from the site and returns a list of pages. - * - * @param response the response from the site. - */ - protected abstract fun videoListParse(response: Response): List