fix(src/all): Fix google drive based extensions (#3062)
Signed-off-by: Secozzi <folke.steen85@gmail.com>
This commit is contained in:
parent
ed2f4d6d13
commit
b2523ab859
@ -2,15 +2,11 @@ package eu.kanade.tachiyomi.lib.googledriveextractor
|
|||||||
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.Video
|
import eu.kanade.tachiyomi.animesource.model.Video
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
import okhttp3.CookieJar
|
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.internal.commonEmptyRequestBody
|
|
||||||
|
|
||||||
class GoogleDriveExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
class GoogleDriveExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||||
|
|
||||||
@ -20,28 +16,21 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
|
|||||||
|
|
||||||
private val cookieList = client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl())
|
private val cookieList = client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl())
|
||||||
|
|
||||||
private val noRedirectClient = OkHttpClient.Builder()
|
fun videosFromUrl(itemId: String, videoName: String = "Video"): List<Video> {
|
||||||
.followRedirects(false)
|
val url = "https://drive.usercontent.google.com/download?id=$itemId"
|
||||||
.build()
|
|
||||||
|
|
||||||
fun videosFromUrl(itemUrl: String, videoName: String = "Video"): List<Video> {
|
|
||||||
val cookieJar = GDriveCookieJar()
|
|
||||||
|
|
||||||
cookieJar.saveFromResponse("https://drive.google.com".toHttpUrl(), cookieList)
|
|
||||||
|
|
||||||
val docHeaders = headers.newBuilder().apply {
|
val docHeaders = headers.newBuilder().apply {
|
||||||
add("Accept", ACCEPT)
|
add("Accept", ACCEPT)
|
||||||
add("Connection", "keep-alive")
|
|
||||||
add("Cookie", cookieList.toStr())
|
add("Cookie", cookieList.toStr())
|
||||||
add("Host", "drive.google.com")
|
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val docResp = noRedirectClient.newCall(
|
val docResp = client.newCall(
|
||||||
GET(itemUrl, headers = docHeaders),
|
GET(url, docHeaders)
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
if (docResp.isRedirect) {
|
if (!docResp.peekBody(15).string().equals("<!DOCTYPE html>", true)) {
|
||||||
return videoFromRedirect(itemUrl, videoName, "", cookieJar)
|
return listOf(
|
||||||
|
Video(url, videoName, url, docHeaders)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val document = docResp.asJsoup()
|
val document = docResp.asJsoup()
|
||||||
@ -50,78 +39,14 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
|
|||||||
?.let { " ${it.ownText().trim()} " }
|
?.let { " ${it.ownText().trim()} " }
|
||||||
?: ""
|
?: ""
|
||||||
|
|
||||||
val downloadUrl = document.selectFirst("form#download-form")?.attr("action") ?: return emptyList()
|
val videoUrl = url.toHttpUrl().newBuilder().apply {
|
||||||
val postHeaders = headers.newBuilder().apply {
|
document.select("input[type=hidden]").forEach {
|
||||||
add("Accept", ACCEPT)
|
setQueryParameter(it.attr("name"), it.attr("value"))
|
||||||
add("Content-Type", "application/x-www-form-urlencoded")
|
}
|
||||||
set("Cookie", client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).toStr())
|
}.build().toString()
|
||||||
add("Host", "drive.google.com")
|
|
||||||
add("Referer", "https://drive.google.com/")
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
val newUrl = noRedirectClient.newCall(
|
|
||||||
POST(downloadUrl, headers = postHeaders, body = commonEmptyRequestBody),
|
|
||||||
).execute().use { it.headers["location"] ?: downloadUrl }
|
|
||||||
|
|
||||||
return videoFromRedirect(newUrl, videoName, itemSize, cookieJar)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun videoFromRedirect(
|
|
||||||
downloadUrl: String,
|
|
||||||
videoName: String,
|
|
||||||
itemSize: String,
|
|
||||||
cookieJar: GDriveCookieJar,
|
|
||||||
): List<Video> {
|
|
||||||
var newUrl = downloadUrl
|
|
||||||
|
|
||||||
val newHeaders = headers.newBuilder().apply {
|
|
||||||
add("Accept", ACCEPT)
|
|
||||||
set("Cookie", cookieJar.loadForRequest(newUrl.toHttpUrl()).toStr())
|
|
||||||
set("Host", newUrl.toHttpUrl().host)
|
|
||||||
add("Referer", "https://drive.google.com/")
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
var newResp = noRedirectClient.newCall(
|
|
||||||
GET(newUrl, headers = newHeaders),
|
|
||||||
).execute()
|
|
||||||
|
|
||||||
var redirectCounter = 1
|
|
||||||
while (newResp.isRedirect && redirectCounter < 15) {
|
|
||||||
val setCookies = newResp.headers("Set-Cookie").mapNotNull { Cookie.parse(newResp.request.url, it) }
|
|
||||||
cookieJar.saveFromResponse(newResp.request.url, setCookies)
|
|
||||||
|
|
||||||
newUrl = newResp.headers["location"]!!
|
|
||||||
newResp.close()
|
|
||||||
|
|
||||||
val newHeaders = headers.newBuilder().apply {
|
|
||||||
add("Accept", ACCEPT)
|
|
||||||
set("Cookie", cookieJar.loadForRequest(newUrl.toHttpUrl()).toStr())
|
|
||||||
set("Host", newUrl.toHttpUrl().host)
|
|
||||||
add("Referer", "https://drive.google.com/")
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
newResp = noRedirectClient.newCall(
|
|
||||||
GET(newUrl, headers = newHeaders),
|
|
||||||
).execute()
|
|
||||||
redirectCounter += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
val videoUrl = newResp.use { it.request.url }
|
|
||||||
|
|
||||||
val videoHeaders = headers.newBuilder().apply {
|
|
||||||
add("Accept", ACCEPT)
|
|
||||||
set("Cookie", cookieJar.loadForRequest(videoUrl).toStr())
|
|
||||||
set("Host", videoUrl.host)
|
|
||||||
add("Referer", "https://drive.google.com/")
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
Video(
|
Video(videoUrl, videoName + itemSize, videoUrl, docHeaders)
|
||||||
videoUrl.toString(),
|
|
||||||
videoName + itemSize,
|
|
||||||
videoUrl.toString(),
|
|
||||||
headers = videoHeaders,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,22 +54,3 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
|
|||||||
return this.joinToString("; ") { "${it.name}=${it.value}" }
|
return this.joinToString("; ") { "${it.name}=${it.value}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GDriveCookieJar : CookieJar {
|
|
||||||
|
|
||||||
private val cookieStore = mutableMapOf<String, MutableList<Cookie>>()
|
|
||||||
|
|
||||||
// Append rather than overwrite, what could go wrong?
|
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
|
||||||
val oldCookies = (cookieStore[url.host] ?: emptyList()).filter { c ->
|
|
||||||
!cookies.any { t -> c.name == t.name }
|
|
||||||
}
|
|
||||||
cookieStore[url.host] = (oldCookies + cookies).toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
|
||||||
val cookies = cookieStore[url.host] ?: emptyList()
|
|
||||||
|
|
||||||
return cookies.filter { it.expiresAt >= System.currentTimeMillis() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -357,7 +357,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
// ============================ Video Links =============================
|
// ============================ Video Links =============================
|
||||||
|
|
||||||
override suspend fun getVideoList(episode: SEpisode): List<Video> =
|
override suspend fun getVideoList(episode: SEpisode): List<Video> =
|
||||||
GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)
|
GoogleDriveExtractor(client, headers).videosFromUrl(episode.url.substringAfter("?id="))
|
||||||
|
|
||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
|
@ -68,8 +68,8 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
latestPost = ""
|
latestPost = ""
|
||||||
layout = ""
|
layout = ""
|
||||||
settings = ""
|
settings = ""
|
||||||
currentReferer = "https://kayoanime.com/ongoing-anime/"
|
currentReferer = "https://kayoanime.com/ongoing-animes/"
|
||||||
GET("$baseUrl/ongoing-anime/")
|
GET("$baseUrl/ongoing-animes/")
|
||||||
} else {
|
} else {
|
||||||
val formBody = FormBody.Builder()
|
val formBody = FormBody.Builder()
|
||||||
.add("action", "tie_archives_load_more")
|
.add("action", "tie_archives_load_more")
|
||||||
@ -206,11 +206,17 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchAnimeSelector(): String = popularAnimeSelector()
|
override fun searchAnimeParse(response: Response): AnimesPage =
|
||||||
|
popularAnimeParse(response)
|
||||||
|
|
||||||
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
|
override fun searchAnimeSelector(): String =
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
|
||||||
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
|
override fun searchAnimeFromElement(element: Element): SAnime =
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun searchAnimeNextPageSelector(): String =
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
|
||||||
// ============================== Filters ===============================
|
// ============================== Filters ===============================
|
||||||
|
|
||||||
@ -393,7 +399,8 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val url = it.selectFirst("a[href*=tinyurl.com]")!!.attr("href")
|
val url = it.selectFirst("a[href*=tinyurl.com]")!!.attr("href")
|
||||||
val redirected = noRedirectClient.newCall(GET(url)).execute()
|
val redirected = noRedirectClient.newCall(GET(url)).execute()
|
||||||
redirected.headers["location"]?.let { location ->
|
redirected.headers["location"]?.let { location ->
|
||||||
if (location.toHttpUrl().host.contains("workers.dev")) {
|
val host = location.toHttpUrl().host
|
||||||
|
if (host.contains("workers.dev")) {
|
||||||
episodeList.addAll(
|
episodeList.addAll(
|
||||||
indexExtractor.getEpisodesFromIndex(
|
indexExtractor.getEpisodesFromIndex(
|
||||||
location,
|
location,
|
||||||
@ -402,6 +409,14 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host.contains("slogoanime")) {
|
||||||
|
val document = client.newCall(GET(location)).execute().asJsoup()
|
||||||
|
document.select("a[href*=drive.google.com]").distinctBy { it.text() }.forEach {
|
||||||
|
val url = it.selectFirst("a[href*=drive.google.com]")!!.attr("href").substringBeforeLast("?usp=shar")
|
||||||
|
traverseFolder(url, getVideoPathsFromElement(season) + " " + it.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,18 +437,16 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
// ============================ Video Links =============================
|
// ============================ Video Links =============================
|
||||||
|
|
||||||
override suspend fun getVideoList(episode: SEpisode): List<Video> {
|
override suspend fun getVideoList(episode: SEpisode): List<Video> {
|
||||||
val host = episode.url.toHttpUrl().host
|
val httpUrl = episode.url.toHttpUrl()
|
||||||
val videoList = if (host == "drive.google.com") {
|
val host = httpUrl.host
|
||||||
GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)
|
return if (host == "drive.google.com") {
|
||||||
|
val id = httpUrl.queryParameter("id")!!
|
||||||
|
GoogleDriveExtractor(client, headers).videosFromUrl(id)
|
||||||
} else if (host.contains("workers.dev")) {
|
} else if (host.contains("workers.dev")) {
|
||||||
getIndexVideoUrl(episode.url)
|
getIndexVideoUrl(episode.url)
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Unsupported url: ${episode.url}")
|
throw Exception("Unsupported url: ${episode.url}")
|
||||||
}
|
}
|
||||||
|
|
||||||
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
|
|
||||||
|
|
||||||
return videoList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun videoListSelector(): String = throw UnsupportedOperationException()
|
override fun videoListSelector(): String = throw UnsupportedOperationException()
|
||||||
|
@ -55,7 +55,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/ongoing-series/")
|
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/ongoing-series/")
|
||||||
|
|
||||||
override fun popularAnimeSelector(): String = "section#movies-list > div.movies-box"
|
override fun popularAnimeSelector(): String = "section#movies-list div.movies-box"
|
||||||
|
|
||||||
override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
|
override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
|
||||||
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
|
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
|
||||||
@ -323,7 +323,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
// ============================ Video Links =============================
|
// ============================ Video Links =============================
|
||||||
|
|
||||||
override suspend fun getVideoList(episode: SEpisode): List<Video> {
|
override suspend fun getVideoList(episode: SEpisode): List<Video> {
|
||||||
val videoList = GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)
|
val videoList = GoogleDriveExtractor(client, headers).videosFromUrl(episode.url.substringAfter("?id="))
|
||||||
return videoList
|
return videoList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user