Initial commit
This commit is contained in:
commit
98ed7e8839
2263 changed files with 108711 additions and 0 deletions
15
src/pl/desuonline/build.gradle
Normal file
15
src/pl/desuonline/build.gradle
Normal file
|
@ -0,0 +1,15 @@
|
|||
ext {
|
||||
extName = 'desu-online'
|
||||
extClass = '.DesuOnline'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://desu-online.pl'
|
||||
overrideVersionCode = 5
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:okru-extractor'))
|
||||
implementation(project(':lib:googledrive-extractor'))
|
||||
implementation(project(':lib:sibnet-extractor'))
|
||||
}
|
BIN
src/pl/desuonline/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/pl/desuonline/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
src/pl/desuonline/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/pl/desuonline/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
src/pl/desuonline/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/pl/desuonline/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
BIN
src/pl/desuonline/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/pl/desuonline/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.9 KiB |
BIN
src/pl/desuonline/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/pl/desuonline/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
src/pl/desuonline/res/web_hi_res_512.png
Normal file
BIN
src/pl/desuonline/res/web_hi_res_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 126 KiB |
|
@ -0,0 +1,86 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pl.desuonline
|
||||
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animeextension.pl.desuonline.extractors.CDAExtractor
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.googledriveextractor.GoogleDriveExtractor
|
||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||
import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
|
||||
import okhttp3.Response
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class DesuOnline : AnimeStream(
|
||||
"pl",
|
||||
"desu-online",
|
||||
"https://desu-online.pl",
|
||||
) {
|
||||
override val dateFormatter by lazy {
|
||||
SimpleDateFormat("d MMMM, yyyy", Locale("pl", "PL"))
|
||||
}
|
||||
|
||||
private val prefServerKey = "preferred_server"
|
||||
private val prefServerDefault = "CDA"
|
||||
|
||||
// ============================ Video Links =============================
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> =
|
||||
super.videoListParse(response).ifEmpty { throw Exception("Failed to fetch videos") }
|
||||
|
||||
private val okruExtractor by lazy { OkruExtractor(client) }
|
||||
private val cdaExtractor by lazy { CDAExtractor(client, headers, "$baseUrl/") }
|
||||
private val sibnetExtractor by lazy { SibnetExtractor(client) }
|
||||
private val gdriveExtractor by lazy { GoogleDriveExtractor(client, headers) }
|
||||
|
||||
override fun getVideoList(url: String, name: String): List<Video> {
|
||||
return when {
|
||||
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url, name)
|
||||
url.contains("cda.pl") -> cdaExtractor.videosFromUrl(url, name)
|
||||
url.contains("sibnet") -> sibnetExtractor.videosFromUrl(url, prefix = "$name - ")
|
||||
url.contains("drive.google.com") -> {
|
||||
val id = Regex("[\\w-]{28,}").find(url)?.groupValues?.get(0) ?: return emptyList()
|
||||
gdriveExtractor.videosFromUrl("https://drive.google.com/uc?id=$id", videoName = name)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// ============================= Utilities ==============================
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
|
||||
val server = preferences.getString(prefServerKey, prefServerDefault)!!
|
||||
|
||||
return sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(server, true) },
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
// ============================== Settings ==============================
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
super.setupPreferenceScreen(screen) // Quality preferences
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = prefServerKey
|
||||
title = "Preferred server"
|
||||
entries = arrayOf("CDA", "Sibnet", "Google Drive", "ok.ru")
|
||||
entryValues = arrayOf("CDA", "sibnet", "gd", "okru")
|
||||
setDefaultValue(prefServerDefault)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pl.desuonline.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parseAs
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonArray
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class CDAExtractor(private val client: OkHttpClient, private val headers: Headers, private val referer: String) {
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
fun videosFromUrl(url: String, name: String): List<Video> {
|
||||
val urlHost = url.toHttpUrl().host
|
||||
|
||||
val docHeaders = headers.newBuilder().apply {
|
||||
add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
|
||||
add("Host", urlHost)
|
||||
add("Referer", referer)
|
||||
}.build()
|
||||
val doc = client.newCall(
|
||||
GET(url, headers = docHeaders),
|
||||
).execute().asJsoup()
|
||||
|
||||
val playerData = doc.selectFirst("div[id~=mediaplayer][player_data]")
|
||||
?.attr("player_data")
|
||||
?.let { json.decodeFromString<PlayerData>(it) }
|
||||
?: return emptyList()
|
||||
|
||||
val timestamp = playerData.api.ts.substringBefore("_")
|
||||
val videoData = playerData.video
|
||||
|
||||
var idCounter = 1
|
||||
return videoData.qualities.map { (quality, qualityId) ->
|
||||
val postBody = json.encodeToString(
|
||||
buildJsonObject {
|
||||
put("id", idCounter)
|
||||
put("jsonrpc", "2.0")
|
||||
put("method", "videoGetLink")
|
||||
putJsonArray("params") {
|
||||
add(url.toHttpUrl().pathSegments.last())
|
||||
add(qualityId)
|
||||
add(timestamp.toInt())
|
||||
add(videoData.hash2)
|
||||
}
|
||||
},
|
||||
).toRequestBody("application/json; charset=utf-8".toMediaType())
|
||||
|
||||
val postHeaders = headers.newBuilder().apply {
|
||||
add("Accept", "application/json, text/javascript, */*; q=0.01")
|
||||
add("Host", "www.cda.pl")
|
||||
add("Origin", "https://$urlHost")
|
||||
add("Referer", url)
|
||||
}.build()
|
||||
|
||||
val videoUrl = client.newCall(
|
||||
POST("https://www.cda.pl/", headers = postHeaders, body = postBody),
|
||||
).execute().parseAs<PostResponse>().result.resp
|
||||
|
||||
idCounter++
|
||||
|
||||
Video(videoUrl, "$name - $quality", videoUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class PlayerData(
|
||||
val api: PlayerApi,
|
||||
val video: PlayerVideoData,
|
||||
) {
|
||||
@Serializable
|
||||
data class PlayerApi(
|
||||
val ts: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PlayerVideoData(
|
||||
val hash2: String,
|
||||
val qualities: Map<String, String>,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class PostResponse(
|
||||
val result: PostResult,
|
||||
) {
|
||||
@Serializable
|
||||
data class PostResult(
|
||||
val resp: String,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue