From e00a8459702f566f741afade09718180d1fdf8c2 Mon Sep 17 00:00:00 2001 From: Secozzi <49240133+Secozzi@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:55:42 +0000 Subject: [PATCH] feat(all/googledrive): Improve fetching of details (#2324) --- src/all/googledrive/README.md | 6 +- src/all/googledrive/build.gradle | 2 +- .../all/googledrive/GoogleDrive.kt | 66 +++++++++++++------ .../all/googledrive/GoogleDriveDto.kt | 15 +++++ .../googledrive/GoogleDriveMultiFormReqs.kt | 9 ++- 5 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/all/googledrive/README.md b/src/all/googledrive/README.md index 5886622e6..3972adf67 100644 --- a/src/all/googledrive/README.md +++ b/src/all/googledrive/README.md @@ -9,7 +9,11 @@ Table of Content ## FAQ -### How do i add entries? +### How do I customize info? + +The Google Drive Extension allow for editing the same way as [local anime](https://aniyomi.org/docs/guides/local-anime-source/advanced) . + +### How do I add entries? The Google Drive Extension *only* supports google drive folders, so no shared drives (but folders inside shared drives works fine!). If you have a folder, which contains sub-folders of an anime, such as: ``` https://drive.google.com/drive/folders/some-long-id diff --git a/src/all/googledrive/build.gradle b/src/all/googledrive/build.gradle index 21ab6278e..a5da37392 100644 --- a/src/all/googledrive/build.gradle +++ b/src/all/googledrive/build.gradle @@ -8,7 +8,7 @@ ext { extName = 'Google Drive' pkgNameSuffix = 'all.googledrive' extClass = '.GoogleDrive' - extVersionCode = 9 + extVersionCode = 10 libVersion = '13' } diff --git a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDrive.kt b/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDrive.kt index cf61db0c2..149a8fb6d 100644 --- a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDrive.kt +++ b/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDrive.kt @@ -31,6 +31,7 @@ import okhttp3.ProtocolException import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response +import okhttp3.internal.commonEmptyRequestBody import org.jsoup.nodes.Document import rx.Observable import uy.kohesive.injekt.Injekt @@ -45,7 +46,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() { override val id = 4222017068256633289 - override var baseUrl = "" + override var baseUrl = "https://drive.google.com" // Hack to manipulate what gets opened in webview private val baseUrlInternal by lazy { @@ -199,28 +200,55 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() { null } ?: return Observable.just(anime) - if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) { - return Observable.just(anime) + // Get cover + + val coverResponse = client.newCall( + createPost(driveDocument, folderId, searchReqWithType(folderId, "cover", IMAGE_MIMETYPE)), + ).execute().parseAs { JSON_REGEX.find(it)!!.groupValues[1] } + + coverResponse.items?.firstOrNull()?.let { + anime.thumbnail_url = "https://drive.google.com/uc?id=${it.id}" } - var pageToken: String? = "" - while (pageToken != null) { - val response = client.newCall( - createPost(driveDocument, folderId), - ).execute() + // Get details - val parsed = response.parseAs { - JSON_REGEX.find(it)!!.groupValues[1] + val detailsResponse = client.newCall( + createPost(driveDocument, folderId, searchReqWithType(folderId, "details.json", "")), + ).execute().parseAs { JSON_REGEX.find(it)!!.groupValues[1] } + + detailsResponse.items?.firstOrNull()?.let { + val newPostHeaders = getHeaders.newBuilder().apply { + add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + set("Host", "drive.usercontent.google.com") + add("Origin", "https://drive.google.com") + add("Referer", "https://drive.google.com/") + add("X-Drive-First-Party", "DriveWebUi") + add("X-Json-Requested", "true") + }.build() + + val newPostUrl = "https://drive.usercontent.google.com/uc?id=${it.id}&authuser=0&export=download" + + val newResponse = client.newCall( + POST(newPostUrl, headers = newPostHeaders, body = commonEmptyRequestBody), + ).execute().parseAs { JSON_REGEX.find(it)!!.groupValues[1] } + + val downloadHeaders = headers.newBuilder().apply { + add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") + add("Connection", "keep-alive") + add("Cookie", getCookie("https://drive.usercontent.google.com")) + add("Host", "drive.usercontent.google.com") + }.build() + + client.newCall( + GET(newResponse.downloadUrl, headers = downloadHeaders), + ).execute().parseAs().let { t -> + t.title?.let { anime.title = it } + t.author?.let { anime.author = it } + t.artist?.let { anime.artist = it } + t.description?.let { anime.description = it } + t.genre?.let { anime.genre = it.joinToString(", ") } + t.status?.let { anime.status = it.toIntOrNull() ?: SAnime.UNKNOWN } } - - if (parsed.items == null) throw Exception("Failed to load items, please log in through webview") - parsed.items.forEach { - if (it.mimeType.startsWith("image/") && it.title.startsWith("cover.")) { - anime.thumbnail_url = "https://drive.google.com/uc?id=${it.id}" - } - } - - pageToken = parsed.nextPageToken } return Observable.just(anime) diff --git a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveDto.kt b/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveDto.kt index cb7a8098b..790ac919c 100644 --- a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveDto.kt +++ b/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveDto.kt @@ -28,3 +28,18 @@ data class LinkDataInfo( val title: String, val size: String, ) + +@Serializable +data class DownloadResponse( + val downloadUrl: String, +) + +@Serializable +data class DetailsJson( + val title: String? = null, + val author: String? = null, + val artist: String? = null, + val description: String? = null, + val genre: List? = null, + val status: String? = null, +) diff --git a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveMultiFormReqs.kt b/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveMultiFormReqs.kt index 2485e3867..b9315a9e0 100644 --- a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveMultiFormReqs.kt +++ b/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDriveMultiFormReqs.kt @@ -1,11 +1,18 @@ package eu.kanade.tachiyomi.animeextension.all.googledrive fun searchReq(parentId: String, query: String): (String, String, String) -> String { + return searchReqWithType(parentId, query, type = FOLDER_MIMETYPE) +} + +fun searchReqWithType(parentId: String, query: String, type: 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" + "/drive/v2internal/files?openDrive=false&reason=111&syncType=0&errorRecovery=false&q=title%20contains%20'$query'$type%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" } + +const val IMAGE_MIMETYPE = "%20and%20(mimeType%20in%20'image%2Fbmp'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fbmp%22'%2C%20'image%2Fjpeg'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fjpeg%22'%2C%20'image%2Fpng'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fpng%22'%2C%20'image%2Fgif'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fgif%22'%2C%20'image%2Ftiff'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Ftiff%22'%2C%20'image%2Fx-ms-bmp'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fx-ms-bmp%22'%2C%20'image%2Fsvg%2Bxml'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fsvg%2Bxml%22'%2C%20'image%2Fvnd.microsoft.icon'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fvnd.microsoft.icon%22'%2C%20'image%2Fheif'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fheif%22'%2C%20'image%2Fheic'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fheic%22'%2C%20'image%2Fwebp'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fwebp%22'%20or%20shortcutDetails.targetMimeType%20in%20'image%2Fbmp'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fbmp%22'%2C%20'image%2Fjpeg'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fjpeg%22'%2C%20'image%2Fpng'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fpng%22'%2C%20'image%2Fgif'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fgif%22'%2C%20'image%2Ftiff'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Ftiff%22'%2C%20'image%2Fx-ms-bmp'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fx-ms-bmp%22'%2C%20'image%2Fsvg%2Bxml'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fsvg%2Bxml%22'%2C%20'image%2Fvnd.microsoft.icon'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fvnd.microsoft.icon%22'%2C%20'image%2Fheif'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fheif%22'%2C%20'image%2Fheic'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fheic%22'%2C%20'image%2Fwebp'%2C%20'application%2Fvnd.google-gsuite.encrypted%3B%20content%3D%22image%2Fwebp%22')" +const val FOLDER_MIMETYPE = "%20and%20(mimeType%20in%20'application%2Fvnd.google-apps.folder'%20or%20shortcutDetails.targetMimeType%20in%20'application%2Fvnd.google-apps.folder')"