fix(all/googledrive): Fix single items, add support for search, small… (#2216)
… code refactor, fix video extractor (#2216)
This commit is contained in:
parent
b5fdaf0b9f
commit
b50e3a0b75
@ -39,8 +39,18 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
|
|||||||
add("Cookie", getCookie(itemUrl))
|
add("Cookie", getCookie(itemUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
val document = client.newCall(GET(itemUrl, itemHeaders)).execute()
|
val documentResp = noRedirectClient.newCall(
|
||||||
.use { it.asJsoup() }
|
GET(itemUrl, itemHeaders)
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
if (documentResp.isRedirect) {
|
||||||
|
val newUrl = documentResp.use { it.headers["location"] }
|
||||||
|
?: return listOf(Video(itemUrl, videoName, itemUrl, itemHeaders))
|
||||||
|
|
||||||
|
return videoFromRedirect(newUrl, itemUrl, videoName)
|
||||||
|
}
|
||||||
|
|
||||||
|
val document = documentResp.use { it.asJsoup() }
|
||||||
|
|
||||||
val itemSize = document.selectFirst("span.uc-name-size")
|
val itemSize = document.selectFirst("span.uc-name-size")
|
||||||
?.let { " ${it.ownText().trim()} " }
|
?.let { " ${it.ownText().trim()} " }
|
||||||
@ -59,6 +69,15 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
|
|||||||
val redirected = response.use { it.headers["location"] }
|
val redirected = response.use { it.headers["location"] }
|
||||||
?: return listOf(Video(url, videoName + itemSize, url))
|
?: return listOf(Video(url, videoName + itemSize, url))
|
||||||
|
|
||||||
|
return videoFromRedirect(redirected, url, videoName, itemSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun videoFromRedirect(
|
||||||
|
redirected: String,
|
||||||
|
fallbackUrl: String,
|
||||||
|
videoName: String,
|
||||||
|
itemSize: String = ""
|
||||||
|
): List<Video> {
|
||||||
val redirectedHeaders = headersBuilder {
|
val redirectedHeaders = headersBuilder {
|
||||||
set("Host", redirected.toHttpUrl().host)
|
set("Host", redirected.toHttpUrl().host)
|
||||||
}
|
}
|
||||||
@ -69,7 +88,7 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
|
|||||||
|
|
||||||
val authCookie = redirectedResponseHeaders.firstOrNull {
|
val authCookie = redirectedResponseHeaders.firstOrNull {
|
||||||
it.first == "set-cookie" && it.second.startsWith("AUTH_")
|
it.first == "set-cookie" && it.second.startsWith("AUTH_")
|
||||||
}?.second?.substringBefore(";") ?: return listOf(Video(url, videoName + itemSize, url))
|
}?.second?.substringBefore(";") ?: return listOf(Video(fallbackUrl, videoName + itemSize, fallbackUrl))
|
||||||
|
|
||||||
val newRedirected = redirectedResponseHeaders["location"]
|
val newRedirected = redirectedResponseHeaders["location"]
|
||||||
?: return listOf(Video(redirected, videoName + itemSize, redirected))
|
?: return listOf(Video(redirected, videoName + itemSize, redirected))
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
apply plugin: 'com.android.application'
|
plugins {
|
||||||
apply plugin: 'kotlin-android'
|
alias(libs.plugins.android.application)
|
||||||
apply plugin: 'kotlinx-serialization'
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlin.serialization)
|
||||||
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
extName = 'Google Drive'
|
extName = 'Google Drive'
|
||||||
pkgNameSuffix = 'all.googledrive'
|
pkgNameSuffix = 'all.googledrive'
|
||||||
extClass = '.GoogleDrive'
|
extClass = '.GoogleDrive'
|
||||||
extVersionCode = 7
|
extVersionCode = 8
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,10 +31,12 @@ import okhttp3.ProtocolException
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.net.URLEncoder
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||||
@ -52,8 +54,6 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override val lang = "all"
|
override val lang = "all"
|
||||||
|
|
||||||
private var nextPageToken: String? = ""
|
|
||||||
|
|
||||||
override val supportsLatest = false
|
override val supportsLatest = false
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
@ -62,11 +62,22 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient
|
override val client: OkHttpClient = network.client
|
||||||
|
|
||||||
|
// Overriding headersBuilder() seems to cause issues with webview
|
||||||
|
private val getHeaders = headers.newBuilder().apply {
|
||||||
|
add("Accept", "*/*")
|
||||||
|
add("Connection", "keep-alive")
|
||||||
|
add("Cookie", getCookie("https://drive.google.com"))
|
||||||
|
add("Host", "drive.google.com")
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
private var nextPageToken: String? = ""
|
||||||
|
|
||||||
// ============================== Popular ===============================
|
// ============================== Popular ===============================
|
||||||
|
|
||||||
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> = Observable.just(parsePage(popularAnimeRequest(page), page))
|
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> =
|
||||||
|
Observable.just(parsePage(popularAnimeRequest(page), page))
|
||||||
|
|
||||||
override fun popularAnimeRequest(page: Int): Request {
|
override fun popularAnimeRequest(page: Int): Request {
|
||||||
require(!baseUrlInternal.isNullOrEmpty()) { "Enter drive path(s) in extension settings." }
|
require(!baseUrlInternal.isNullOrEmpty()) { "Enter drive path(s) in extension settings." }
|
||||||
@ -75,14 +86,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val folderId = match.groups["id"]!!.value
|
val folderId = match.groups["id"]!!.value
|
||||||
val recurDepth = match.groups["depth"]?.value ?: ""
|
val recurDepth = match.groups["depth"]?.value ?: ""
|
||||||
baseUrl = "https://drive.google.com/drive/folders/$folderId"
|
baseUrl = "https://drive.google.com/drive/folders/$folderId"
|
||||||
val driveHeaders = headers.newBuilder()
|
|
||||||
.add("Accept", "*/*")
|
|
||||||
.add("Connection", "keep-alive")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.add("Host", "drive.google.com")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return GET("https://drive.google.com/drive/folders/$folderId$recurDepth", headers = driveHeaders)
|
return GET(
|
||||||
|
"https://drive.google.com/drive/folders/$folderId$recurDepth",
|
||||||
|
headers = getHeaders,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeParse(response: Response): AnimesPage = throw Exception("Not used")
|
override fun popularAnimeParse(response: Response): AnimesPage = throw Exception("Not used")
|
||||||
@ -102,14 +110,21 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
query: String,
|
query: String,
|
||||||
filters: AnimeFilterList,
|
filters: AnimeFilterList,
|
||||||
): Observable<AnimesPage> {
|
): Observable<AnimesPage> {
|
||||||
require(query.isEmpty()) { "Search is disabled. Use search in webview and add it as a single folder in filters." }
|
|
||||||
|
|
||||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||||
val urlFilter = filterList.find { it is URLFilter } as URLFilter
|
val urlFilter = filterList.find { it is URLFilter } as URLFilter
|
||||||
|
|
||||||
return if (urlFilter.state.isEmpty()) {
|
return if (urlFilter.state.isEmpty()) {
|
||||||
val req = searchAnimeRequest(page, query, filters)
|
val req = searchAnimeRequest(page, query, filters)
|
||||||
Observable.just(parsePage(req, page))
|
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
Observable.just(parsePage(req, page))
|
||||||
|
} else {
|
||||||
|
val parentId = req.url.pathSegments.last()
|
||||||
|
val cleanQuery = URLEncoder.encode(query, "UTF-8")
|
||||||
|
val genMultiFormReq = searchReq(parentId, cleanQuery)
|
||||||
|
|
||||||
|
Observable.just(parsePage(req, page, genMultiFormReq))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Observable.just(addSinglePage(urlFilter.state))
|
Observable.just(addSinglePage(urlFilter.state))
|
||||||
}
|
}
|
||||||
@ -126,14 +141,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val folderId = match.groups["id"]!!.value
|
val folderId = match.groups["id"]!!.value
|
||||||
val recurDepth = match.groups["depth"]?.value ?: ""
|
val recurDepth = match.groups["depth"]?.value ?: ""
|
||||||
baseUrl = "https://drive.google.com/drive/folders/$folderId"
|
baseUrl = "https://drive.google.com/drive/folders/$folderId"
|
||||||
val driveHeaders = headers.newBuilder()
|
|
||||||
.add("Accept", "*/*")
|
|
||||||
.add("Connection", "keep-alive")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.add("Host", "drive.google.com")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return GET("https://drive.google.com/drive/folders/$folderId$recurDepth", headers = driveHeaders)
|
return GET(
|
||||||
|
"https://drive.google.com/drive/folders/$folderId$recurDepth",
|
||||||
|
headers = getHeaders,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== FILTERS ===============================
|
// ============================== FILTERS ===============================
|
||||||
@ -171,7 +183,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsRequest(anime: SAnime): Request {
|
override fun animeDetailsRequest(anime: SAnime): Request {
|
||||||
val parsed = json.decodeFromString<LinkData>(anime.url)
|
val parsed = json.decodeFromString<LinkData>(anime.url)
|
||||||
return GET(parsed.url)
|
return GET(parsed.url, headers = getHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
||||||
@ -180,62 +192,21 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
if (parsed.type == "single") return Observable.just(anime)
|
if (parsed.type == "single") return Observable.just(anime)
|
||||||
|
|
||||||
val folderId = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!!.groups["id"]!!.value
|
val folderId = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!!.groups["id"]!!.value
|
||||||
val driveHeaders = headers.newBuilder()
|
|
||||||
.add("Accept", "*/*")
|
|
||||||
.add("Connection", "keep-alive")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.add("Host", "drive.google.com")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val driveDocument = try {
|
val driveDocument = try {
|
||||||
client.newCall(GET(parsed.url, headers = driveHeaders)).execute().asJsoup()
|
client.newCall(GET(parsed.url, headers = getHeaders)).execute().asJsoup()
|
||||||
} catch (a: ProtocolException) {
|
} catch (a: ProtocolException) {
|
||||||
null
|
null
|
||||||
} ?: return Observable.just(anime)
|
} ?: return Observable.just(anime)
|
||||||
|
|
||||||
if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) return Observable.just(anime)
|
if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) {
|
||||||
|
return Observable.just(anime)
|
||||||
val keyScript = driveDocument.select("script").firstOrNull { script ->
|
}
|
||||||
KEY_REGEX.find(script.data()) != null
|
|
||||||
}?.data() ?: return Observable.just(anime)
|
|
||||||
val key = KEY_REGEX.find(keyScript)?.groupValues?.get(1) ?: ""
|
|
||||||
|
|
||||||
val versionScript = driveDocument.select("script").first { script ->
|
|
||||||
KEY_REGEX.find(script.data()) != null
|
|
||||||
}.data()
|
|
||||||
val driveVersion = VERSION_REGEX.find(versionScript)?.groupValues?.get(1) ?: ""
|
|
||||||
val sapisid = client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).firstOrNull {
|
|
||||||
it.name == "SAPISID" || it.name == "__Secure-3PAPISID"
|
|
||||||
}?.value ?: ""
|
|
||||||
|
|
||||||
var pageToken: String? = ""
|
var pageToken: String? = ""
|
||||||
while (pageToken != null) {
|
while (pageToken != null) {
|
||||||
val requestUrl = "/drive/v2internal/files?openDrive=false&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2ChasVisitorPermissions%2CcontainsUnsubscribedChildren%2CmodifiedByMeDate%2ClastViewedByMeDate%2CalternateLink%2CfileSize%2Cowners(kind%2CpermissionId%2CemailAddressFromAccount%2Cdomain%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CcustomerId%2CancestorHasAugmentedPermissions%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2CabuseIsAppealable%2CabuseNoticeReason%2Cshared%2CaccessRequestsCount%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2Csubscribed%2CfolderColor%2ChasChildFolders%2CfileExtension%2CprimarySyncParentId%2CsharingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CflaggedForAbuse%2CfolderFeatures%2Cspaces%2CsourceAppId%2Crecency%2CrecencyReason%2Cversion%2CactionItems%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CprimaryDomainName%2CorganizationDisplayName%2CpassivelySubscribed%2CtrashingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CtrashedDate%2Cparents(id)%2Ccapabilities(canMoveItemIntoTeamDrive%2CcanUntrash%2CcanMoveItemWithinTeamDrive%2CcanMoveItemOutOfTeamDrive%2CcanDeleteChildren%2CcanTrashChildren%2CcanRequestApproval%2CcanReadCategoryMetadata%2CcanEditCategoryMetadata%2CcanAddMyDriveParent%2CcanRemoveMyDriveParent%2CcanShareChildFiles%2CcanShareChildFolders%2CcanRead%2CcanMoveItemWithinDrive%2CcanMoveChildrenWithinDrive%2CcanAddFolderFromAnotherDrive%2CcanChangeSecurityUpdateEnabled%2CcanBlockOwner%2CcanReportSpamOrAbuse%2CcanCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2CcontentRestrictions(readOnly)%2CapprovalMetadata(approvalVersion%2CapprovalSummaries%2ChasIncomingApproval)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus%2CtargetFile%2CcanRequestAccessToTarget)%2CspamMetadata(markedAsSpamDate%2CinSpamView)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=100&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
|
||||||
val body = """--$BOUNDARY
|
|
||||||
|content-type: application/http
|
|
||||||
|content-transfer-encoding: binary
|
|
||||||
|
|
|
||||||
|GET $requestUrl
|
|
||||||
|X-Goog-Drive-Client-Version: $driveVersion
|
|
||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|
||||||
|x-goog-authuser: 0
|
|
||||||
|
|
|
||||||
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = buildString {
|
|
||||||
append("https://clients6.google.com/batch/drive/v2internal")
|
|
||||||
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
|
||||||
append("&key=$key")
|
|
||||||
}
|
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
.add("Origin", "https://drive.google.com")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
createPost(driveDocument, folderId),
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
val parsed = response.parseAs<PostResponse> {
|
val parsed = response.parseAs<PostResponse> {
|
||||||
@ -263,6 +234,20 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val episodeList = mutableListOf<SEpisode>()
|
val episodeList = mutableListOf<SEpisode>()
|
||||||
val parsed = json.decodeFromString<LinkData>(anime.url)
|
val parsed = json.decodeFromString<LinkData>(anime.url)
|
||||||
|
|
||||||
|
if (parsed.type == "single") {
|
||||||
|
return Observable.just(
|
||||||
|
listOf(
|
||||||
|
SEpisode.create().apply {
|
||||||
|
name = "Video"
|
||||||
|
scanlator = parsed.info!!.size
|
||||||
|
url = parsed.url
|
||||||
|
episode_number = 1F
|
||||||
|
date_upload = -1L
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val match = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!! // .groups["id"]!!.value
|
val match = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!! // .groups["id"]!!.value
|
||||||
val maxRecursionDepth = match.groups["depth"]?.let {
|
val maxRecursionDepth = match.groups["depth"]?.let {
|
||||||
it.value.substringAfter("#").substringBefore(",").toInt()
|
it.value.substringAfter("#").substringBefore(",").toInt()
|
||||||
@ -275,63 +260,21 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
if (recursionDepth == maxRecursionDepth) return
|
if (recursionDepth == maxRecursionDepth) return
|
||||||
|
|
||||||
val folderId = DRIVE_FOLDER_REGEX.matchEntire(folderUrl)!!.groups["id"]!!.value
|
val folderId = DRIVE_FOLDER_REGEX.matchEntire(folderUrl)!!.groups["id"]!!.value
|
||||||
val driveHeaders = headers.newBuilder()
|
|
||||||
.add("Accept", "*/*")
|
|
||||||
.add("Connection", "keep-alive")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.add("Host", "drive.google.com")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val driveDocument = try {
|
val driveDocument = try {
|
||||||
client.newCall(GET(folderUrl, headers = driveHeaders)).execute().asJsoup()
|
client.newCall(GET(folderUrl, headers = getHeaders)).execute().asJsoup()
|
||||||
} catch (a: ProtocolException) {
|
} catch (a: ProtocolException) {
|
||||||
throw Exception("Unable to get items, check webview")
|
throw Exception("Unable to get items, check webview")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) return
|
if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) return
|
||||||
|
|
||||||
val keyScript = driveDocument.select("script").firstOrNull { script ->
|
|
||||||
KEY_REGEX.find(script.data()) != null
|
|
||||||
}?.data() ?: throw Exception("Unknown error occured, check webview")
|
|
||||||
val key = KEY_REGEX.find(keyScript)?.groupValues?.get(1) ?: ""
|
|
||||||
|
|
||||||
val versionScript = driveDocument.select("script").first { script ->
|
|
||||||
KEY_REGEX.find(script.data()) != null
|
|
||||||
}.data()
|
|
||||||
val driveVersion = VERSION_REGEX.find(versionScript)?.groupValues?.get(1) ?: ""
|
|
||||||
val sapisid = client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).firstOrNull {
|
|
||||||
it.name == "SAPISID" || it.name == "__Secure-3PAPISID"
|
|
||||||
}?.value ?: ""
|
|
||||||
|
|
||||||
var pageToken: String? = ""
|
var pageToken: String? = ""
|
||||||
var counter = 1
|
var counter = 1
|
||||||
|
|
||||||
while (pageToken != null) {
|
while (pageToken != null) {
|
||||||
val requestUrl = "/drive/v2internal/files?openDrive=false&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2ChasVisitorPermissions%2CcontainsUnsubscribedChildren%2CmodifiedByMeDate%2ClastViewedByMeDate%2CalternateLink%2CfileSize%2Cowners(kind%2CpermissionId%2CemailAddressFromAccount%2Cdomain%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CcustomerId%2CancestorHasAugmentedPermissions%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2CabuseIsAppealable%2CabuseNoticeReason%2Cshared%2CaccessRequestsCount%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2Csubscribed%2CfolderColor%2ChasChildFolders%2CfileExtension%2CprimarySyncParentId%2CsharingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CflaggedForAbuse%2CfolderFeatures%2Cspaces%2CsourceAppId%2Crecency%2CrecencyReason%2Cversion%2CactionItems%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CprimaryDomainName%2CorganizationDisplayName%2CpassivelySubscribed%2CtrashingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CtrashedDate%2Cparents(id)%2Ccapabilities(canMoveItemIntoTeamDrive%2CcanUntrash%2CcanMoveItemWithinTeamDrive%2CcanMoveItemOutOfTeamDrive%2CcanDeleteChildren%2CcanTrashChildren%2CcanRequestApproval%2CcanReadCategoryMetadata%2CcanEditCategoryMetadata%2CcanAddMyDriveParent%2CcanRemoveMyDriveParent%2CcanShareChildFiles%2CcanShareChildFolders%2CcanRead%2CcanMoveItemWithinDrive%2CcanMoveChildrenWithinDrive%2CcanAddFolderFromAnotherDrive%2CcanChangeSecurityUpdateEnabled%2CcanBlockOwner%2CcanReportSpamOrAbuse%2CcanCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2CcontentRestrictions(readOnly)%2CapprovalMetadata(approvalVersion%2CapprovalSummaries%2ChasIncomingApproval)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus%2CtargetFile%2CcanRequestAccessToTarget)%2CspamMetadata(markedAsSpamDate%2CinSpamView)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=100&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
|
||||||
val body = """--$BOUNDARY
|
|
||||||
|content-type: application/http
|
|
||||||
|content-transfer-encoding: binary
|
|
||||||
|
|
|
||||||
|GET $requestUrl
|
|
||||||
|X-Goog-Drive-Client-Version: $driveVersion
|
|
||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|
||||||
|x-goog-authuser: 0
|
|
||||||
|
|
|
||||||
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = buildString {
|
|
||||||
append("https://clients6.google.com/batch/drive/v2internal")
|
|
||||||
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
|
||||||
append("&key=$key")
|
|
||||||
}
|
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
.add("Origin", "https://drive.google.com")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
createPost(driveDocument, folderId),
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
val parsed = response.parseAs<PostResponse> {
|
val parsed = response.parseAs<PostResponse> {
|
||||||
@ -342,7 +285,6 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
parsed.items.forEachIndexed { index, it ->
|
parsed.items.forEachIndexed { index, it ->
|
||||||
if (it.mimeType.startsWith("video")) {
|
if (it.mimeType.startsWith("video")) {
|
||||||
val size = it.fileSize?.toLongOrNull()?.let { formatBytes(it) } ?: ""
|
val size = it.fileSize?.toLongOrNull()?.let { formatBytes(it) } ?: ""
|
||||||
val itemNumberRegex = """ - (?:S\d+E)?(\d+)""".toRegex()
|
|
||||||
val pathName = if (preferences.trimEpisodeInfo) path.trimInfo() else path
|
val pathName = if (preferences.trimEpisodeInfo) path.trimInfo() else path
|
||||||
|
|
||||||
if (start != null && maxRecursionDepth == 1 && counter < start) {
|
if (start != null && maxRecursionDepth == 1 && counter < start) {
|
||||||
@ -353,9 +295,12 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
episodeList.add(
|
episodeList.add(
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
name = if (preferences.trimEpisodeName) it.title.trimInfo() else it.title
|
name =
|
||||||
|
if (preferences.trimEpisodeName) it.title.trimInfo() else it.title
|
||||||
url = "https://drive.google.com/uc?id=${it.id}"
|
url = "https://drive.google.com/uc?id=${it.id}"
|
||||||
episode_number = itemNumberRegex.find(it.title.trimInfo())?.groupValues?.get(1)?.toFloatOrNull() ?: index.toFloat()
|
episode_number =
|
||||||
|
ITEM_NUMBER_REGEX.find(it.title.trimInfo())?.groupValues?.get(1)
|
||||||
|
?.toFloatOrNull() ?: (index + 1).toFloat()
|
||||||
date_upload = -1L
|
date_upload = -1L
|
||||||
scanlator = if (preferences.scanlatorOrder) {
|
scanlator = if (preferences.scanlatorOrder) {
|
||||||
"/$pathName • $size"
|
"/$pathName • $size"
|
||||||
@ -379,19 +324,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsed.type == "single") {
|
traverseFolder(parsed.url, "")
|
||||||
episodeList.add(
|
|
||||||
SEpisode.create().apply {
|
|
||||||
name = parsed.info!!.title
|
|
||||||
scanlator = parsed.info.size
|
|
||||||
url = parsed.url
|
|
||||||
episode_number = 1F
|
|
||||||
date_upload = -1L
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
traverseFolder(parsed.url, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Observable.just(episodeList.reversed())
|
return Observable.just(episodeList.reversed())
|
||||||
}
|
}
|
||||||
@ -406,11 +339,13 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
private fun addSinglePage(folderUrl: String): AnimesPage {
|
private fun addSinglePage(folderUrl: String): AnimesPage {
|
||||||
val match = DRIVE_FOLDER_REGEX.matchEntire(folderUrl) ?: throw Exception("Invalid drive url")
|
val match =
|
||||||
|
DRIVE_FOLDER_REGEX.matchEntire(folderUrl) ?: throw Exception("Invalid drive url")
|
||||||
val recurDepth = match.groups["depth"]?.value ?: ""
|
val recurDepth = match.groups["depth"]?.value ?: ""
|
||||||
|
|
||||||
val anime = SAnime.create().apply {
|
val anime = SAnime.create().apply {
|
||||||
title = match.groups["name"]?.value?.substringAfter("[")?.substringBeforeLast("]") ?: "Folder"
|
title = match.groups["name"]?.value?.substringAfter("[")?.substringBeforeLast("]")
|
||||||
|
?: "Folder"
|
||||||
url = LinkData(
|
url = LinkData(
|
||||||
"https://drive.google.com/drive/folders/${match.groups["id"]!!.value}$recurDepth",
|
"https://drive.google.com/drive/folders/${match.groups["id"]!!.value}$recurDepth",
|
||||||
"multi",
|
"multi",
|
||||||
@ -420,7 +355,60 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return AnimesPage(listOf(anime), false)
|
return AnimesPage(listOf(anime), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parsePage(request: Request, page: Int): AnimesPage {
|
private fun createPost(
|
||||||
|
document: Document,
|
||||||
|
folderId: String,
|
||||||
|
getMultiFormPath: (String, String, String) -> String = { folderIdStr, nextPageTokenStr, keyStr ->
|
||||||
|
defaultGetRequest(folderIdStr, nextPageTokenStr, keyStr)
|
||||||
|
},
|
||||||
|
): Request {
|
||||||
|
val keyScript = document.select("script").first { script ->
|
||||||
|
KEY_REGEX.find(script.data()) != null
|
||||||
|
}.data()
|
||||||
|
val key = KEY_REGEX.find(keyScript)?.groupValues?.get(1) ?: ""
|
||||||
|
|
||||||
|
val versionScript = document.select("script").first { script ->
|
||||||
|
KEY_REGEX.find(script.data()) != null
|
||||||
|
}.data()
|
||||||
|
val driveVersion = VERSION_REGEX.find(versionScript)?.groupValues?.get(1) ?: ""
|
||||||
|
val sapisid =
|
||||||
|
client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).firstOrNull {
|
||||||
|
it.name == "SAPISID" || it.name == "__Secure-3PAPISID"
|
||||||
|
}?.value ?: ""
|
||||||
|
|
||||||
|
val requestUrl = getMultiFormPath(folderId, nextPageToken ?: "", key)
|
||||||
|
val body = """--$BOUNDARY
|
||||||
|
|content-type: application/http
|
||||||
|
|content-transfer-encoding: binary
|
||||||
|
|
|
||||||
|
|GET $requestUrl
|
||||||
|
|X-Goog-Drive-Client-Version: $driveVersion
|
||||||
|
|authorization: ${generateSapisidhashHeader(sapisid)}
|
||||||
|
|x-goog-authuser: 0
|
||||||
|
|
|
||||||
|
|--$BOUNDARY--""".trimMargin("|")
|
||||||
|
.toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
||||||
|
|
||||||
|
val postUrl = buildString {
|
||||||
|
append("https://clients6.google.com/batch/drive/v2internal")
|
||||||
|
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
||||||
|
append("&key=$key")
|
||||||
|
}
|
||||||
|
|
||||||
|
val postHeaders = headers.newBuilder().apply {
|
||||||
|
add("Content-Type", "text/plain; charset=UTF-8")
|
||||||
|
add("Origin", "https://drive.google.com")
|
||||||
|
add("Cookie", getCookie("https://drive.google.com"))
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return POST(postUrl, body = body, headers = postHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parsePage(
|
||||||
|
request: Request,
|
||||||
|
page: Int,
|
||||||
|
genMultiFormReq: ((String, String, String) -> String)? = null,
|
||||||
|
): AnimesPage {
|
||||||
val animeList = mutableListOf<SAnime>()
|
val animeList = mutableListOf<SAnime>()
|
||||||
|
|
||||||
val recurDepth = request.url.encodedFragment?.let { "#$it" } ?: ""
|
val recurDepth = request.url.encodedFragment?.let { "#$it" } ?: ""
|
||||||
@ -437,47 +425,17 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return AnimesPage(emptyList(), false)
|
return AnimesPage(emptyList(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val keyScript = driveDocument.select("script").first { script ->
|
|
||||||
KEY_REGEX.find(script.data()) != null
|
|
||||||
}.data()
|
|
||||||
val key = KEY_REGEX.find(keyScript)?.groupValues?.get(1) ?: ""
|
|
||||||
|
|
||||||
val versionScript = driveDocument.select("script").first { script ->
|
|
||||||
KEY_REGEX.find(script.data()) != null
|
|
||||||
}.data()
|
|
||||||
val driveVersion = VERSION_REGEX.find(versionScript)?.groupValues?.get(1) ?: ""
|
|
||||||
val sapisid = client.cookieJar.loadForRequest("https://drive.google.com".toHttpUrl()).firstOrNull {
|
|
||||||
it.name == "SAPISID" || it.name == "__Secure-3PAPISID"
|
|
||||||
}?.value ?: ""
|
|
||||||
|
|
||||||
if (page == 1) nextPageToken = ""
|
if (page == 1) nextPageToken = ""
|
||||||
val requestUrl = "/drive/v2internal/files?openDrive=false&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2ChasVisitorPermissions%2CcontainsUnsubscribedChildren%2CmodifiedByMeDate%2ClastViewedByMeDate%2CalternateLink%2CfileSize%2Cowners(kind%2CpermissionId%2CemailAddressFromAccount%2Cdomain%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CcustomerId%2CancestorHasAugmentedPermissions%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2CabuseIsAppealable%2CabuseNoticeReason%2Cshared%2CaccessRequestsCount%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2Csubscribed%2CfolderColor%2ChasChildFolders%2CfileExtension%2CprimarySyncParentId%2CsharingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CflaggedForAbuse%2CfolderFeatures%2Cspaces%2CsourceAppId%2Crecency%2CrecencyReason%2Cversion%2CactionItems%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CprimaryDomainName%2CorganizationDisplayName%2CpassivelySubscribed%2CtrashingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CtrashedDate%2Cparents(id)%2Ccapabilities(canMoveItemIntoTeamDrive%2CcanUntrash%2CcanMoveItemWithinTeamDrive%2CcanMoveItemOutOfTeamDrive%2CcanDeleteChildren%2CcanTrashChildren%2CcanRequestApproval%2CcanReadCategoryMetadata%2CcanEditCategoryMetadata%2CcanAddMyDriveParent%2CcanRemoveMyDriveParent%2CcanShareChildFiles%2CcanShareChildFolders%2CcanRead%2CcanMoveItemWithinDrive%2CcanMoveChildrenWithinDrive%2CcanAddFolderFromAnotherDrive%2CcanChangeSecurityUpdateEnabled%2CcanBlockOwner%2CcanReportSpamOrAbuse%2CcanCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2CcontentRestrictions(readOnly)%2CapprovalMetadata(approvalVersion%2CapprovalSummaries%2ChasIncomingApproval)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus%2CtargetFile%2CcanRequestAccessToTarget)%2CspamMetadata(markedAsSpamDate%2CinSpamView)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$nextPageToken&maxResults=100&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
val post = if (genMultiFormReq == null) {
|
||||||
val body = """--$BOUNDARY
|
createPost(driveDocument, folderId)
|
||||||
|content-type: application/http
|
} else {
|
||||||
|content-transfer-encoding: binary
|
createPost(
|
||||||
|
|
driveDocument,
|
||||||
|GET $requestUrl
|
folderId,
|
||||||
|X-Goog-Drive-Client-Version: $driveVersion
|
genMultiFormReq,
|
||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
)
|
||||||
|x-goog-authuser: 0
|
|
||||||
|
|
|
||||||
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = buildString {
|
|
||||||
append("https://clients6.google.com/batch/drive/v2internal")
|
|
||||||
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
|
||||||
append("&key=$key")
|
|
||||||
}
|
}
|
||||||
|
val response = client.newCall(post).execute()
|
||||||
val postHeaders = headers.newBuilder()
|
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
.add("Origin", "https://drive.google.com")
|
|
||||||
.add("Cookie", getCookie("https://drive.google.com"))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val response = client.newCall(
|
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
|
||||||
).execute()
|
|
||||||
|
|
||||||
val parsed = response.parseAs<PostResponse> {
|
val parsed = response.parseAs<PostResponse> {
|
||||||
JSON_REGEX.find(it)!!.groupValues[1]
|
JSON_REGEX.find(it)!!.groupValues[1]
|
||||||
@ -492,7 +450,10 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
url = LinkData(
|
url = LinkData(
|
||||||
"https://drive.google.com/uc?id=${it.id}",
|
"https://drive.google.com/uc?id=${it.id}",
|
||||||
"single",
|
"single",
|
||||||
LinkDataInfo(it.title, it.fileSize?.toLongOrNull()?.let { formatBytes(it) } ?: ""),
|
LinkDataInfo(
|
||||||
|
it.title,
|
||||||
|
it.fileSize?.toLongOrNull()?.let { formatBytes(it) } ?: "",
|
||||||
|
),
|
||||||
).toJsonString()
|
).toJsonString()
|
||||||
thumbnail_url = ""
|
thumbnail_url = ""
|
||||||
},
|
},
|
||||||
@ -523,7 +484,10 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/yt-dlp/yt-dlp/blob/8f0be90ecb3b8d862397177bb226f17b245ef933/yt_dlp/extractor/youtube.py#L573
|
// https://github.com/yt-dlp/yt-dlp/blob/8f0be90ecb3b8d862397177bb226f17b245ef933/yt_dlp/extractor/youtube.py#L573
|
||||||
private fun generateSapisidhashHeader(SAPISID: String, origin: String = "https://drive.google.com"): String {
|
private fun generateSapisidhashHeader(
|
||||||
|
SAPISID: String,
|
||||||
|
origin: String = "https://drive.google.com",
|
||||||
|
): String {
|
||||||
val timeNow = System.currentTimeMillis() / 1000
|
val timeNow = System.currentTimeMillis() / 1000
|
||||||
// SAPISIDHASH algorithm from https://stackoverflow.com/a/32065323
|
// SAPISIDHASH algorithm from https://stackoverflow.com/a/32065323
|
||||||
val sapisidhash = MessageDigest
|
val sapisidhash = MessageDigest
|
||||||
@ -582,7 +546,12 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
editText.addTextChangedListener(
|
editText.addTextChangedListener(
|
||||||
object : TextWatcher {
|
object : TextWatcher {
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
override fun beforeTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
count: Int,
|
||||||
|
after: Int,
|
||||||
|
) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +569,13 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.all(::isFolder)
|
.all(::isFolder)
|
||||||
|
|
||||||
editText.error = if (!isValid) "${text.split(";").first { !isFolder(it) }} is not a valid google drive folder" else null
|
editText.error = if (!isValid) {
|
||||||
|
"${
|
||||||
|
text.split(";").first { !isFolder(it) }
|
||||||
|
} is not a valid google drive folder"
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
editText.rootView.findViewById<Button>(android.R.id.button1)
|
editText.rootView.findViewById<Button>(android.R.id.button1)
|
||||||
?.isEnabled = editText.error == null
|
?.isEnabled = editText.error == null
|
||||||
}
|
}
|
||||||
@ -631,6 +606,8 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
private val VERSION_REGEX = Regex(""""([^"]+web-frontend[^"]+)"""")
|
private val VERSION_REGEX = Regex(""""([^"]+web-frontend[^"]+)"""")
|
||||||
private val JSON_REGEX = Regex("""(?:)\s*(\{(.+)\})\s*(?:)""", RegexOption.DOT_MATCHES_ALL)
|
private val JSON_REGEX = Regex("""(?:)\s*(\{(.+)\})\s*(?:)""", RegexOption.DOT_MATCHES_ALL)
|
||||||
private const val BOUNDARY = "=====vc17a3rwnndj====="
|
private const val BOUNDARY = "=====vc17a3rwnndj====="
|
||||||
|
|
||||||
|
private val ITEM_NUMBER_REGEX = Regex(""" - (?:S\d+E)?(\d+)\b""")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val SharedPreferences.domainList
|
private val SharedPreferences.domainList
|
||||||
@ -669,8 +646,13 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
try {
|
try {
|
||||||
val res = preferences.edit().putString(DOMAIN_PREF_KEY, newValue as String).commit()
|
val res =
|
||||||
Toast.makeText(screen.context, "Restart Aniyomi to apply changes", Toast.LENGTH_LONG).show()
|
preferences.edit().putString(DOMAIN_PREF_KEY, newValue as String).commit()
|
||||||
|
Toast.makeText(
|
||||||
|
screen.context,
|
||||||
|
"Restart Aniyomi to apply changes",
|
||||||
|
Toast.LENGTH_LONG,
|
||||||
|
).show()
|
||||||
res
|
res
|
||||||
} catch (e: java.lang.Exception) {
|
} catch (e: java.lang.Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
@ -16,16 +16,6 @@ data class PostResponse(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Details(
|
|
||||||
val title: String? = null,
|
|
||||||
val author: String? = null,
|
|
||||||
val artist: String? = null,
|
|
||||||
val description: String? = null,
|
|
||||||
val genre: List<String>? = null,
|
|
||||||
val status: Int? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class LinkData(
|
data class LinkData(
|
||||||
val url: String,
|
val url: String,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.googledrive
|
||||||
|
|
||||||
|
fun searchReq(parentId: String, query: String): (String, String, String) -> String {
|
||||||
|
return { _: String, nextPageToken: String, key: String ->
|
||||||
|
"/drive/v2internal/files?openDrive=false&reason=111&syncType=0&errorRecovery=false&q=title%20contains%20'$query'%20and%20(mimeType%20in%20'application%2Fvnd.google-apps.folder'%20or%20shortcutDetails.targetMimeType%20in%20'application%2Fvnd.google-apps.folder')%20and%20trashed%20%3D%20false%20and%20'$parentId'%20in%20ancestors&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2ChasVisitorPermissions%2CcontainsUnsubscribedChildren%2CmodifiedByMeDate%2ClastViewedByMeDate%2CalternateLink%2CfileSize%2Cowners(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CcustomerId%2CancestorHasAugmentedPermissions%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2CabuseIsAppealable%2CabuseNoticeReason%2Cshared%2CaccessRequestsCount%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2Csubscribed%2CfolderColor%2ChasChildFolders%2CfileExtension%2CprimarySyncParentId%2CsharingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CflaggedForAbuse%2CfolderFeatures%2Cspaces%2CsourceAppId%2Crecency%2CrecencyReason%2Cversion%2CactionItems%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CprimaryDomainName%2CorganizationDisplayName%2CpassivelySubscribed%2CtrashingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CtrashedDate%2Cparents(id)%2Ccapabilities(canMoveItemIntoTeamDrive%2CcanUntrash%2CcanModifyContentRestriction%2CcanMoveItemWithinTeamDrive%2CcanMoveItemOutOfTeamDrive%2CcanDeleteChildren%2CcanTrashChildren%2CcanRequestApproval%2CcanReadCategoryMetadata%2CcanEditCategoryMetadata%2CcanAddMyDriveParent%2CcanRemoveMyDriveParent%2CcanShareChildFiles%2CcanShareChildFolders%2CcanRead%2CcanMoveItemWithinDrive%2CcanMoveChildrenWithinDrive%2CcanAddFolderFromAnotherDrive%2CcanChangeSecurityUpdateEnabled%2CcanBlockOwner%2CcanReportSpamOrAbuse%2CcanCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2CcontentRestrictions(readOnly)%2CapprovalMetadata(approvalVersion%2CapprovalSummaries%2ChasIncomingApproval)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus%2CtargetFile%2CcanRequestAccessToTarget)%2CspamMetadata(markedAsSpamDate%2CinSpamView)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$nextPageToken&maxResults=50&rawUserQuery=parent%3A$parentId%20type%3Afolder%20title%3A$query&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=relevance%20desc&retryCount=0&key=$key HTTP/1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun defaultGetRequest(folderId: String, nextPageToken: String, key: String): String {
|
||||||
|
return "/drive/v2internal/files?openDrive=false&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2ChasVisitorPermissions%2CcontainsUnsubscribedChildren%2CmodifiedByMeDate%2ClastViewedByMeDate%2CalternateLink%2CfileSize%2Cowners(kind%2CpermissionId%2CemailAddressFromAccount%2Cdomain%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CcustomerId%2CancestorHasAugmentedPermissions%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2CabuseIsAppealable%2CabuseNoticeReason%2Cshared%2CaccessRequestsCount%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2Csubscribed%2CfolderColor%2ChasChildFolders%2CfileExtension%2CprimarySyncParentId%2CsharingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CflaggedForAbuse%2CfolderFeatures%2Cspaces%2CsourceAppId%2Crecency%2CrecencyReason%2Cversion%2CactionItems%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CprimaryDomainName%2CorganizationDisplayName%2CpassivelySubscribed%2CtrashingUser(kind%2CpermissionId%2CemailAddressFromAccount%2Cid)%2CtrashedDate%2Cparents(id)%2Ccapabilities(canMoveItemIntoTeamDrive%2CcanUntrash%2CcanMoveItemWithinTeamDrive%2CcanMoveItemOutOfTeamDrive%2CcanDeleteChildren%2CcanTrashChildren%2CcanRequestApproval%2CcanReadCategoryMetadata%2CcanEditCategoryMetadata%2CcanAddMyDriveParent%2CcanRemoveMyDriveParent%2CcanShareChildFiles%2CcanShareChildFolders%2CcanRead%2CcanMoveItemWithinDrive%2CcanMoveChildrenWithinDrive%2CcanAddFolderFromAnotherDrive%2CcanChangeSecurityUpdateEnabled%2CcanBlockOwner%2CcanReportSpamOrAbuse%2CcanCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2CcontentRestrictions(readOnly)%2CapprovalMetadata(approvalVersion%2CapprovalSummaries%2ChasIncomingApproval)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus%2CtargetFile%2CcanRequestAccessToTarget)%2CspamMetadata(markedAsSpamDate%2CinSpamView)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$nextPageToken&maxResults=100&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user