Add tachiyomi-extensions-lib as module (#1919)

This commit is contained in:
arkon 2019-12-26 12:34:43 -05:00 committed by GitHub
parent d093fac043
commit 9ff7da20e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 789 additions and 7 deletions

View File

@ -0,0 +1,31 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 27
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName '1.2'
}
buildTypes {
release {
minifyEnabled false
}
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly 'com.squareup.okhttp3:okhttp:3.10.0'
compileOnly 'io.reactivex:rxjava:1.3.6'
compileOnly 'org.jsoup:jsoup:1.10.2'
compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.kanade.tachiyomi.extensions">
</manifest>

View File

@ -0,0 +1,11 @@
package eu.kanade.tachiyomi.network
import android.content.Context
import okhttp3.OkHttpClient
class NetworkHelper(context: Context) {
val client: OkHttpClient = throw Exception("Stub!")
val cloudflareClient: OkHttpClient = throw Exception("Stub!")
}

View File

@ -0,0 +1,13 @@
package eu.kanade.tachiyomi.network
import okhttp3.Call
import okhttp3.Response
import rx.Observable
fun Call.asObservable(): Observable<Response> {
throw Exception("Stub!")
}
fun Call.asObservableSuccess(): Observable<Response> {
throw Exception("Stub!")
}

View File

@ -0,0 +1,25 @@
package eu.kanade.tachiyomi.network
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.Request
import okhttp3.RequestBody
private val DEFAULT_CACHE_CONTROL: CacheControl = throw Exception("Stub!")
private val DEFAULT_HEADERS: Headers = throw Exception("Stub!")
private val DEFAULT_BODY: RequestBody = throw Exception("Stub!")
fun GET(url: String,
headers: Headers = DEFAULT_HEADERS,
cache: CacheControl = DEFAULT_CACHE_CONTROL): Request {
throw Exception("Stub!")
}
fun POST(url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL): Request {
throw Exception("Stub!")
}

View File

@ -0,0 +1,46 @@
package eu.kanade.tachiyomi.source
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import rx.Observable
interface CatalogueSource : Source {
/**
* An ISO 639-1 compliant language code (two letters in lower case).
*/
val lang: String
/**
* Whether the source has support for latest updates.
*/
val supportsLatest: Boolean
/**
* Returns an observable containing a page with a list of manga.
*
* @param page the page number to retrieve.
*/
fun fetchPopularManga(page: Int): Observable<MangasPage>
/**
* Returns an observable containing a page with a list of manga.
*
* @param page the page number to retrieve.
* @param query the search query.
* @param filters the list of filters to apply.
*/
fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage>
/**
* Returns an observable containing a page with a list of latest manga updates.
*
* @param page the page number to retrieve.
*/
fun fetchLatestUpdates(page: Int): Observable<MangasPage>
/**
* Returns the list of filters for the source.
*/
fun getFilterList(): FilterList
}

View File

@ -0,0 +1,44 @@
package eu.kanade.tachiyomi.source
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import rx.Observable
/**
* A basic interface for creating a source. It could be an online source, a local source, etc...
*/
interface Source {
/**
* Id for the source. Must be unique.
*/
val id: Long
/**
* Name of the source.
*/
val name: String
/**
* Returns an observable with the updated details for a manga.
*
* @param manga the manga to update.
*/
fun fetchMangaDetails(manga: SManga): Observable<SManga>
/**
* Returns an observable with all the available chapters for a manga.
*
* @param manga the manga to update.
*/
fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
/**
* Returns an observable with the list of pages a chapter has.
*
* @param chapter the chapter.
*/
fun fetchPageList(chapter: SChapter): Observable<List<Page>>
}

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.source
/**
* A factory for creating sources at runtime.
*/
interface SourceFactory {
/**
* Create a new copy of the sources
* @return The created sources
*/
fun createSources(): List<Source>
}

View File

@ -0,0 +1,27 @@
package eu.kanade.tachiyomi.source.model
sealed class Filter<T>(val name: String, var state: T) {
open class Header(name: String) : Filter<Any>(name, 0)
open class Separator(name: String = "") : Filter<Any>(name, 0)
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(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<V>(name: String, state: List<V>): Filter<List<V>>(name, state)
abstract class Sort(name: String, val values: Array<String>, state: Selection? = null)
: Filter<Sort.Selection?>(name, state) {
data class Selection(val index: Int, val ascending: Boolean)
}
}

View File

@ -0,0 +1,7 @@
package eu.kanade.tachiyomi.source.model
data class FilterList(val list: List<Filter<*>>) : List<Filter<*>> by list {
constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList())
}

View File

@ -0,0 +1,3 @@
package eu.kanade.tachiyomi.source.model
data class MangasPage(val mangas: List<SManga>, val hasNextPage: Boolean)

View File

@ -0,0 +1,10 @@
package eu.kanade.tachiyomi.source.model
import android.net.Uri
class Page(
val index: Int,
val url: String = "",
var imageUrl: String? = null,
var uri: Uri? = null
)

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.source.model
interface SChapter {
var url: String
var name: String
var date_upload: Long
var chapter_number: Float
var scanlator: String?
companion object {
fun create(): SChapter {
throw Exception("Stub!")
}
}
}

View File

@ -0,0 +1,34 @@
package eu.kanade.tachiyomi.source.model
interface SManga {
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
companion object {
const val UNKNOWN = 0
const val ONGOING = 1
const val COMPLETED = 2
const val LICENSED = 3
fun create(): SManga {
throw Exception("Stub!")
}
}
}

View File

@ -0,0 +1,311 @@
package eu.kanade.tachiyomi.source.online
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.*
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import rx.Observable
/**
* A simple implementation for sources from a website.
*/
@Suppress("unused", "unused_parameter")
abstract class HttpSource : CatalogueSource {
/**
* Network service.
*/
protected val network: NetworkHelper = throw Exception("Stub!")
/**
* 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: Int = throw Exception("Stub!")
/**
* 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: Long = throw Exception("Stub!")
/**
* Headers used for requests.
*/
val headers: Headers = throw Exception("Stub!")
/**
* Default network client for doing requests.
*/
open val client: OkHttpClient = throw Exception("Stub!")
/**
* Headers builder for requests. Implementations can override this method for custom headers.
*/
open protected fun headersBuilder(): Headers.Builder {
throw Exception("Stub!")
}
/**
* Visible name of the source.
*/
override fun toString(): String {
throw Exception("Stub!")
}
/**
* Returns an observable containing a page with a list of manga. Normally it's not needed to
* override this method.
*
* @param page the page number to retrieve.
*/
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
throw Exception("Stub!")
}
/**
* Returns the request for the popular manga given the page.
*
* @param page the page number to retrieve.
*/
abstract protected fun popularMangaRequest(page: Int): Request
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
abstract protected fun popularMangaParse(response: Response): MangasPage
/**
* Returns an observable containing a page with a list of manga. 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 fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
throw Exception("Stub!")
}
/**
* Returns the request for the search manga given the page.
*
* @param page the page number to retrieve.
* @param query the search query.
* @param filters the list of filters to apply.
*/
abstract protected fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
abstract protected fun searchMangaParse(response: Response): MangasPage
/**
* Returns an observable containing a page with a list of latest manga updates.
*
* @param page the page number to retrieve.
*/
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
throw Exception("Stub!")
}
/**
* Returns the request for latest manga given the page.
*
* @param page the page number to retrieve.
*/
abstract protected fun latestUpdatesRequest(page: Int): Request
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
abstract protected fun latestUpdatesParse(response: Response): MangasPage
/**
* Returns an observable with the updated details for a manga. Normally it's not needed to
* override this method.
*
* @param manga the manga to be updated.
*/
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
throw Exception("Stub!")
}
/**
* Returns the request for the details of a manga. Override only if it's needed to change the
* url, send different headers or request method like POST.
*
* @param manga the manga to be updated.
*/
open fun mangaDetailsRequest(manga: SManga): Request {
throw Exception("Stub!")
}
/**
* Parses the response from the site and returns the details of a manga.
*
* @param response the response from the site.
*/
abstract protected fun mangaDetailsParse(response: Response): SManga
/**
* Returns an observable with the updated chapter list for a manga. Normally it's not needed to
* override this method.
*
* @param manga the manga to look for chapters.
*/
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
throw Exception("Stub!")
}
/**
* Returns the request for updating the chapter list. Override only if it's needed to override
* the url, send different headers or request method like POST.
*
* @param manga the manga to look for chapters.
*/
open protected fun chapterListRequest(manga: SManga): Request {
throw Exception("Stub!")
}
/**
* Parses the response from the site and returns a list of chapters.
*
* @param response the response from the site.
*/
abstract protected fun chapterListParse(response: Response): List<SChapter>
/**
* Returns an observable with the page list for a chapter.
*
* @param chapter the chapter whose page list has to be fetched.
*/
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
throw Exception("Stub!")
}
/**
* Returns the request for getting the page list. Override only if it's needed to override the
* url, send different headers or request method like POST.
*
* @param chapter the chapter whose page list has to be fetched.
*/
open protected fun pageListRequest(chapter: SChapter): Request {
throw Exception("Stub!")
}
/**
* Parses the response from the site and returns a list of pages.
*
* @param response the response from the site.
*/
abstract protected fun pageListParse(response: Response): List<Page>
/**
* Returns an observable with the page containing the source url of the image. If there's any
* error, it will return null instead of throwing an exception.
*
* @param page the page whose source image has to be fetched.
*/
open fun fetchImageUrl(page: Page): Observable<String> {
throw Exception("Stub!")
}
/**
* Returns the request for getting the url to the source image. Override only if it's needed to
* override the url, send different headers or request method like POST.
*
* @param page the chapter whose page list has to be fetched
*/
open protected fun imageUrlRequest(page: Page): Request {
throw Exception("Stub!")
}
/**
* Parses the response from the site and returns the absolute url to the source image.
*
* @param response the response from the site.
*/
abstract protected fun imageUrlParse(response: Response): String
/**
* Returns an observable with the response of the source image.
*
* @param page the page whose source image has to be downloaded.
*/
fun fetchImage(page: Page): Observable<Response> {
throw Exception("Stub!")
}
/**
* Returns the request for getting the source image. Override only if it's needed to override
* the url, send different headers or request method like POST.
*
* @param page the chapter whose page list has to be fetched
*/
open protected fun imageRequest(page: Page): Request {
throw Exception("Stub!")
}
/**
* Assigns the url of the chapter without the scheme and domain. It saves some redundancy from
* database and the urls could still work after a domain change.
*
* @param url the full url to the chapter.
*/
fun SChapter.setUrlWithoutDomain(url: String) {
throw Exception("Stub!")
}
/**
* Assigns the url of the manga without the scheme and domain. It saves some redundancy from
* database and the urls could still work after a domain change.
*
* @param url the full url to the manga.
*/
fun SManga.setUrlWithoutDomain(url: String) {
throw Exception("Stub!")
}
/**
* Returns the url of the given string without the scheme and domain.
*
* @param orig the full url.
*/
private fun getUrlWithoutDomain(orig: String): String {
throw Exception("Stub!")
}
/**
* Called before inserting a new chapter into database. Use it if you need to override chapter
* fields, like the title or the chapter number. Do not change anything to [manga].
*
* @param chapter the chapter to be added.
* @param manga the manga of the chapter.
*/
open fun prepareNewChapter(chapter: SChapter, manga: SManga) {
}
/**
* Returns the list of filters for the source.
*/
override fun getFilterList(): FilterList {
throw Exception("Stub!")
}
}

View File

@ -0,0 +1,169 @@
package eu.kanade.tachiyomi.source.online
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
/**
* A simple implementation for sources from a website using Jsoup, an HTML parser.
*/
@Suppress("unused", "unused_parameter")
abstract class ParsedHttpSource : HttpSource() {
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun popularMangaParse(response: Response): MangasPage {
throw Exception("Stub!")
}
/**
* Returns the Jsoup selector that returns a list of [Element] corresponding to each manga.
*/
abstract protected fun popularMangaSelector(): String
/**
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
* totally fine to fill only those two values.
*
* @param element an element obtained from [popularMangaSelector].
*/
abstract protected fun popularMangaFromElement(element: Element): SManga
/**
* Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if
* there's no next page.
*/
abstract protected fun popularMangaNextPageSelector(): String?
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun searchMangaParse(response: Response): MangasPage {
throw Exception("Stub!")
}
/**
* Returns the Jsoup selector that returns a list of [Element] corresponding to each manga.
*/
abstract protected fun searchMangaSelector(): String
/**
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
* totally fine to fill only those two values.
*
* @param element an element obtained from [searchMangaSelector].
*/
abstract protected fun searchMangaFromElement(element: Element): SManga
/**
* Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if
* there's no next page.
*/
abstract protected fun searchMangaNextPageSelector(): String?
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun latestUpdatesParse(response: Response): MangasPage {
throw Exception("Stub!")
}
/**
* Returns the Jsoup selector that returns a list of [Element] corresponding to each manga.
*/
abstract protected fun latestUpdatesSelector(): String
/**
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
* totally fine to fill only those two values.
*
* @param element an element obtained from [latestUpdatesSelector].
*/
abstract protected fun latestUpdatesFromElement(element: Element): SManga
/**
* Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if
* there's no next page.
*/
abstract protected fun latestUpdatesNextPageSelector(): String?
/**
* Parses the response from the site and returns the details of a manga.
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response): SManga {
throw Exception("Stub!")
}
/**
* Returns the details of the manga from the given [document].
*
* @param document the parsed document.
*/
abstract protected fun mangaDetailsParse(document: Document): SManga
/**
* Parses the response from the site and returns a list of chapters.
*
* @param response the response from the site.
*/
override fun chapterListParse(response: Response): List<SChapter> {
throw Exception("Stub!")
}
/**
* Returns the Jsoup selector that returns a list of [Element] corresponding to each chapter.
*/
abstract protected fun chapterListSelector(): String
/**
* Returns a chapter from the given element.
*
* @param element an element obtained from [chapterListSelector].
*/
abstract protected fun chapterFromElement(element: Element): SChapter
/**
* Parses the response from the site and returns the page list.
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response): List<Page> {
throw Exception("Stub!")
}
/**
* Returns a page list from the given document.
*
* @param document the parsed document.
*/
abstract protected fun pageListParse(document: Document): List<Page>
/**
* Parse the response from the site and returns the absolute url to the source image.
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response): String {
throw Exception("Stub!")
}
/**
* Returns the absolute url to the source image from the document.
*
* @param document the parsed document.
*/
abstract protected fun imageUrlParse(document: Document): String
}

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.util
import okhttp3.Response
import org.jsoup.nodes.Document
/**
* Returns a Jsoup document for this response.
* @param html the body of the response. Use only if the body was read before calling this method.
*/
fun Response.asJsoup(html: String? = null): Document {
throw Exception("Stub!")
}

View File

@ -1,10 +1,5 @@
new File(rootDir, "src").eachDir { dir -> include ':lib-extension'
dir.eachDir { subdir -> project(':lib-extension').projectDir = new File("lib/extension")
String name = ":${dir.name}-${subdir.name}"
include name
project(name).projectDir = new File("src/${dir.name}/${subdir.name}")
}
}
include ':lib-ratelimit' include ':lib-ratelimit'
project(':lib-ratelimit').projectDir = new File("lib/ratelimit") project(':lib-ratelimit').projectDir = new File("lib/ratelimit")
@ -14,3 +9,11 @@ project(':duktape-stub').projectDir = new File("lib/duktape-stub")
include ':preference-stub' include ':preference-stub'
project(':preference-stub').projectDir = new File("lib/preference-stub") project(':preference-stub').projectDir = new File("lib/preference-stub")
new File(rootDir, "src").eachDir { dir ->
dir.eachDir { subdir ->
String name = ":${dir.name}-${subdir.name}"
include name
project(name).projectDir = new File("src/${dir.name}/${subdir.name}")
}
}