AnimeSama: Switch to New Endpoint and Direct AnimeSama Link Integration (#10802)
* Open URL in apps + new way to get all chapters * AnimeSama: Update extVersionCode * AnimeSama: Changes requested by vetleledaal * AnimeSama: Fix special chapters that could not be opened
This commit is contained in:
parent
3b0eb9a789
commit
0c8a27b820
23
src/fr/animesama/AndroidManifest.xml
Normal file
23
src/fr/animesama/AndroidManifest.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".fr.animesama.AnimeSamaUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="anime-sama.fr"
|
||||
android:pathPattern="/catalogue/..*"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'AnimeSama'
|
||||
extClass = '.AnimeSama'
|
||||
extVersionCode = 7
|
||||
extVersionCode = 8
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
@ -2,9 +2,11 @@ package eu.kanade.tachiyomi.extension.fr.animesama
|
||||
|
||||
import android.util.Log
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
@ -12,6 +14,8 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
@ -20,6 +24,7 @@ import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
|
||||
class AnimeSama : ParsedHttpSource() {
|
||||
|
||||
@ -96,7 +101,7 @@ class AnimeSama : ParsedHttpSource() {
|
||||
return SManga.create().apply {
|
||||
title = element.select("h1").text()
|
||||
setUrlWithoutDomain(element.select("a").attr("href"))
|
||||
thumbnail_url = element.select("img").attr("src")
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +118,7 @@ class AnimeSama : ParsedHttpSource() {
|
||||
return SManga.create().apply {
|
||||
title = element.select("h1").text()
|
||||
setUrlWithoutDomain(element.select("a").attr("href").removeSuffix("scan/vf/"))
|
||||
thumbnail_url = element.select("img").attr("src")
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,6 +140,51 @@ class AnimeSama : ParsedHttpSource() {
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
private fun detailsParse(response: Response): SManga? {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val scriptContent = document.select("script:containsData(panneauScan(\"nom\", \"url\"))").toString()
|
||||
val splitedContent = scriptContent.split(";").toMutableList()
|
||||
// Remove exemple
|
||||
splitedContent.removeAt(0)
|
||||
|
||||
val pattern = """panneauScan\("(.+?)", "(.+?)"\)""".toRegex()
|
||||
val numberOfScans = splitedContent.count { line ->
|
||||
val matchResult = pattern.find(line)
|
||||
matchResult != null && !matchResult.groupValues[2].contains("va")
|
||||
}
|
||||
|
||||
if (numberOfScans == 0) return null
|
||||
|
||||
val manga: SManga = SManga.create()
|
||||
manga.description = document.select("#sousBlocMiddle > div h2:contains(Synopsis)+p").text()
|
||||
manga.genre = document.select("#sousBlocMiddle > div h2:contains(Genres)+a").text()
|
||||
manga.title = document.select("#titreOeuvre").text()
|
||||
manga.thumbnail_url = document.selectFirst("#coverOeuvre")?.absUrl("src")
|
||||
manga.setUrlWithoutDomain(document.baseUri())
|
||||
return manga
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return if (query.startsWith("ID:")) {
|
||||
val id = query.substringAfterLast("ID:")
|
||||
|
||||
val mangaUrl = "$baseUrl/catalogue".toHttpUrl()
|
||||
.newBuilder()
|
||||
.addPathSegment(id)
|
||||
.build()
|
||||
val requestToCheckManga = GET(mangaUrl, headers)
|
||||
client.newCall(requestToCheckManga).asObservableSuccess().map {
|
||||
MangasPage(
|
||||
listOfNotNull(detailsParse(it)),
|
||||
false,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
override fun searchMangaNextPageSelector(): String = popularMangaNextPageSelector()
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
@ -144,7 +194,7 @@ class AnimeSama : ParsedHttpSource() {
|
||||
description = document.select("#sousBlocMiddle > div h2:contains(Synopsis)+p").text()
|
||||
genre = document.select("#sousBlocMiddle > div h2:contains(Genres)+a").text()
|
||||
title = document.select("#titreOeuvre").text()
|
||||
thumbnail_url = document.select("#coverOeuvre").attr("src")
|
||||
thumbnail_url = document.selectFirst("#coverOeuvre")?.absUrl("src")
|
||||
}
|
||||
|
||||
// Chapters
|
||||
@ -162,22 +212,16 @@ class AnimeSama : ParsedHttpSource() {
|
||||
private fun parseChapterFromResponse(response: Response, translationName: String): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val chapterUrl = document.baseUri().toHttpUrl()
|
||||
val title = document.select("#titreOeuvre").text()
|
||||
val chapterUrl = "$baseUrl/s2/scans/get_nb_chap_et_img.php".toHttpUrl()
|
||||
.newBuilder()
|
||||
.query(null)
|
||||
.addPathSegment("episodes.js")
|
||||
.addQueryParameter("title", document.select("#titreOeuvre").text())
|
||||
.addQueryParameter("oeuvre", title)
|
||||
.build()
|
||||
|
||||
val requestToFetchChapters = GET(chapterUrl, headers)
|
||||
val javascriptFile = client.newCall(requestToFetchChapters).execute()
|
||||
val javascriptFileContent = javascriptFile.body.string()
|
||||
|
||||
val parsedJavascriptFileToJson = javascriptFileContent
|
||||
.let { Regex("""eps(\d+)""").findAll(it) }
|
||||
.map { it.groupValues[1].toInt() }
|
||||
.distinct() // Remove duplicate episodes
|
||||
.sortedDescending().toList()
|
||||
val apiNbChapImgResponse = client.newCall(requestToFetchChapters).execute()
|
||||
val apiNbChapImgJson = Json.decodeFromString<Map<String, Int>>(apiNbChapImgResponse.body.string())
|
||||
|
||||
val parsedChapterList: MutableList<SChapter> = ArrayList()
|
||||
var chapterDelay = 0
|
||||
|
||||
@ -197,18 +241,18 @@ class AnimeSama : ParsedHttpSource() {
|
||||
parsedChapterList.add(
|
||||
SChapter.create().apply {
|
||||
name = "Chapitre $i"
|
||||
setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).build().toString())
|
||||
setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).addQueryParameter("title", title).build().toString())
|
||||
scanlator = translationName
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
specialRegex.find(command) != null -> {
|
||||
val title = specialRegex.find(command)!!.groupValues[1]
|
||||
val chapterTitle = specialRegex.find(command)!!.groupValues[1]
|
||||
parsedChapterList.add(
|
||||
SChapter.create().apply {
|
||||
name = "Chapitre $title"
|
||||
setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).build().toString())
|
||||
name = "Chapitre $chapterTitle"
|
||||
setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).addQueryParameter("title", title).build().toString())
|
||||
scanlator = translationName
|
||||
},
|
||||
)
|
||||
@ -218,11 +262,11 @@ class AnimeSama : ParsedHttpSource() {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index in parsedChapterList.size until parsedJavascriptFileToJson.size) {
|
||||
(parsedChapterList.size until apiNbChapImgJson.size).forEach { index ->
|
||||
parsedChapterList.add(
|
||||
SChapter.create().apply {
|
||||
name = "Chapitre " + (parsedChapterList.size + 1 - chapterDelay)
|
||||
setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).build().toString())
|
||||
setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).addQueryParameter("title", title).build().toString())
|
||||
scanlator = translationName
|
||||
},
|
||||
)
|
||||
@ -239,14 +283,14 @@ class AnimeSama : ParsedHttpSource() {
|
||||
splitedContent.removeAt(0)
|
||||
|
||||
val parsedChapterList: MutableList<SChapter> = mutableListOf()
|
||||
|
||||
val pattern = """panneauScan\("(.+?)", "(.+?)"\)""".toRegex()
|
||||
val scanPattern = Regex("""(Scans|\(|\))""")
|
||||
splitedContent.forEach { line ->
|
||||
val pattern = """panneauScan\("(.+?)", "(.+?)"\)""".toRegex()
|
||||
val matchResult = pattern.find(line)
|
||||
if (matchResult != null) {
|
||||
val (scanTitle, scanUrl) = matchResult.destructured
|
||||
if (!scanUrl.contains("va")) {
|
||||
val scanlatorGroup = scanTitle.replace(Regex("""(Scans|\(|\))"""), "").trim()
|
||||
val scanlatorGroup = scanTitle.replace(scanPattern, "").trim()
|
||||
val fetchExistentSubMangas = GET(
|
||||
url.newBuilder()
|
||||
.addPathSegments(
|
||||
@ -260,7 +304,7 @@ class AnimeSama : ParsedHttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
parsedChapterList.sortBy { chapter -> ("$baseUrl${chapter.url}").toHttpUrl().queryParameter("id")?.toIntOrNull() }
|
||||
parsedChapterList.sortBy { chapter -> "$baseUrl${chapter.url}".toHttpUrl().queryParameter("id")?.toIntOrNull() }
|
||||
|
||||
return parsedChapterList.asReversed()
|
||||
}
|
||||
@ -272,32 +316,19 @@ class AnimeSama : ParsedHttpSource() {
|
||||
val title = url.queryParameter("title")
|
||||
val chapter = url.queryParameter("id")
|
||||
|
||||
val documentString = document.body().toString()
|
||||
val chapterUrl = "$baseUrl/s2/scans/get_nb_chap_et_img.php".toHttpUrl()
|
||||
.newBuilder()
|
||||
.addQueryParameter("oeuvre", title)
|
||||
.build()
|
||||
val requestToFetchChapters = GET(chapterUrl, headers)
|
||||
|
||||
val allChapters: Map<Int, Int> = Regex("""eps(\d+)\s*(?:=\s*\[(.*?)]|\.length\s*=\s*(\d+))""")
|
||||
.findAll(documentString)
|
||||
.associate { match ->
|
||||
val episode = match.groupValues[1].toInt()
|
||||
val arrayContent = match.groupValues[2]
|
||||
val explicitLength = match.groupValues[3]
|
||||
val apiNbChapImgResponse = client.newCall(requestToFetchChapters).execute()
|
||||
val apiNbChapImgJson = Json.decodeFromString<Map<String, Int>>(apiNbChapImgResponse.body.string())
|
||||
val imageCount = apiNbChapImgJson[chapter] ?: 0
|
||||
|
||||
val length = when {
|
||||
explicitLength.isNotEmpty() -> explicitLength.toInt()
|
||||
arrayContent.isNotEmpty() -> arrayContent.split(Regex(",\\s*")).count { it.isNotBlank() }
|
||||
else -> 0
|
||||
}
|
||||
|
||||
episode to length
|
||||
}
|
||||
|
||||
val chapterSize = allChapters[chapter?.toInt()] ?: 1
|
||||
|
||||
val imageList = mutableListOf<Page>()
|
||||
for (index in 1 until chapterSize + 1) {
|
||||
imageList.add(
|
||||
Page(index, imageUrl = "$cdn$title/$chapter/$index.jpg"),
|
||||
)
|
||||
}
|
||||
val imageList = (1..imageCount).map { index ->
|
||||
Page(index, imageUrl = "$cdn$title/$chapter/$index.jpg")
|
||||
}.toMutableList()
|
||||
|
||||
return imageList
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package eu.kanade.tachiyomi.extension.fr.animesama
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* Springboard that accepts https://anime-sama.fr/catalogue/xxxxxx/ intents and redirects them to
|
||||
* the main Tachiyomi process.
|
||||
*/
|
||||
class AnimeSamaUrlActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 1) {
|
||||
val id = pathSegments[1]
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
putExtra("query", "ID:$id")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("AnimeSamaUrlActivity", e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e("AnimeSamaUrlActivity", "could not parse uri from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user