diff --git a/docs/supportedsites.md b/docs/supportedsites.md index 62e115a2..c7fae6d2 100644 --- a/docs/supportedsites.md +++ b/docs/supportedsites.md @@ -103,6 +103,12 @@ Consider all sites to be NSFW unless otherwise known. Blogs, Posts, Search Results + + Bunkr + https://bunkr.is/ + Albums + + Comic Vine https://comicvine.gamespot.com/ @@ -1261,12 +1267,6 @@ Consider all sites to be NSFW unless otherwise known. lolisafe and chibisafe - - Bunkr - https://app.bunkr.is/ - Albums - - ZzZz https://zz.ht/ diff --git a/gallery_dl/extractor/__init__.py b/gallery_dl/extractor/__init__.py index e273f843..70cebb37 100644 --- a/gallery_dl/extractor/__init__.py +++ b/gallery_dl/extractor/__init__.py @@ -25,6 +25,7 @@ modules = [ "bcy", "behance", "blogger", + "bunkr", "comicvine", "cyberdrop", "danbooru", diff --git a/gallery_dl/extractor/bunkr.py b/gallery_dl/extractor/bunkr.py new file mode 100644 index 00000000..9904d0a1 --- /dev/null +++ b/gallery_dl/extractor/bunkr.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +# Copyright 2022 Mike Fährmann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +"""Extractors for https://bunkr.is/""" + +from .lolisafe import LolisafeAlbumExtractor +from .. import text +import json + + +class BunkrAlbumExtractor(LolisafeAlbumExtractor): + """Extractor for bunkr.is albums""" + category = "bunkr" + root = "https://app.bunkr.is" + pattern = r"(?:https?://)?(?:app\.)?bunkr\.(?:is|to)/a/([^/?#]+)" + test = ( + ("https://app.bunkr.is/a/Lktg9Keq", { + "pattern": r"https://cdn\.bunkr\.is/test-テスト-\"&>-QjgneIQv\.png", + "content": "0c8768055e4e20e7c7259608b67799171b691140", + "keyword": { + "album_id": "Lktg9Keq", + "album_name": 'test テスト "&>', + "count": 1, + "filename": 'test-テスト-"&>-QjgneIQv', + "id": "QjgneIQv", + "name": 'test-テスト-"&>', + "num": int, + }, + }), + # mp4 (#2239) + ("https://bunkr.is/a/ptRHaCn2", { + "pattern": r"https://media-files\.bunkr\.is/_-RnHoW69L\.mp4", + "content": "80e61d1dbc5896ae7ef9a28734c747b28b320471", + }), + ("https://bunkr.to/a/Lktg9Keq"), + ) + + def fetch_album(self, album_id): + if "//app." in self.root: + return self._fetch_album_api(album_id) + else: + return self._fetch_album_site(album_id) + + def _fetch_album_api(self, album_id): + files, data = LolisafeAlbumExtractor.fetch_album(self, album_id) + + for file in files: + url = file["file"] + if url.endswith(".mp4"): + file["file"] = url.replace( + "//cdn.bunkr.is/", "//media-files.bunkr.is/", 1) + else: + file["_fallback"] = (url.replace("//cdn.", "//cdn3.", 1),) + + return files, data + + def _fetch_album_site(self, album_id): + url = self.root + "/a/" + self.album_id + + try: + data = json.loads(text.extract( + self.request(url).text, + 'id="__NEXT_DATA__" type="application/json">', '<')[0]) + props = data["props"]["pageProps"] + album = props["album"] + files = props["files"] + except Exception as exc: + self.log.debug(exc) + self.root = self.root.replace("bunkr", "app.bunkr", 1) + return self._fetch_album_api(album_id) + + for file in files: + name = file["name"] + if name.endswith(".mp4"): + file["file"] = "https://media-files.bunkr.is/" + name + else: + file["file"] = file["cdn"] + "/" + name + + return files, { + "album_id" : self.album_id, + "album_name" : text.unescape(album["name"]), + "description": text.unescape(album["description"]), + "count" : len(files), + } diff --git a/gallery_dl/extractor/lolisafe.py b/gallery_dl/extractor/lolisafe.py index 2aea44c3..7c6ef69b 100644 --- a/gallery_dl/extractor/lolisafe.py +++ b/gallery_dl/extractor/lolisafe.py @@ -20,10 +20,6 @@ class LolisafeExtractor(BaseExtractor): BASE_PATTERN = LolisafeExtractor.update({ - "bunkr": { - "root": "https://app.bunkr.is", - "pattern": r"(?:app\.)?bunkr\.(?:is|to)", - }, "zzzz" : { "root": "https://zz.ht", "pattern": r"zz\.(?:ht|fo)", @@ -35,25 +31,6 @@ class LolisafeAlbumExtractor(LolisafeExtractor): subcategory = "album" pattern = BASE_PATTERN + "/a/([^/?#]+)" test = ( - ("https://app.bunkr.is/a/Lktg9Keq", { - "pattern": r"https://cdn\.bunkr\.is/test-テスト-\"&>-QjgneIQv\.png", - "content": "0c8768055e4e20e7c7259608b67799171b691140", - "keyword": { - "album_id": "Lktg9Keq", - "album_name": 'test テスト "&>', - "count": 1, - "filename": 'test-テスト-"&>-QjgneIQv', - "id": "QjgneIQv", - "name": 'test-テスト-"&>', - "num": int, - }, - }), - # mp4 (#2239) - ("https://bunkr.is/a/ptRHaCn2", { - "pattern": r"https://media-files\.bunkr\.is/_-RnHoW69L\.mp4", - "content": "80e61d1dbc5896ae7ef9a28734c747b28b320471", - }), - ("https://bunkr.to/a/Lktg9Keq"), ("https://zz.ht/a/lop7W6EZ", { "pattern": r"https://z\.zz\.fo/(4anuY|ih560)\.png", "count": 2, @@ -71,11 +48,7 @@ class LolisafeAlbumExtractor(LolisafeExtractor): domain = self.config("domain") if domain is None or domain == "auto": - if self.category == "bunkr": - self.root = "https://app.bunkr.is" - else: - self.root = text.root_from_url(match.group(0)) - + self.root = text.root_from_url(match.group(0)) else: self.root = text.ensure_http_scheme(domain) @@ -89,10 +62,6 @@ class LolisafeAlbumExtractor(LolisafeExtractor): data["_fallback"] = file["_fallback"] text.nameext_from_url(url, data) data["name"], sep, data["id"] = data["filename"].rpartition("-") - - if data["extension"] == "mp4": - url = url.replace( - "//cdn.bunkr.is/", "//media-files.bunkr.is/", 1) yield Message.Url, url, data def fetch_album(self, album_id):