Add extension (#1447)
This commit is contained in:
2
src/all/googledriveindex/AndroidManifest.xml
Normal file
2
src/all/googledriveindex/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="eu.kanade.tachiyomi.animeextension" />
|
13
src/all/googledriveindex/build.gradle
Normal file
13
src/all/googledriveindex/build.gradle
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'GoogleDriveIndex'
|
||||||
|
pkgNameSuffix = 'all.googledriveindex'
|
||||||
|
extClass = '.GoogleDriveIndex'
|
||||||
|
extVersionCode = 1
|
||||||
|
libVersion = '13'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
BIN
src/all/googledriveindex/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/all/googledriveindex/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
src/all/googledriveindex/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/all/googledriveindex/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
src/all/googledriveindex/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/all/googledriveindex/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
BIN
src/all/googledriveindex/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/all/googledriveindex/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
BIN
src/all/googledriveindex/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/all/googledriveindex/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
src/all/googledriveindex/res/web_hi_res_512.png
Normal file
BIN
src/all/googledriveindex/res/web_hi_res_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
@ -0,0 +1,38 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.googledriveindex
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ResponseData(
|
||||||
|
val nextPageToken: String? = null,
|
||||||
|
val data: DataObject,
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class DataObject(
|
||||||
|
val files: List<FileObject>,
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class FileObject(
|
||||||
|
val mimeType: String,
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val modifiedTime: String? = null,
|
||||||
|
val size: String? = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LinkData(
|
||||||
|
val type: String,
|
||||||
|
val url: String,
|
||||||
|
val info: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class IdUrl(
|
||||||
|
val id: String,
|
||||||
|
val url: String,
|
||||||
|
val referer: String,
|
||||||
|
val type: String,
|
||||||
|
)
|
@ -0,0 +1,497 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.googledriveindex
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Base64
|
||||||
|
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.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.Response
|
||||||
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||||
|
|
||||||
|
override val name = "GoogleDriveIndex"
|
||||||
|
|
||||||
|
override val baseUrl by lazy {
|
||||||
|
preferences.getString("domain_list", "")!!.split(",").first()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val lang = "all"
|
||||||
|
|
||||||
|
private var pageToken: String? = ""
|
||||||
|
|
||||||
|
override val supportsLatest = false
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val client: OkHttpClient = network.cloudflareClient
|
||||||
|
|
||||||
|
// ============================== Popular ===============================
|
||||||
|
|
||||||
|
override fun popularAnimeRequest(page: Int): Request {
|
||||||
|
if (baseUrl.isEmpty()) {
|
||||||
|
throw Exception("Enter drive path(s) in extension settings.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page == 1) pageToken = ""
|
||||||
|
val popHeaders = headers.newBuilder()
|
||||||
|
.add("Accept", "*/*")
|
||||||
|
.add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
|
.add("Host", baseUrl.toHttpUrl().host)
|
||||||
|
.add("Origin", "https://${baseUrl.toHttpUrl().host}")
|
||||||
|
.add("Referer", baseUrl)
|
||||||
|
.add("X-Requested-With", "XMLHttpRequest")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val popBody = "password=&page_token=$pageToken&page_index=${page - 1}".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
||||||
|
|
||||||
|
return POST(baseUrl, body = popBody, headers = popHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
|
return parsePage(response, baseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================== Latest ===============================
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): AnimesPage = throw Exception("Not used")
|
||||||
|
|
||||||
|
// =============================== Search ===============================
|
||||||
|
|
||||||
|
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||||
|
if (baseUrl.isEmpty()) {
|
||||||
|
throw Exception("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()
|
||||||
|
|
||||||
|
if (page == 1) pageToken = ""
|
||||||
|
val searchHeaders = headers.newBuilder()
|
||||||
|
.add("Accept", "*/*")
|
||||||
|
.add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
|
.add("Host", serverUrl.toHttpUrl().host)
|
||||||
|
.add("Origin", "https://${serverUrl.toHttpUrl().host}")
|
||||||
|
.add("X-Requested-With", "XMLHttpRequest")
|
||||||
|
|
||||||
|
return if (query.isBlank()) {
|
||||||
|
val popBody = "password=&page_token=$pageToken&page_index=${page - 1}".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
||||||
|
POST(
|
||||||
|
serverUrl,
|
||||||
|
body = popBody,
|
||||||
|
headers = searchHeaders.add("Referer", serverUrl).build(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val cleanQuery = query.replace(" ", "+")
|
||||||
|
val searchUrl = "https://${serverUrl.toHttpUrl().host}/${serverUrl.toHttpUrl().pathSegments[0]}search"
|
||||||
|
|
||||||
|
val popBody = "q=$cleanQuery&page_token=$pageToken&page_index=${page - 1}".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
||||||
|
|
||||||
|
POST(
|
||||||
|
searchUrl,
|
||||||
|
body = popBody,
|
||||||
|
headers = searchHeaders.add("Referer", "$searchUrl?q=$cleanQuery").build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||||
|
return parsePage(response, response.request.url.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================== FILTERS ===============================
|
||||||
|
|
||||||
|
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||||
|
AnimeFilter.Header("Text search will only search inside selected server"),
|
||||||
|
ServerFilter(getDomains()),
|
||||||
|
)
|
||||||
|
|
||||||
|
private class ServerFilter(domains: Array<Pair<String, String>>) : UriPartFilter(
|
||||||
|
"Select server",
|
||||||
|
domains,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getDomains(): Array<Pair<String, String>> {
|
||||||
|
return preferences.getString("domain_list", "")!!.split(",").map {
|
||||||
|
Pair(it.substringAfter("https://"), it)
|
||||||
|
}.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||||
|
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||||
|
fun toUriPart() = vals[state].second
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== Anime Details ============================
|
||||||
|
|
||||||
|
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
||||||
|
return Observable.just(anime)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun animeDetailsParse(response: Response): SAnime = throw Exception("Not used")
|
||||||
|
|
||||||
|
// ============================== Episodes ==============================
|
||||||
|
|
||||||
|
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
||||||
|
val episodeList = mutableListOf<SEpisode>()
|
||||||
|
val parsed = json.decodeFromString<LinkData>(anime.url)
|
||||||
|
var counter = 1
|
||||||
|
|
||||||
|
val newParsed = if (parsed.type != "search") {
|
||||||
|
parsed
|
||||||
|
} else {
|
||||||
|
val idParsed = json.decodeFromString<IdUrl>(parsed.url)
|
||||||
|
val id2pathHeaders = headers.newBuilder()
|
||||||
|
.add("Accept", "*/*")
|
||||||
|
.add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
|
.add("Host", idParsed.url.toHttpUrl().host)
|
||||||
|
.add("Origin", "https://${idParsed.url.toHttpUrl().host}")
|
||||||
|
.add("Referer", idParsed.referer)
|
||||||
|
.add("X-Requested-With", "XMLHttpRequest")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val postBody = "id=${idParsed.id}".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
||||||
|
val slug = client.newCall(
|
||||||
|
POST(idParsed.url + "id2path", body = postBody, headers = id2pathHeaders),
|
||||||
|
).execute().body.string()
|
||||||
|
|
||||||
|
LinkData(
|
||||||
|
idParsed.type,
|
||||||
|
idParsed.url + slug,
|
||||||
|
parsed.info,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newParsed.type == "single") {
|
||||||
|
val episode = SEpisode.create()
|
||||||
|
val size = if (newParsed.info == null) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
" - ${newParsed.info}"
|
||||||
|
}
|
||||||
|
episode.name = "${newParsed.url.toHttpUrl().pathSegments.last()}$size"
|
||||||
|
episode.url = newParsed.url
|
||||||
|
episode.episode_number = 1F
|
||||||
|
episodeList.add(episode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newParsed.type == "multi") {
|
||||||
|
val basePathCounter = newParsed.url.toHttpUrl().pathSize
|
||||||
|
|
||||||
|
fun traverseDirectory(url: String) {
|
||||||
|
var newToken: String? = ""
|
||||||
|
var newPageIndex = 0
|
||||||
|
|
||||||
|
while (newToken != null) {
|
||||||
|
val popHeaders = headers.newBuilder()
|
||||||
|
.add("Accept", "*/*")
|
||||||
|
.add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
|
.add("Host", url.toHttpUrl().host)
|
||||||
|
.add("Origin", "https://${url.toHttpUrl().host}")
|
||||||
|
.add("Referer", url)
|
||||||
|
.add("X-Requested-With", "XMLHttpRequest")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val popBody = "password=&page_token=$newToken&page_index=$newPageIndex".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
||||||
|
|
||||||
|
val parsedBody = client.newCall(
|
||||||
|
POST(url, body = popBody, headers = popHeaders),
|
||||||
|
).execute().body.string().decrypt()
|
||||||
|
val parsed = json.decodeFromString<ResponseData>(parsedBody)
|
||||||
|
|
||||||
|
parsed.data.files.forEach { item ->
|
||||||
|
if (item.mimeType.endsWith("folder")) {
|
||||||
|
if (
|
||||||
|
preferences.getString("blacklist_folders", "")!!.split("/")
|
||||||
|
.any { it.equals(item.name, ignoreCase = true) }
|
||||||
|
) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
val newUrl = joinUrl(url, item.name).addSuffix("/")
|
||||||
|
traverseDirectory(newUrl)
|
||||||
|
}
|
||||||
|
if (item.mimeType.startsWith("video/")) {
|
||||||
|
val episode = SEpisode.create()
|
||||||
|
val epUrl = joinUrl(url, item.name)
|
||||||
|
val paths = epUrl.toHttpUrl().pathSegments
|
||||||
|
|
||||||
|
// Get season stuff
|
||||||
|
val season = if (paths.size == basePathCounter) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
paths[basePathCounter - 1]
|
||||||
|
}
|
||||||
|
val seasonInfoRegex = """(\([\s\w-]+\))(?: ?\[[\s\w-]+\])?${'$'}""".toRegex()
|
||||||
|
val seasonInfo = if (seasonInfoRegex.containsMatchIn(season)) {
|
||||||
|
"${seasonInfoRegex.find(season)!!.groups[1]!!.value} • "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
val seasonText = if (season.isBlank()) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
"[${season.trimInfo()}] "
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get other info
|
||||||
|
val extraInfo = if (paths.size > basePathCounter) {
|
||||||
|
"/" + paths.subList(basePathCounter - 1, paths.size - 1).joinToString("/") { it.trimInfo() }
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
val size = item.size?.toLongOrNull()?.let { formatFileSize(it) }
|
||||||
|
|
||||||
|
episode.name = "$seasonText${item.name.trimInfo()}${if (size == null) "" else " - $size"}"
|
||||||
|
episode.url = epUrl
|
||||||
|
episode.scanlator = seasonInfo + extraInfo
|
||||||
|
episode.episode_number = counter.toFloat()
|
||||||
|
counter++
|
||||||
|
|
||||||
|
episodeList.add(episode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newToken = parsed.nextPageToken
|
||||||
|
newPageIndex += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseDirectory(newParsed.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Observable.just(episodeList.reversed())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used")
|
||||||
|
|
||||||
|
// ============================ Video Links =============================
|
||||||
|
|
||||||
|
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
||||||
|
val url = episode.url
|
||||||
|
|
||||||
|
val doc = client.newCall(
|
||||||
|
GET("$url?a=view"),
|
||||||
|
).execute().asJsoup()
|
||||||
|
|
||||||
|
val script = doc.selectFirst("script:containsData(videodomain)")?.data()
|
||||||
|
?: return Observable.just(listOf(Video(url, "Video", url)))
|
||||||
|
val domainUrl = script.substringAfter("\"videodomain\":\"").substringBefore("\"")
|
||||||
|
val videoUrl = if (domainUrl.isBlank()) {
|
||||||
|
url
|
||||||
|
} else {
|
||||||
|
domainUrl + url.toHttpUrl().encodedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return Observable.just(listOf(Video(videoUrl, "Video", videoUrl)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
|
private fun joinUrl(path1: String, path2: String): String {
|
||||||
|
return path1.removeSuffix("/") + "/" + path2.removePrefix("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.decrypt(): String {
|
||||||
|
return Base64.decode(this.reversed().substring(24, this.length - 20), Base64.DEFAULT).toString(Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.addSuffix(suffix: String): String {
|
||||||
|
return if (this.endsWith(suffix)) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
this.plus(suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.trimInfo(): String {
|
||||||
|
var newString = this.replaceFirst("""^\[\w+\] ?""".toRegex(), "")
|
||||||
|
val regex = """( ?\[[\s\w-]+\]| ?\([\s\w-]+\))(\.mkv|\.mp4|\.avi)?${'$'}""".toRegex()
|
||||||
|
|
||||||
|
while (regex.containsMatchIn(newString)) {
|
||||||
|
newString = regex.replace(newString) { matchResult ->
|
||||||
|
matchResult.groups[2]?.value ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newString.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatFileSize(bytes: Long): String {
|
||||||
|
return when {
|
||||||
|
bytes >= 1073741824 -> "%.2f GB".format(bytes / 1073741824.0)
|
||||||
|
bytes >= 1048576 -> "%.2f MB".format(bytes / 1048576.0)
|
||||||
|
bytes >= 1024 -> "%.2f KB".format(bytes / 1024.0)
|
||||||
|
bytes > 1 -> "$bytes bytes"
|
||||||
|
bytes == 1L -> "$bytes byte"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LinkData.toJsonString(): String {
|
||||||
|
return json.encodeToString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IdUrl.toJsonString(): String {
|
||||||
|
return json.encodeToString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parsePage(response: Response, url: String): AnimesPage {
|
||||||
|
val parsed = json.decodeFromString<ResponseData>(response.body.string().decrypt())
|
||||||
|
val animeList = mutableListOf<SAnime>()
|
||||||
|
val isSearch = url.endsWith(":search")
|
||||||
|
|
||||||
|
parsed.data.files.forEach { item ->
|
||||||
|
if (item.mimeType.endsWith("folder")) {
|
||||||
|
val anime = SAnime.create()
|
||||||
|
anime.title = item.name.trimInfo()
|
||||||
|
|
||||||
|
if (isSearch) {
|
||||||
|
anime.setUrlWithoutDomain(
|
||||||
|
LinkData(
|
||||||
|
"search",
|
||||||
|
IdUrl(
|
||||||
|
item.id,
|
||||||
|
url.substringBeforeLast("search"),
|
||||||
|
response.request.header("Referer")!!,
|
||||||
|
"multi",
|
||||||
|
).toJsonString(),
|
||||||
|
).toJsonString(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
anime.setUrlWithoutDomain(
|
||||||
|
LinkData(
|
||||||
|
"multi",
|
||||||
|
joinUrl(url, item.name).addSuffix("/"),
|
||||||
|
).toJsonString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
animeList.add(anime)
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
item.mimeType.startsWith("video/") &&
|
||||||
|
!(preferences.getBoolean("ignore_non_folder", true) && isSearch)
|
||||||
|
) {
|
||||||
|
val anime = SAnime.create()
|
||||||
|
anime.title = item.name.trimInfo()
|
||||||
|
|
||||||
|
if (isSearch) {
|
||||||
|
anime.setUrlWithoutDomain(
|
||||||
|
LinkData(
|
||||||
|
"search",
|
||||||
|
IdUrl(
|
||||||
|
item.id,
|
||||||
|
url.substringBeforeLast("search"),
|
||||||
|
response.request.header("Referer")!!,
|
||||||
|
"single",
|
||||||
|
).toJsonString(),
|
||||||
|
item.size?.toLongOrNull()?.let { formatFileSize(it) },
|
||||||
|
).toJsonString(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
anime.setUrlWithoutDomain(
|
||||||
|
LinkData(
|
||||||
|
"single",
|
||||||
|
joinUrl(url, item.name),
|
||||||
|
item.size?.toLongOrNull()?.let { formatFileSize(it) },
|
||||||
|
).toJsonString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
animeList.add(anime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pageToken = parsed.nextPageToken
|
||||||
|
|
||||||
|
return AnimesPage(animeList, parsed.nextPageToken != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
val domainListPref = EditTextPreference(screen.context).apply {
|
||||||
|
key = "domain_list"
|
||||||
|
title = "Enter drive paths to be shown in extension"
|
||||||
|
summary = """Enter drive paths to be shown in extension
|
||||||
|
|Enter as comma separated list
|
||||||
|
""".trimMargin()
|
||||||
|
this.setDefaultValue("")
|
||||||
|
dialogTitle = "Path list"
|
||||||
|
dialogMessage = "Separate paths with a comma"
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
try {
|
||||||
|
val res = preferences.edit().putString("domain_list", newValue as String).commit()
|
||||||
|
Toast.makeText(screen.context, "Restart Aniyomi to apply changes", Toast.LENGTH_LONG).show()
|
||||||
|
res
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val blacklistFolders = EditTextPreference(screen.context).apply {
|
||||||
|
key = "blacklist_folders"
|
||||||
|
title = "Blacklist folder names"
|
||||||
|
summary = """Enter names of folders to skip over
|
||||||
|
|Enter as slash / separated list
|
||||||
|
""".trimMargin()
|
||||||
|
this.setDefaultValue("NC/Extras")
|
||||||
|
dialogTitle = "Blacklisted folders"
|
||||||
|
dialogMessage = "Separate folders with a slash (case insensitive)"
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
try {
|
||||||
|
val res = preferences.edit().putString("blacklist_folders", newValue as String).commit()
|
||||||
|
Toast.makeText(screen.context, "Restart Aniyomi to apply changes", Toast.LENGTH_LONG).show()
|
||||||
|
res
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ignoreNonFolder = SwitchPreferenceCompat(screen.context).apply {
|
||||||
|
key = "ignore_non_folder"
|
||||||
|
title = "Only include folders on search"
|
||||||
|
setDefaultValue(true)
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
preferences.edit().putBoolean(key, newValue as Boolean).commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.addPreference(domainListPref)
|
||||||
|
screen.addPreference(blacklistFolders)
|
||||||
|
screen.addPreference(ignoreNonFolder)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user