Kagane: add support for Tachimanga (#11710)

* Kagane: add support for Tachimanga

* cache cert
This commit is contained in:
lamaxama 2025-11-23 01:28:44 +08:00 committed by Draff
parent eec57bbf65
commit 64b3c18350
Signed by: Draff
GPG Key ID: E8A89F3211677653
2 changed files with 48 additions and 11 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Kagane' extName = 'Kagane'
extClass = '.Kagane' extClass = '.Kagane'
extVersionCode = 11 extVersionCode = 12
isNsfw = true isNsfw = true
} }

View File

@ -255,12 +255,16 @@ class Kagane : HttpSource(), ConfigurableSource {
add("Referer", "$baseUrl/") add("Referer", "$baseUrl/")
}.build() }.build()
private fun getCertificate(): String { private fun getCertificate(url: String): String {
return client.newCall(GET("$apiUrl/api/v1/static/bin.bin", apiHeaders)).execute() return client.newCall(GET(url, apiHeaders)).execute()
.body.bytes() .body.bytes()
.toBase64() .toBase64()
} }
private val windvineCertificate by lazy { getCertificate("$apiUrl/api/v1/static/bin.bin") }
private val fairPlayCertificate by lazy { getCertificate("$apiUrl/api/v1/static/crt.crt") }
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
if (chapter.url.count { it == ';' } != 2) throw Exception("Chapter url error, please refresh chapter list.") if (chapter.url.count { it == ';' } != 2) throw Exception("Chapter url error, please refresh chapter list.")
var (seriesId, chapterId, pageCount) = chapter.url.split(";") var (seriesId, chapterId, pageCount) = chapter.url.split(";")
@ -304,6 +308,10 @@ class Kagane : HttpSource(), ConfigurableSource {
</head> </head>
<body> <body>
<script> <script>
function detectDRMSupport() {
return "WebKitMediaKeys" in window ? "fairplay" : "MediaKeys" in window && "function" == typeof navigator.requestMediaKeySystemAccess ? "widevine" : null
}
function base64ToArrayBuffer(base64) { function base64ToArrayBuffer(base64) {
var binaryString = atob(base64); var binaryString = atob(base64);
var bytes = new Uint8Array(binaryString.length); var bytes = new Uint8Array(binaryString.length);
@ -314,21 +322,39 @@ class Kagane : HttpSource(), ConfigurableSource {
} }
async function getData() { async function getData() {
const g = base64ToArrayBuffer("${getCertificate()}"); let widevine = detectDRMSupport() !== 'fairplay';
let t = await navigator.requestMediaKeySystemAccess("com.widevine.alpha", [{ const g = base64ToArrayBuffer(widevine ? "$windvineCertificate" : "$fairPlayCertificate");
initDataTypes: ["cenc"], let t = widevine ? await navigator.requestMediaKeySystemAccess("com.widevine.alpha", [{
audioCapabilities: [], initDataTypes: ["cenc"],
videoCapabilities: [{ audioCapabilities: [],
contentType: 'video/mp4; codecs="avc1.42E01E"' videoCapabilities: [{
}] contentType: 'video/mp4; codecs="avc1.42E01E"'
}]
}]) : await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
initDataTypes: ["skd"],
audioCapabilities: [{
contentType: 'audio/mp4; codecs="mp4a.40.2"'
}],
videoCapabilities: [{
contentType: 'video/mp4; codecs="avc1.42E01E"'
}]
}]); }]);
let e = await t.createMediaKeys(); let e = await t.createMediaKeys();
await e.setServerCertificate(g); await e.setServerCertificate(g);
let video = widevine ? null : document.createElement("video");
if (video) {
video.style.display = "none";
document.body.appendChild(video);
await video.setMediaKeys(e);
}
let n = e.createSession(); let n = e.createSession();
let i = new Promise((resolve, reject) => { let i = new Promise((resolve, reject) => {
function onMessage(event) { function onMessage(event) {
n.removeEventListener("message", onMessage); n.removeEventListener("message", onMessage);
if (video) {
document.body.removeChild(video)
}
resolve(event.message); resolve(event.message);
} }
@ -341,7 +367,18 @@ class Kagane : HttpSource(), ConfigurableSource {
n.addEventListener("error", onError); n.addEventListener("error", onError);
}); });
await n.generateRequest("cenc", base64ToArrayBuffer("${getPssh(f).toBase64()}")); if (widevine) {
await n.generateRequest("cenc", base64ToArrayBuffer("${getPssh(f).toBase64()}"));
} else {
let oo = base64ToArrayBuffer("${f.toBase64()}")
let c = Array.from(new Uint8Array(oo)).map(t => t.toString(16).padStart(2, "0")).join("");
let d = JSON.stringify({
uri: "skd://" + c,
assetId: "$chapterId",
});
const textEncoder = new TextEncoder();
await n.generateRequest("skd", textEncoder.encode(d));
}
let o = await i; let o = await i;
let m = new Uint8Array(o); let m = new Uint8Array(o);
let v = btoa(String.fromCharCode(...m)); let v = btoa(String.fromCharCode(...m));