diff --git a/dart/manga/multisrc/madara/madara.dart b/dart/manga/multisrc/madara/madara.dart index 49c50f57..4931de9f 100644 --- a/dart/manga/multisrc/madara/madara.dart +++ b/dart/manga/multisrc/madara/madara.dart @@ -8,6 +8,24 @@ class Madara extends MProvider { final Client client = Client(); + MPages mangaFromElements(List elements) { + List mangaList = []; + + for (var i = 0; i < elements.length; i++) { + final postTitle = elements[i].selectFirst("div.post-title a"); + final imageElement = elements[i].selectFirst("img"); + final image = extractImageUrl(imageElement); + + MManga manga = MManga(); + manga.name = postTitle.text; + manga.imageUrl = substringBefore(image, " "); + manga.link = postTitle.getHref; + mangaList.add(manga); + } + + return MPages(mangaList, true); + } + @override Future getPopular(int page) async { final res = (await client.get( @@ -290,57 +308,103 @@ class Madara extends MProvider { final res = (await client.get(Uri.parse(url))); final document = parseHtml(res.body); - final pageElements = document.select( - "div.page-break, li.blocks-gallery-item, .reading-content .text-left:not(:has(.blocks-gallery-item)) img", + var images = getImagesFromPage(document); + if (images.length == 1) { + images = buildPageUrls(images, document); + } + + return images.isNotEmpty ? images : parseProtectorImage(document); + } + + List getImagesFromPage(MDocument doc) { + final elements = doc.select( + "div.page-break img, li.blocks-gallery-item img, .reading-content .text-left:not(:has(.blocks-gallery-item)) img", ); + return elements.map((e) => extractImageUrl(e)?.trim()).toList(); + } - List imgs = []; - for (var element in pageElements) { - try { - final imgElement = element.selectFirst("img"); - final img = extractImageUrl(imageElement); - imgs.add(img); - } catch (_) {} + List parseProtectorImage(Document doc) { + final protectorData = doc.selectFirst(chapterProtectorSelector)?.innerHtml; + if (protectorData == null) return []; + + final password = getPasswordFromProtector(protectorData); + final chapterDataStr = getChapterDataStr(protectorData); + + if (chapterDataStr == null || password == null) return []; + + final decryptedText = decryptChapterData(chapterDataStr, password); + if (decryptedText == null) return []; + + return List.from(jsonDecode(jsonDecode(decryptedText))); + } + + // Extract password + String? getPasswordFromProtector(String protectorData) { + final regex = RegExp(r"wpmangaprotectornonce='(.*?)';"); + final match = regex.firstMatch(protectorData); + return match != null ? match.group(1) : null; + } + + // Extract encrypted chapter string + String? getChapterDataStr(String protectorData) { + final regex = RegExp(r"chapter_data='(.*?)';"); + final match = regex.firstMatch(protectorData); + return match != null ? match.group(1)!.replaceAll(r"\/", "/") : null; + } + + // Decrypt AES encrypted string + String? decryptChapterData(String chapterDataStr, String password) { + try { + final Map chapterData = jsonDecode(chapterDataStr); + final salt = _hexToBytes(chapterData["s"]); + final ct = base64Decode(chapterData["ct"]); + + // Prefix with 'Salted__' and salt + final completeCipher = Uint8List.fromList([ + ...utf8.encode('Salted__'), + ...salt, + ...ct, + ]); + + // Encode completeCipher again to base64 + final base64Cipher = base64Encode(completeCipher); + + // Decrypt + final decrypted = decryptAESCryptoJS(base64Cipher, password); + return decrypted; + } catch (e) { + print("Error decrypting chapter data: $e"); + return null; } + } + List buildPageUrls(List imgs, MDocument document) { List pageUrls = []; + final pagesNumber = document + .selectFirst("#single-pager") + .select("option") + .length; - if (imgs.length == 1) { - final pagesNumber = document - .selectFirst("#single-pager") - .select("option") - .length; - final imgUrl = imgs.first; - for (var i = 0; i < pagesNumber; i++) { - final val = i + 1; - if (i.toString().length == 1) { - pageUrls.add(imgUrl.replaceAll("01", '0$val')); - } else { - pageUrls.add(imgUrl.replaceAll("01", val.toString())); - } + final imgUrl = imgs.first; + for (var i = 0; i < pagesNumber; i++) { + final val = i + 1; + if (i.toString().length == 1) { + pageUrls.add(imgUrl.replaceAll("01", '0$val')); + } else { + pageUrls.add(imgUrl.replaceAll("01", val.toString())); } - } else { - return imgs; } + return pageUrls; } - MPages mangaFromElements(List elements) { - List mangaList = []; - - for (var i = 0; i < elements.length; i++) { - final postTitle = elements[i].selectFirst("div.post-title a"); - final imageElement = elements[i].selectFirst("img"); - final image = extractImageUrl(imageElement); - - MManga manga = MManga(); - manga.name = postTitle.text; - manga.imageUrl = substringBefore(image, " "); - manga.link = postTitle.getHref; - mangaList.add(manga); + // Convert hex string to bytes + List _hexToBytes(String hex) { + final List result = []; + for (var i = 0; i < hex.length; i += 2) { + result.add(int.parse(hex.substring(i, i + 2), radix: 16)); } - - return MPages(mangaList, true); + return result; } @override