fix(src): fix google drive based extensions. (#1906)
This commit is contained in:
@ -6,7 +6,7 @@ ext {
|
|||||||
extName = 'Google Drive'
|
extName = 'Google Drive'
|
||||||
pkgNameSuffix = 'all.googledrive'
|
pkgNameSuffix = 'all.googledrive'
|
||||||
extClass = '.GoogleDrive'
|
extClass = '.GoogleDrive'
|
||||||
extVersionCode = 6
|
extVersionCode = 7
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
var pageToken: String? = ""
|
var pageToken: String? = ""
|
||||||
while (pageToken != null) {
|
while (pageToken != null) {
|
||||||
val requestUrl = "/drive/v2beta/files?openDrive=true&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2CmodifiedByMeDate%2ClastViewedByMeDate%2CfileSize%2Cowners(kind%2CpermissionId%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2Cid)%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2Cshared%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2CfileExtension%2CsharingUser(kind%2CpermissionId%2Cid)%2Cspaces%2Cversion%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CtrashingUser(kind%2CpermissionId%2Cid)%2CtrashedDate%2Cparents(id)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus)%2Ccapabilities(canCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=50&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
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
|
val body = """--$BOUNDARY
|
||||||
|content-type: application/http
|
|content-type: application/http
|
||||||
|content-transfer-encoding: binary
|
|content-transfer-encoding: binary
|
||||||
@ -220,15 +220,13 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|authorization: ${generateSapisidhashHeader(sapisid)}
|
||||||
|x-goog-authuser: 0
|
|x-goog-authuser: 0
|
||||||
|
|
|
|
||||||
|--$BOUNDARY
|
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
||||||
|
|
|
||||||
""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = "https://clients6.google.com/batch/drive/v2beta".toHttpUrl().newBuilder()
|
val postUrl = buildString {
|
||||||
.addQueryParameter("${'$'}ct", "multipart/mixed;boundary=\"$BOUNDARY\"")
|
append("https://clients6.google.com/batch/drive/v2internal")
|
||||||
.addQueryParameter("key", key)
|
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
||||||
.build()
|
append("&key=$key")
|
||||||
.toString()
|
}
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
val postHeaders = headers.newBuilder()
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
.add("Content-Type", "text/plain; charset=UTF-8")
|
||||||
@ -239,9 +237,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
POST(postUrl, body = body, headers = postHeaders),
|
||||||
).execute()
|
).execute()
|
||||||
val parsed = json.decodeFromString<PostResponse>(
|
|
||||||
JSON_REGEX.find(response.body.string())!!.groupValues[1],
|
val parsed = response.parseAs<PostResponse> {
|
||||||
)
|
JSON_REGEX.find(it)!!.groupValues[1]
|
||||||
|
}
|
||||||
|
|
||||||
if (parsed.items == null) throw Exception("Failed to load items, please log in through webview")
|
if (parsed.items == null) throw Exception("Failed to load items, please log in through webview")
|
||||||
parsed.items.forEach {
|
parsed.items.forEach {
|
||||||
if (it.mimeType.startsWith("image/") && it.title.startsWith("cover.")) {
|
if (it.mimeType.startsWith("image/") && it.title.startsWith("cover.")) {
|
||||||
@ -306,7 +306,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
var pageToken: String? = ""
|
var pageToken: String? = ""
|
||||||
var counter = 1
|
var counter = 1
|
||||||
while (pageToken != null) {
|
while (pageToken != null) {
|
||||||
val requestUrl = "/drive/v2beta/files?openDrive=true&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2CmodifiedByMeDate%2ClastViewedByMeDate%2CfileSize%2Cowners(kind%2CpermissionId%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2Cid)%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2Cshared%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2CfileExtension%2CsharingUser(kind%2CpermissionId%2Cid)%2Cspaces%2Cversion%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CtrashingUser(kind%2CpermissionId%2Cid)%2CtrashedDate%2Cparents(id)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus)%2Ccapabilities(canCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=50&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
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
|
val body = """--$BOUNDARY
|
||||||
|content-type: application/http
|
|content-type: application/http
|
||||||
|content-transfer-encoding: binary
|
|content-transfer-encoding: binary
|
||||||
@ -316,15 +316,13 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|authorization: ${generateSapisidhashHeader(sapisid)}
|
||||||
|x-goog-authuser: 0
|
|x-goog-authuser: 0
|
||||||
|
|
|
|
||||||
|--$BOUNDARY
|
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
||||||
|
|
|
||||||
""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = "https://clients6.google.com/batch/drive/v2beta".toHttpUrl().newBuilder()
|
val postUrl = buildString {
|
||||||
.addQueryParameter("${'$'}ct", "multipart/mixed;boundary=\"$BOUNDARY\"")
|
append("https://clients6.google.com/batch/drive/v2internal")
|
||||||
.addQueryParameter("key", key)
|
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
||||||
.build()
|
append("&key=$key")
|
||||||
.toString()
|
}
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
val postHeaders = headers.newBuilder()
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
.add("Content-Type", "text/plain; charset=UTF-8")
|
||||||
@ -335,9 +333,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
POST(postUrl, body = body, headers = postHeaders),
|
||||||
).execute()
|
).execute()
|
||||||
val parsed = json.decodeFromString<PostResponse>(
|
|
||||||
JSON_REGEX.find(response.body.string())!!.groupValues[1],
|
val parsed = response.parseAs<PostResponse> {
|
||||||
)
|
JSON_REGEX.find(it)!!.groupValues[1]
|
||||||
|
}
|
||||||
|
|
||||||
if (parsed.items == null) throw Exception("Failed to load items, please log in through webview")
|
if (parsed.items == null) throw Exception("Failed to load items, please log in through webview")
|
||||||
parsed.items.forEachIndexed { index, it ->
|
parsed.items.forEachIndexed { index, it ->
|
||||||
if (it.mimeType.startsWith("video")) {
|
if (it.mimeType.startsWith("video")) {
|
||||||
@ -451,25 +451,23 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}?.value ?: ""
|
}?.value ?: ""
|
||||||
|
|
||||||
if (page == 1) nextPageToken = ""
|
if (page == 1) nextPageToken = ""
|
||||||
val requestUrl = "/drive/v2beta/files?openDrive=true&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2CmodifiedByMeDate%2ClastViewedByMeDate%2CfileSize%2Cowners(kind%2CpermissionId%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2Cid)%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2Cshared%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2CfileExtension%2CsharingUser(kind%2CpermissionId%2Cid)%2Cspaces%2Cversion%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CtrashingUser(kind%2CpermissionId%2Cid)%2CtrashedDate%2Cparents(id)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus)%2Ccapabilities(canCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$nextPageToken&maxResults=50&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
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 body = """--$BOUNDARY
|
val body = """--$BOUNDARY
|
||||||
|content-type: application/http
|
|content-type: application/http
|
||||||
|content-transfer-encoding: binary
|
|content-transfer-encoding: binary
|
||||||
|
|
|
|
||||||
|GET $requestUrl
|
|GET $requestUrl
|
||||||
|X-Goog-Drive-Client-Version: $driveVersion
|
|X-Goog-Drive-Client-Version: $driveVersion
|
||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|authorization: ${generateSapisidhashHeader(sapisid)}
|
||||||
|x-goog-authuser: 0
|
|x-goog-authuser: 0
|
||||||
|
|
|
|
||||||
|--$BOUNDARY
|
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
||||||
|
|
|
||||||
""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = "https://clients6.google.com/batch/drive/v2beta".toHttpUrl().newBuilder()
|
val postUrl = buildString {
|
||||||
.addQueryParameter("${'$'}ct", "multipart/mixed;boundary=\"$BOUNDARY\"")
|
append("https://clients6.google.com/batch/drive/v2internal")
|
||||||
.addQueryParameter("key", key)
|
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
||||||
.build()
|
append("&key=$key")
|
||||||
.toString()
|
}
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
val postHeaders = headers.newBuilder()
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
.add("Content-Type", "text/plain; charset=UTF-8")
|
||||||
@ -480,9 +478,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
POST(postUrl, body = body, headers = postHeaders),
|
||||||
).execute()
|
).execute()
|
||||||
val parsed = json.decodeFromString<PostResponse>(
|
|
||||||
JSON_REGEX.find(response.body.string())!!.groupValues[1],
|
val parsed = response.parseAs<PostResponse> {
|
||||||
)
|
JSON_REGEX.find(it)!!.groupValues[1]
|
||||||
|
}
|
||||||
|
|
||||||
if (parsed.items == null) throw Exception("Failed to load items, please log in through webview")
|
if (parsed.items == null) throw Exception("Failed to load items, please log in through webview")
|
||||||
parsed.items.forEachIndexed { index, it ->
|
parsed.items.forEachIndexed { index, it ->
|
||||||
if (it.mimeType.startsWith("video")) {
|
if (it.mimeType.startsWith("video")) {
|
||||||
@ -517,6 +517,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return AnimesPage(animeList, nextPageToken != null)
|
return AnimesPage(animeList, nextPageToken != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
|
||||||
|
val responseBody = use { transform(it.body.string()) }
|
||||||
|
return json.decodeFromString(responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -6,7 +6,7 @@ ext {
|
|||||||
extName = 'Kayoanime'
|
extName = 'Kayoanime'
|
||||||
pkgNameSuffix = 'en.kayoanime'
|
pkgNameSuffix = 'en.kayoanime'
|
||||||
extClass = '.Kayoanime'
|
extClass = '.Kayoanime'
|
||||||
extVersionCode = 6
|
extVersionCode = 7
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +319,7 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
var pageToken: String? = ""
|
var pageToken: String? = ""
|
||||||
while (pageToken != null) {
|
while (pageToken != null) {
|
||||||
val requestUrl = "/drive/v2beta/files?openDrive=true&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2CmodifiedByMeDate%2ClastViewedByMeDate%2CfileSize%2Cowners(kind%2CpermissionId%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2Cid)%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2Cshared%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2CfileExtension%2CsharingUser(kind%2CpermissionId%2Cid)%2Cspaces%2Cversion%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CtrashingUser(kind%2CpermissionId%2Cid)%2CtrashedDate%2Cparents(id)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus)%2Ccapabilities(canCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=50&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
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
|
val body = """--$BOUNDARY
|
||||||
|content-type: application/http
|
|content-type: application/http
|
||||||
|content-transfer-encoding: binary
|
|content-transfer-encoding: binary
|
||||||
@ -329,15 +329,13 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|authorization: ${generateSapisidhashHeader(sapisid)}
|
||||||
|x-goog-authuser: 0
|
|x-goog-authuser: 0
|
||||||
|
|
|
|
||||||
|--$BOUNDARY
|
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
||||||
|
|
|
||||||
""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = "https://clients6.google.com/batch/drive/v2beta".toHttpUrl().newBuilder()
|
val postUrl = buildString {
|
||||||
.addQueryParameter("${'$'}ct", "multipart/mixed;boundary=\"$BOUNDARY\"")
|
append("https://clients6.google.com/batch/drive/v2internal")
|
||||||
.addQueryParameter("key", key)
|
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
||||||
.build()
|
append("&key=$key")
|
||||||
.toString()
|
}
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
val postHeaders = headers.newBuilder()
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
.add("Content-Type", "text/plain; charset=UTF-8")
|
||||||
@ -348,13 +346,15 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
POST(postUrl, body = body, headers = postHeaders),
|
||||||
).execute()
|
).execute()
|
||||||
val parsed = json.decodeFromString<GDrivePostResponse>(
|
|
||||||
JSON_REGEX.find(response.body.string())!!.groupValues[1],
|
val parsed = response.parseAs<GDrivePostResponse> {
|
||||||
)
|
JSON_REGEX.find(it)!!.groupValues[1]
|
||||||
|
}
|
||||||
|
|
||||||
if (parsed.items == null) throw Exception("Failed to load items, please log in to google drive through webview")
|
if (parsed.items == null) throw Exception("Failed to load items, please log in to google drive through webview")
|
||||||
parsed.items.forEachIndexed { index, it ->
|
parsed.items.forEachIndexed { index, it ->
|
||||||
if (it.mimeType.startsWith("video")) {
|
if (it.mimeType.startsWith("video")) {
|
||||||
val size = formatBytes(it.fileSize?.toLongOrNull())
|
val size = it.fileSize?.toLongOrNull()?.let { formatBytes(it) }
|
||||||
val pathName = path.trimInfo()
|
val pathName = path.trimInfo()
|
||||||
|
|
||||||
episodeList.add(
|
episodeList.add(
|
||||||
@ -447,6 +447,11 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
|
||||||
|
val responseBody = use { transform(it.body.string()) }
|
||||||
|
return json.decodeFromString(responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -523,15 +528,15 @@ class Kayoanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val code: String,
|
val code: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun formatBytes(bytes: Long?): String? {
|
private fun formatBytes(bytes: Long): String {
|
||||||
val units = arrayOf("B", "KB", "MB", "GB", "TB", "PB", "EB")
|
return when {
|
||||||
var value = bytes?.toDouble() ?: return null
|
bytes >= 1_000_000_000 -> "%.2f GB".format(bytes / 1_000_000_000.0)
|
||||||
var i = 0
|
bytes >= 1_000_000 -> "%.2f MB".format(bytes / 1_000_000.0)
|
||||||
while (value >= 1024 && i < units.size - 1) {
|
bytes >= 1_000 -> "%.2f KB".format(bytes / 1_000.0)
|
||||||
value /= 1024
|
bytes > 1 -> "$bytes bytes"
|
||||||
i++
|
bytes == 1L -> "$bytes byte"
|
||||||
|
else -> ""
|
||||||
}
|
}
|
||||||
return String.format("%.1f %s", value, units[i])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCookie(url: String): String {
|
private fun getCookie(url: String): String {
|
||||||
|
@ -6,7 +6,7 @@ ext {
|
|||||||
extName = 'Ripcrabbyanime'
|
extName = 'Ripcrabbyanime'
|
||||||
pkgNameSuffix = 'en.ripcrabbyanime'
|
pkgNameSuffix = 'en.ripcrabbyanime'
|
||||||
extClass = '.Ripcrabbyanime'
|
extClass = '.Ripcrabbyanime'
|
||||||
extVersionCode = 5
|
extVersionCode = 6
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@ package eu.kanade.tachiyomi.animeextension.en.ripcrabbyanime
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import eu.kanade.tachiyomi.AppInfo
|
||||||
import eu.kanade.tachiyomi.animeextension.en.ripcrabbyanime.extractors.GoogleDriveExtractor
|
import eu.kanade.tachiyomi.animeextension.en.ripcrabbyanime.extractors.GoogleDriveExtractor
|
||||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||||
@ -38,7 +41,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override val id = 623659475482363776
|
override val id = 623659475482363776
|
||||||
|
|
||||||
override val baseUrl = "https://ripcrabbyanime.in"
|
override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! }
|
||||||
|
|
||||||
override val lang = "en"
|
override val lang = "en"
|
||||||
|
|
||||||
@ -106,11 +109,9 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return if (response.request.url.encodedPath.startsWith("/search/")) {
|
return if (response.request.url.encodedPath.startsWith("/search/")) {
|
||||||
popularAnimeParse(response)
|
popularAnimeParse(response)
|
||||||
} else {
|
} else {
|
||||||
val document = response.asJsoup()
|
val animeList = response.asJsoup()
|
||||||
|
.select(searchAnimeSelector())
|
||||||
val animeList = document.select(searchAnimeSelector()).map { element ->
|
.map(::popularAnimeFromElement)
|
||||||
popularAnimeFromElement(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
return AnimesPage(animeList, animeList.size == 40)
|
return AnimesPage(animeList, animeList.size == 40)
|
||||||
}
|
}
|
||||||
@ -211,6 +212,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val driveDocument = client.newCall(
|
val driveDocument = client.newCall(
|
||||||
GET(url, headers = driveHeaders),
|
GET(url, headers = driveHeaders),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
|
|
||||||
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").first { script ->
|
val keyScript = driveDocument.select("script").first { script ->
|
||||||
@ -228,7 +230,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
var pageToken: String? = ""
|
var pageToken: String? = ""
|
||||||
while (pageToken != null) {
|
while (pageToken != null) {
|
||||||
val requestUrl = "/drive/v2beta/files?openDrive=true&reason=102&syncType=0&errorRecovery=false&q=trashed%20%3D%20false%20and%20'$folderId'%20in%20parents&fields=kind%2CnextPageToken%2Citems(kind%2CmodifiedDate%2CmodifiedByMeDate%2ClastViewedByMeDate%2CfileSize%2Cowners(kind%2CpermissionId%2Cid)%2ClastModifyingUser(kind%2CpermissionId%2Cid)%2ChasThumbnail%2CthumbnailVersion%2Ctitle%2Cid%2CresourceKey%2Cshared%2CsharedWithMeDate%2CuserPermission(role)%2CexplicitlyTrashed%2CmimeType%2CquotaBytesUsed%2Ccopyable%2CfileExtension%2CsharingUser(kind%2CpermissionId%2Cid)%2Cspaces%2Cversion%2CteamDriveId%2ChasAugmentedPermissions%2CcreatedDate%2CtrashingUser(kind%2CpermissionId%2Cid)%2CtrashedDate%2Cparents(id)%2CshortcutDetails(targetId%2CtargetMimeType%2CtargetLookupStatus)%2Ccapabilities(canCopy%2CcanDownload%2CcanEdit%2CcanAddChildren%2CcanDelete%2CcanRemoveChildren%2CcanShare%2CcanTrash%2CcanRename%2CcanReadTeamDrive%2CcanMoveTeamDriveItem)%2Clabels(starred%2Ctrashed%2Crestricted%2Cviewed))%2CincompleteSearch&appDataFilter=NO_APP_DATA&spaces=drive&pageToken=$pageToken&maxResults=50&supportsTeamDrives=true&includeItemsFromAllDrives=true&corpora=default&orderBy=folder%2Ctitle_natural%20asc&retryCount=0&key=$key HTTP/1.1"
|
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
|
val body = """--$BOUNDARY
|
||||||
|content-type: application/http
|
|content-type: application/http
|
||||||
|content-transfer-encoding: binary
|
|content-transfer-encoding: binary
|
||||||
@ -238,15 +240,13 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|authorization: ${generateSapisidhashHeader(sapisid)}
|
|authorization: ${generateSapisidhashHeader(sapisid)}
|
||||||
|x-goog-authuser: 0
|
|x-goog-authuser: 0
|
||||||
|
|
|
|
||||||
|--$BOUNDARY
|
|--$BOUNDARY--""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
||||||
|
|
|
||||||
""".trimMargin("|").toRequestBody("multipart/mixed; boundary=\"$BOUNDARY\"".toMediaType())
|
|
||||||
|
|
||||||
val postUrl = "https://clients6.google.com/batch/drive/v2beta".toHttpUrl().newBuilder()
|
val postUrl = buildString {
|
||||||
.addQueryParameter("${'$'}ct", "multipart/mixed;boundary=\"$BOUNDARY\"")
|
append("https://clients6.google.com/batch/drive/v2internal")
|
||||||
.addQueryParameter("key", key)
|
append("?${'$'}ct=multipart/mixed; boundary=\"$BOUNDARY\"")
|
||||||
.build()
|
append("&key=$key")
|
||||||
.toString()
|
}
|
||||||
|
|
||||||
val postHeaders = headers.newBuilder()
|
val postHeaders = headers.newBuilder()
|
||||||
.add("Content-Type", "text/plain; charset=UTF-8")
|
.add("Content-Type", "text/plain; charset=UTF-8")
|
||||||
@ -257,14 +257,15 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
POST(postUrl, body = body, headers = postHeaders),
|
POST(postUrl, body = body, headers = postHeaders),
|
||||||
).execute()
|
).execute()
|
||||||
val parsed = json.decodeFromString<PostResponse>(
|
|
||||||
JSON_REGEX.find(response.body.string())!!.groupValues[1],
|
val parsed = response.parseAs<PostResponse> {
|
||||||
)
|
JSON_REGEX.find(it)!!.groupValues[1]
|
||||||
|
}
|
||||||
|
|
||||||
if (parsed.items == null) throw Exception("Failed to load items, please log in to google drive through webview")
|
if (parsed.items == null) throw Exception("Failed to load items, please log in to google drive through webview")
|
||||||
parsed.items.forEachIndexed { index, it ->
|
parsed.items.forEachIndexed { index, it ->
|
||||||
if (it.mimeType.startsWith("video")) {
|
if (it.mimeType.startsWith("video")) {
|
||||||
val size = formatBytes(it.fileSize?.toLongOrNull())
|
val size = it.fileSize?.toLongOrNull()?.let { formatBytes(it) }
|
||||||
val pathName = path.trimInfo()
|
|
||||||
|
|
||||||
episodeList.add(
|
episodeList.add(
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
@ -272,7 +273,7 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
this.url = "https://drive.google.com/uc?id=${it.id}"
|
this.url = "https://drive.google.com/uc?id=${it.id}"
|
||||||
episode_number = ITEM_NUMBER_REGEX.find(it.title.trimInfo())?.groupValues?.get(1)?.toFloatOrNull() ?: index.toFloat()
|
episode_number = ITEM_NUMBER_REGEX.find(it.title.trimInfo())?.groupValues?.get(1)?.toFloatOrNull() ?: index.toFloat()
|
||||||
date_upload = -1L
|
date_upload = -1L
|
||||||
scanlator = "$size • /$pathName"
|
scanlator = "$size • /$path"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -291,18 +292,26 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
document.select("div.tokha > div > a[href]").distinctBy { t ->
|
document.select("div.tokha > div > a[href]").distinctBy { t ->
|
||||||
t.text()
|
t.text()
|
||||||
}.forEach {
|
}.filter { t -> t.attr("href").isNotEmpty() }.forEach {
|
||||||
val url = it.attr("href").toHttpUrl()
|
val url = it.attr("abs:href").toHttpUrl()
|
||||||
val noRedirectClient = client.newBuilder().followRedirects(false).build()
|
val noRedirectClient = client.newBuilder().followRedirects(false).build()
|
||||||
|
|
||||||
if (url.host.contains("drive.google.com")) {
|
if (url.host.contains("drive.google.com")) {
|
||||||
traverseFolder(url.toString().substringBeforeLast("?usp=shar"), it.text())
|
val id = Regex("[\\w-]{28,}").find(
|
||||||
|
url.toString().substringBeforeLast("?usp=shar"),
|
||||||
|
)!!.groupValues[0]
|
||||||
|
|
||||||
|
traverseFolder("https://drive.google.com/drive/folders/$id", it.text())
|
||||||
}
|
}
|
||||||
if (url.host.contains("tinyurl")) {
|
if (url.host.contains("tinyurl")) {
|
||||||
val redirected = noRedirectClient.newCall(GET(url.toString())).execute()
|
val redirected = noRedirectClient.newCall(GET(url.toString())).execute()
|
||||||
redirected.headers["location"]?.let { location ->
|
redirected.headers["location"]?.let { location ->
|
||||||
if (location.toHttpUrl().host.contains("drive.google.com")) {
|
if (location.toHttpUrl().host.contains("drive.google.com")) {
|
||||||
traverseFolder(location.substringBeforeLast("?usp=shar"), it.text())
|
val id = Regex("[\\w-]{28,}").find(
|
||||||
|
location.substringBeforeLast("?usp=shar"),
|
||||||
|
)!!.groupValues[0]
|
||||||
|
|
||||||
|
traverseFolder("https://drive.google.com/drive/folders/$id", it.text())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,6 +339,11 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
|
||||||
|
val responseBody = use { transform(it.body.string()) }
|
||||||
|
return json.decodeFromString(responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -368,15 +382,15 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return newString.trim()
|
return newString.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatBytes(bytes: Long?): String? {
|
private fun formatBytes(bytes: Long): String {
|
||||||
val units = arrayOf("B", "KB", "MB", "GB", "TB", "PB", "EB")
|
return when {
|
||||||
var value = bytes?.toDouble() ?: return null
|
bytes >= 1_000_000_000 -> "%.2f GB".format(bytes / 1_000_000_000.0)
|
||||||
var i = 0
|
bytes >= 1_000_000 -> "%.2f MB".format(bytes / 1_000_000.0)
|
||||||
while (value >= 1024 && i < units.size - 1) {
|
bytes >= 1_000 -> "%.2f KB".format(bytes / 1_000.0)
|
||||||
value /= 1024
|
bytes > 1 -> "$bytes bytes"
|
||||||
i++
|
bytes == 1L -> "$bytes byte"
|
||||||
|
else -> ""
|
||||||
}
|
}
|
||||||
return String.format("%.1f %s", value, units[i])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCookie(url: String): String {
|
private fun getCookie(url: String): String {
|
||||||
@ -407,6 +421,11 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
private const val TRIM_EPISODE_NAME_KEY = "trim_episode"
|
private const val TRIM_EPISODE_NAME_KEY = "trim_episode"
|
||||||
private const val TRIM_EPISODE_NAME_DEFAULT = true
|
private const val TRIM_EPISODE_NAME_DEFAULT = true
|
||||||
|
|
||||||
|
private val PREF_DOMAIN_KEY = "preferred_domain_name_v${AppInfo.getVersionName()}"
|
||||||
|
private const val PREF_DOMAIN_TITLE = "Override BaseUrl"
|
||||||
|
private const val PREF_DOMAIN_DEFAULT = "https://ripcrabbyanimes.com"
|
||||||
|
private const val PREF_DOMAIN_SUMMARY = "For temporary uses. Updating the extension will erase this setting."
|
||||||
}
|
}
|
||||||
|
|
||||||
private val SharedPreferences.trimEpisodeName
|
private val SharedPreferences.trimEpisodeName
|
||||||
@ -415,6 +434,21 @@ class Ripcrabbyanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
// ============================== Settings ==============================
|
// ============================== Settings ==============================
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
EditTextPreference(screen.context).apply {
|
||||||
|
key = PREF_DOMAIN_KEY
|
||||||
|
title = PREF_DOMAIN_TITLE
|
||||||
|
summary = PREF_DOMAIN_SUMMARY
|
||||||
|
dialogTitle = PREF_DOMAIN_TITLE
|
||||||
|
dialogMessage = "Default: $PREF_DOMAIN_DEFAULT"
|
||||||
|
setDefaultValue(PREF_DOMAIN_DEFAULT)
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
val newValueString = newValue as String
|
||||||
|
Toast.makeText(screen.context, "Restart Aniyomi to apply new setting.", Toast.LENGTH_LONG).show()
|
||||||
|
preferences.edit().putString(key, newValueString.trim()).commit()
|
||||||
|
}
|
||||||
|
}.also(screen::addPreference)
|
||||||
|
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = TRIM_EPISODE_NAME_KEY
|
key = TRIM_EPISODE_NAME_KEY
|
||||||
title = "Trim info from episode name"
|
title = "Trim info from episode name"
|
||||||
|
Reference in New Issue
Block a user