diff --git a/src/all/googledrive/README.md b/src/all/googledrive/README.md deleted file mode 100644 index cf434d9f..00000000 --- a/src/all/googledrive/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# DISCLAIMER - -This extension requires you to log in through Google and relies heavily on scraping the website of Google Drive, which may be against their terms of service. Use at your own risk. - -# Google Drive - -Table of Content -- [FAQ](#FAQ) - - [How do i add entries?](#how-do-i-add-entries) - - [What are all these options for drive paths?](#what-are-all-these-options-for-drive-paths) - - [I added the drive paths but it still get "Enter drive path(s) in extension settings."](#i-added-the-drive-paths-but-it-still-get-enter-drive-paths-in-extension-settings) - - [I cannot log in through webview](#i-cannot-log-in-through-webview) - -## FAQ - -### 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 -├── anime1/ -│ ├── episode 1.mkv -│ ├── episode 2.mkv -│ └── ... -└── anime2/ - ├── episode 1.mkv - ├── episode 2.mkv - └── ... -``` -Then it you should go to extension settings, and add the url there. You can add multiple drive paths by separating them with a semicolon `;`. To select between the paths, open up the extension and click the filter, from there you can select a specific drive. - -If you instead have a folder that contains the episodes directly, such as: -``` -https://drive.google.com/drive/folders/some-long-id -├── episode 1.mkv -├── episode 2.mkv -└── ... -``` -Then you should open the extension, click filters, then paste the folder link in the `Add single folder` filter. - -### What are all these options for drive paths? -The extension allows for some options when adding the drive path: -1. You can customize the name of a drive path by prepending the url with []. This will change the display name when selecting different drive paths in filters. Example: `[Weekly episodes]https://drive.google.com/drive/folders/some-long-id` -2. You can limit the recursion depth by adding a `#` to the end of the url together with a number. If you set it to `1`, the extension will not go into any sub-folders when loading episodes. If you set it to `2`, the extension will traverse into any sub-folders, but not sub-folders of sub-folders, and so on and so forth. It's useful if one folder has a separate folder for each seasons that you want to traverse through, but if another folder has separate folder for openings/endings that you *don't* want to traverse through. Example: `https://drive.google.com/drive/folders/some-long-id#3` -3. It is also possible to specify a range of episodes to load. It needs to be added together with the recursion depth as seen in step 2. Note: it only works if the recursion depth is set to `1`. The range is inclusive, so doing #1,2,7 will load the 2nd up to, and including, the 7th item. Example: `https://drive.google.com/drive/folders/some-long-id#1,2,7` - -It is possible to mix these options, and they work for both ways to add folders. - -### I added the drive paths but it still get "Enter drive path(s) in extension settings." -This can be caused by the caching that Aniyomi does. Reinstalling the extension will fix this issue (reinstalling an extension does not remove any extension settings) - -### I cannot log in through webview -Google can sometimes think that webview isn't a secure browser, and will thus refuse to let you log in. There are a few things you can try to mitigate this: -1. In the top right, click the three dots then click `Clear cookies` -2. In the top right, click the three dots then click `Refresh` -3. Click the `Try again` button after the website doesn't let you log in -4. Make sure that your webview is up to date - -Try a combination of these steps, and after a few tries it should eventually let you log in. diff --git a/src/all/googledrive/build.gradle b/src/all/googledrive/build.gradle deleted file mode 100644 index 20a3b6f7..00000000 --- a/src/all/googledrive/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -ext { - extName = 'Google Drive' - extClass = '.GoogleDrive' - extVersionCode = 15 -} - -apply from: "$rootDir/common.gradle" - -dependencies { - implementation(project(':lib:googledrive-extractor')) -} \ No newline at end of file diff --git a/src/all/googledrive/res/mipmap-hdpi/ic_launcher.png b/src/all/googledrive/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index cde62642..00000000 Binary files a/src/all/googledrive/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/googledrive/res/mipmap-mdpi/ic_launcher.png b/src/all/googledrive/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index e335d742..00000000 Binary files a/src/all/googledrive/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/googledrive/res/mipmap-xhdpi/ic_launcher.png b/src/all/googledrive/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 3780d179..00000000 Binary files a/src/all/googledrive/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/googledrive/res/mipmap-xxhdpi/ic_launcher.png b/src/all/googledrive/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 75c56dbc..00000000 Binary files a/src/all/googledrive/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/googledrive/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/googledrive/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index a68eef1b..00000000 Binary files a/src/all/googledrive/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/googledrive/res/web_hi_res_512.png b/src/all/googledrive/res/web_hi_res_512.png deleted file mode 100644 index 4ca28cb0..00000000 Binary files a/src/all/googledrive/res/web_hi_res_512.png and /dev/null differ 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 deleted file mode 100644 index 22ad7799..00000000 --- a/src/all/googledrive/src/eu/kanade/tachiyomi/animeextension/all/googledrive/GoogleDrive.kt +++ /dev/null @@ -1,720 +0,0 @@ -package eu.kanade.tachiyomi.animeextension.all.googledrive - -import android.app.Application -import android.content.SharedPreferences -import android.text.Editable -import android.text.TextWatcher -import android.widget.Button -import android.widget.EditText -import android.widget.Toast -import androidx.preference.EditTextPreference -import androidx.preference.PreferenceScreen -import androidx.preference.SwitchPreferenceCompat -import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource -import eu.kanade.tachiyomi.animesource.model.AnimeFilter -import eu.kanade.tachiyomi.animesource.model.AnimeFilterList -import eu.kanade.tachiyomi.animesource.model.AnimesPage -import eu.kanade.tachiyomi.animesource.model.SAnime -import eu.kanade.tachiyomi.animesource.model.SEpisode -import eu.kanade.tachiyomi.animesource.model.Video -import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource -import eu.kanade.tachiyomi.lib.googledriveextractor.GoogleDriveExtractor -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.util.asJsoup -import eu.kanade.tachiyomi.util.parseAs -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.ProtocolException -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import okhttp3.internal.commonEmptyRequestBody -import org.jsoup.nodes.Document -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import java.net.URLEncoder -import java.security.MessageDigest - -class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() { - - override val name = "Google Drive" - - override val id = 4222017068256633289 - - override var baseUrl = "https://drive.google.com" - - // Hack to manipulate what gets opened in webview - private val baseUrlInternal by lazy { - preferences.domainList.split(";").firstOrNull() - } - - override val lang = "all" - - override val supportsLatest = false - - private val json: Json by injectLazy() - - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - - // 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 =============================== - - override suspend fun getPopularAnime(page: Int): AnimesPage = - parsePage(popularAnimeRequest(page), page) - - override fun popularAnimeRequest(page: Int): Request { - require(!baseUrlInternal.isNullOrEmpty()) { "Enter drive path(s) in extension settings." } - - val match = DRIVE_FOLDER_REGEX.matchEntire(baseUrlInternal!!)!! - val folderId = match.groups["id"]!!.value - val recurDepth = match.groups["depth"]?.value ?: "" - baseUrl = "https://drive.google.com/drive/folders/$folderId" - - return GET( - "https://drive.google.com/drive/folders/$folderId$recurDepth", - headers = getHeaders, - ) - } - - override fun popularAnimeParse(response: Response): AnimesPage = throw UnsupportedOperationException() - - // =============================== Latest =============================== - - override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException() - - override fun latestUpdatesParse(response: Response): AnimesPage = throw UnsupportedOperationException() - - // =============================== Search =============================== - - override fun searchAnimeParse(response: Response): AnimesPage = throw UnsupportedOperationException() - - override suspend fun getSearchAnime( - page: Int, - query: String, - filters: AnimeFilterList, - ): AnimesPage { - val filterList = if (filters.isEmpty()) getFilterList() else filters - val urlFilter = filterList.find { it is URLFilter } as URLFilter - - return if (urlFilter.state.isEmpty()) { - val req = searchAnimeRequest(page, query, filters) - - if (query.isEmpty()) { - parsePage(req, page) - } else { - val parentId = req.url.pathSegments.last() - val cleanQuery = URLEncoder.encode(query, "UTF-8") - val genMultiFormReq = searchReq(parentId, cleanQuery) - - parsePage(req, page, genMultiFormReq) - } - } else { - addSinglePage(urlFilter.state) - } - } - - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - require(!baseUrlInternal.isNullOrEmpty()) { "Enter drive path(s) in extension settings." } - - val filterList = if (filters.isEmpty()) getFilterList() else filters - val serverFilter = filterList.find { it is ServerFilter } as ServerFilter - val serverUrl = serverFilter.toUriPart() - - val match = DRIVE_FOLDER_REGEX.matchEntire(serverUrl)!! - val folderId = match.groups["id"]!!.value - val recurDepth = match.groups["depth"]?.value ?: "" - baseUrl = "https://drive.google.com/drive/folders/$folderId" - - return GET( - "https://drive.google.com/drive/folders/$folderId$recurDepth", - headers = getHeaders, - ) - } - - // ============================== FILTERS =============================== - - override fun getFilterList(): AnimeFilterList = AnimeFilterList( - ServerFilter(getDomains()), - AnimeFilter.Separator(), - AnimeFilter.Header("Add single folder"), - URLFilter(), - ) - - private class ServerFilter(domains: Array>) : UriPartFilter( - "Select drive path", - domains, - ) - - private fun getDomains(): Array> { - if (preferences.domainList.isBlank()) return emptyArray() - return preferences.domainList.split(";").map { - val name = DRIVE_FOLDER_REGEX.matchEntire(it)!!.groups["name"]?.let { - it.value.substringAfter("[").substringBeforeLast("]") - } - Pair(name ?: it.toHttpUrl().encodedPath, it) - }.toTypedArray() - } - - private open class UriPartFilter(displayName: String, val vals: Array>) : - AnimeFilter.Select(displayName, vals.map { it.first }.toTypedArray()) { - fun toUriPart() = vals[state].second - } - - private class URLFilter : AnimeFilter.Text("Url") - - // =========================== Anime Details ============================ - - override fun animeDetailsRequest(anime: SAnime): Request { - val parsed = json.decodeFromString(anime.url) - return GET(parsed.url, headers = getHeaders) - } - - override suspend fun getAnimeDetails(anime: SAnime): SAnime { - val parsed = json.decodeFromString(anime.url) - - if (parsed.type == "single") return anime - - val folderId = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!!.groups["id"]!!.value - - val driveDocument = try { - client.newCall(GET(parsed.url, headers = getHeaders)).execute().asJsoup() - } catch (a: ProtocolException) { - null - } ?: return anime - - // Get cover - - val coverResponse = client.newCall( - createPost(driveDocument, folderId, nextPageToken, 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}" - } - - // Get details - - val detailsResponse = client.newCall( - createPost(driveDocument, folderId, nextPageToken, 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 } - } - } - - return anime - } - - override fun animeDetailsParse(response: Response): SAnime = throw UnsupportedOperationException() - - // ============================== Episodes ============================== - - override suspend fun getEpisodeList(anime: SAnime): List { - val episodeList = mutableListOf() - val parsed = json.decodeFromString(anime.url) - - if (parsed.type == "single") { - return 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 maxRecursionDepth = match.groups["depth"]?.let { - it.value.substringAfter("#").substringBefore(",").toInt() - } ?: 2 - val (start, stop) = match.groups["range"]?.let { - it.value.substringAfter(",").split(",").map { it.toInt() } - } ?: listOf(null, null) - - fun traverseFolder(folderUrl: String, path: String, recursionDepth: Int = 0) { - if (recursionDepth == maxRecursionDepth) return - - val folderId = DRIVE_FOLDER_REGEX.matchEntire(folderUrl)!!.groups["id"]!!.value - - val driveDocument = try { - client.newCall(GET(folderUrl, headers = getHeaders)).execute().asJsoup() - } catch (a: ProtocolException) { - throw Exception("Unable to get items, check webview") - } - - if (driveDocument.selectFirst("title:contains(Error 404 \\(Not found\\))") != null) return - - var pageToken: String? = "" - var counter = 1 - - while (pageToken != null) { - val response = client.newCall( - createPost(driveDocument, folderId, pageToken), - ).execute() - - val parsed = response.parseAs { - JSON_REGEX.find(it)!!.groupValues[1] - } - - if (parsed.items == null) throw Exception("Failed to load items, please log in through webview") - parsed.items.forEachIndexed { index, it -> - if (it.mimeType.startsWith("video")) { - val size = it.fileSize?.toLongOrNull()?.let { formatBytes(it) } ?: "" - val pathName = if (preferences.trimEpisodeInfo) path.trimInfo() else path - - if (start != null && maxRecursionDepth == 1 && counter < start) { - counter++ - return@forEachIndexed - } - if (stop != null && maxRecursionDepth == 1 && counter > stop) return - - episodeList.add( - SEpisode.create().apply { - name = - if (preferences.trimEpisodeName) it.title.trimInfo() else it.title - url = "https://drive.google.com/uc?id=${it.id}" - episode_number = - ITEM_NUMBER_REGEX.find(it.title.trimInfo())?.groupValues?.get(1) - ?.toFloatOrNull() ?: (index + 1).toFloat() - date_upload = -1L - scanlator = if (preferences.scanlatorOrder) { - "/$pathName • $size" - } else { - "$size • /$pathName" - } - }, - ) - counter++ - } - if (it.mimeType.endsWith(".folder")) { - traverseFolder( - "https://drive.google.com/drive/folders/${it.id}", - if (path.isEmpty()) it.title else "$path/${it.title}", - recursionDepth + 1, - ) - } - } - - pageToken = parsed.nextPageToken - } - } - - traverseFolder(parsed.url, "") - - return episodeList.reversed() - } - - override fun episodeListParse(response: Response): List = throw UnsupportedOperationException() - - // ============================ Video Links ============================= - - override suspend fun getVideoList(episode: SEpisode): List