Skip to content

Commit

Permalink
fix(src/all): Fix google drive based extensions (#3062)
Browse files Browse the repository at this point in the history
Signed-off-by: Secozzi <[email protected]>
  • Loading branch information
Secozzi authored Mar 21, 2024
1 parent ed2f4d6 commit b2523ab
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@ package eu.kanade.tachiyomi.lib.googledriveextractor

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 okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.internal.commonEmptyRequestBody

class GoogleDriveExtractor(private val client: OkHttpClient, private val headers: Headers) {

Expand All @@ -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 noRedirectClient = OkHttpClient.Builder()
.followRedirects(false)
.build()

fun videosFromUrl(itemUrl: String, videoName: String = "Video"): List<Video> {
val cookieJar = GDriveCookieJar()

cookieJar.saveFromResponse("https://drive.google.com".toHttpUrl(), cookieList)

fun videosFromUrl(itemId: String, videoName: String = "Video"): List<Video> {
val url = "https://drive.usercontent.google.com/download?id=$itemId"
val docHeaders = headers.newBuilder().apply {
add("Accept", ACCEPT)
add("Connection", "keep-alive")
add("Cookie", cookieList.toStr())
add("Host", "drive.google.com")
}.build()

val docResp = noRedirectClient.newCall(
GET(itemUrl, headers = docHeaders),
val docResp = client.newCall(
GET(url, docHeaders)
).execute()

if (docResp.isRedirect) {
return videoFromRedirect(itemUrl, videoName, "", cookieJar)
if (!docResp.peekBody(15).string().equals("<!DOCTYPE html>", true)) {
return listOf(
Video(url, videoName, url, docHeaders)
)
}

val document = docResp.asJsoup()
Expand All @@ -50,101 +39,18 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
?.let { " ${it.ownText().trim()} " }
?: ""

val downloadUrl = document.selectFirst("form#download-form")?.attr("action") ?: return emptyList()
val postHeaders = headers.newBuilder().apply {
add("Accept", ACCEPT)
add("Content-Type", "application/x-www-form-urlencoded")
set("Cookie", client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).toStr())
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()
val videoUrl = url.toHttpUrl().newBuilder().apply {
document.select("input[type=hidden]").forEach {
setQueryParameter(it.attr("name"), it.attr("value"))
}
}.build().toString()

return listOf(
Video(
videoUrl.toString(),
videoName + itemSize,
videoUrl.toString(),
headers = videoHeaders,
),
Video(videoUrl, videoName + itemSize, videoUrl, docHeaders)
)
}

private fun List<Cookie>.toStr(): String {
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() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================ Video Links =============================

override suspend fun getVideoList(episode: SEpisode): List<Video> =
GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)
GoogleDriveExtractor(client, headers).videosFromUrl(episode.url.substringAfter("?id="))

// ============================= Utilities ==============================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
latestPost = ""
layout = ""
settings = ""
currentReferer = "https://kayoanime.com/ongoing-anime/"
GET("$baseUrl/ongoing-anime/")
currentReferer = "https://kayoanime.com/ongoing-animes/"
GET("$baseUrl/ongoing-animes/")
} else {
val formBody = FormBody.Builder()
.add("action", "tie_archives_load_more")
Expand Down Expand Up @@ -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 ===============================

Expand Down Expand Up @@ -393,7 +399,8 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val url = it.selectFirst("a[href*=tinyurl.com]")!!.attr("href")
val redirected = noRedirectClient.newCall(GET(url)).execute()
redirected.headers["location"]?.let { location ->
if (location.toHttpUrl().host.contains("workers.dev")) {
val host = location.toHttpUrl().host
if (host.contains("workers.dev")) {
episodeList.addAll(
indexExtractor.getEpisodesFromIndex(
location,
Expand All @@ -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())
}
}
}
}
}
Expand All @@ -422,18 +437,16 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ============================ Video Links =============================

override suspend fun getVideoList(episode: SEpisode): List<Video> {
val host = episode.url.toHttpUrl().host
val videoList = if (host == "drive.google.com") {
GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)
val httpUrl = episode.url.toHttpUrl()
val host = httpUrl.host
return if (host == "drive.google.com") {
val id = httpUrl.queryParameter("id")!!
GoogleDriveExtractor(client, headers).videosFromUrl(id)
} else if (host.contains("workers.dev")) {
getIndexVideoUrl(episode.url)
} else {
throw Exception("Unsupported url: ${episode.url}")
}

require(videoList.isNotEmpty()) { "Failed to fetch videos" }

return videoList
}

override fun videoListSelector(): String = throw UnsupportedOperationException()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {

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 {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
Expand Down Expand Up @@ -323,7 +323,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ============================ Video Links =============================

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
}

Expand Down

0 comments on commit b2523ab

Please sign in to comment.