diff --git a/docs/configuration.rst b/docs/configuration.rst index 0f9009c9..f52f5ce1 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -4064,6 +4064,16 @@ Description the first in the list gets chosen (usually `mp3`). +extractor.koofr.zip +------------------- +Type + ``bool`` +Default + ``false`` +Description + Download shared `/links/` with multiple files as a single `.zip` file. + + extractor.lolisafe.domain ------------------------- Type diff --git a/docs/gallery-dl.conf b/docs/gallery-dl.conf index d80494d5..3a6d4b09 100644 --- a/docs/gallery-dl.conf +++ b/docs/gallery-dl.conf @@ -518,6 +518,10 @@ "covers": false, "format": "mp3" }, + "koofr": + { + "zip": false + }, "luscious": { "gif": false diff --git a/gallery_dl/extractor/koofr.py b/gallery_dl/extractor/koofr.py index 9ebc1331..08a737c5 100644 --- a/gallery_dl/extractor/koofr.py +++ b/gallery_dl/extractor/koofr.py @@ -17,6 +17,8 @@ class KoofrSharedExtractor(Extractor): category = "koofr" subcategory = "shared" root = "https://app.koofr.net" + directory_fmt = ("{category}", "{date:%Y-%m-%d} {title}") + archive_fmt = "{post[id]}_{hash|id}" pattern = (r"(?:https?://)?(?:" r"(?:app\.)?koofr\.(?:net|eu)/links/([\w-]+)|" r"k00\.fr/(\w+))") @@ -41,15 +43,45 @@ class KoofrSharedExtractor(Extractor): "Sec-Fetch-Site" : "same-origin", } data = self.request_json(url, params=params, headers=headers) - - name = data["name"] - file = text.nameext_from_name(name, data["file"]) - file["_http_headers"] = {"Referer": referer} - root = data.get("publicUrlBase") or self.root - url = f"{root}/content/links/{uuid}/files/get/{name}?path=/&force=" - if password: - url = f"{url}&password={password}" + base = f"{root}/content/links/{uuid}/files/get/" + headers = {"Referer": referer} + file = data["file"] - yield Message.Directory, "", file - yield Message.Url, url, file + if file["type"] == "dir" and not self.config("zip", False): + path = True + url = url + "/bundle" + params["path"] = "/" + files = self.request_json( + url, params=params, headers=headers)["files"] + else: + path = False + files = (file,) + + if password: + password = text.escape(password) + + post = { + "id" : data["id"], + "title": data["name"], + "count": len(files), + "date" : self.parse_timestamp(file["modified"] / 1000), + } + + yield Message.Directory, "", post + for num, file in enumerate(files, 1): + file["count"] = len(files) + file["num"] = num + file["post"] = post + file["date"] = self.parse_timestamp(file["modified"] / 1000) + file["_http_headers"] = headers + + name = file["name"] + text.nameext_from_name(name, file) + + name = text.escape(name) + url = (f"{base}{name}?path=%2F{name if path else '&force'}") + if password: + url = f"{url}&password={password}" + + yield Message.Url, url, file diff --git a/test/results/koofr.py b/test/results/koofr.py index 749219cd..ef7d5fdf 100644 --- a/test/results/koofr.py +++ b/test/results/koofr.py @@ -11,7 +11,7 @@ __tests__ = ( { "#url" : "https://k00.fr/cltf71jr", "#class" : koofr.KoofrSharedExtractor, - "#results" : "https://app.koofr.net/content/links/923b4f56-3aaf-49ee-95e3-d85c52b687b0/files/get/wsf-form-job-application-form.json?path=/&force=", + "#results" : "https://app.koofr.net/content/links/923b4f56-3aaf-49ee-95e3-d85c52b687b0/files/get/wsf-form-job-application-form.json?path=%2F&force", "#sha1_content": "f65ccc63a99165ecb9ff2ab92302c25b245a904f", "contentType": "application/json", @@ -28,7 +28,7 @@ __tests__ = ( { "#url" : "https://app.koofr.net/links/923b4f56-3aaf-49ee-95e3-d85c52b687b0", "#class" : koofr.KoofrSharedExtractor, - "#results" : "https://app.koofr.net/content/links/923b4f56-3aaf-49ee-95e3-d85c52b687b0/files/get/wsf-form-job-application-form.json?path=/&force=", + "#results" : "https://app.koofr.net/content/links/923b4f56-3aaf-49ee-95e3-d85c52b687b0/files/get/wsf-form-job-application-form.json?path=%2F&force", "#sha1_content": "f65ccc63a99165ecb9ff2ab92302c25b245a904f", "contentType": "application/json", @@ -47,4 +47,58 @@ __tests__ = ( "#class" : koofr.KoofrSharedExtractor, }, +{ + "#url" : "https://app.koofr.net/links/01deac62-f5d6-4d2b-7043-53b24cc0a038", + "#comment" : "individual files", + "#class" : koofr.KoofrSharedExtractor, + "#options" : {"zip": False}, + "#pattern" : r"https://app\.koofr\.net/content/links/01deac62\-f5d6\-4d2b\-7043\-53b24cc0a038/files/get/smw_msu1\-\d+\.pcm\?path=%2Fsmw_msu1\-\d+\.pcm", + "#count" : 18, + + "contentType": "application/octet-stream", + "count" : 18, + "num" : range(1, 18), + "date" : "type:datetime", + "extension" : "pcm", + "filename" : r"re:smw_msu1-\d+", + "hash" : "hash:md5", + "modified" : int, + "name" : r"re:smw_msu1-\d+\.pcm", + "size" : range(500_000, 20_000_000), + "tags" : {}, + "type" : "file", + "post" : { + "count" : 18, + "date" : "dt:2023-11-19 16:27:56", + "id" : "01deac62-f5d6-4d2b-7043-53b24cc0a038", + "title" : "Church of Kondo", + }, +}, + +{ + "#url" : "https://app.koofr.net/links/01deac62-f5d6-4d2b-7043-53b24cc0a038", + "#comment" : ".zip container", + "#class" : koofr.KoofrSharedExtractor, + "#options" : {"zip": True}, + "#results" : "https://app.koofr.net/content/links/01deac62-f5d6-4d2b-7043-53b24cc0a038/files/get/Church of Kondo?path=%2F&force", + + "contentType": "", + "count" : 1, + "date" : "dt:2023-11-19 16:27:56", + "extension" : "", + "filename" : "Church of Kondo", + "modified" : 1700411276087, + "name" : "Church of Kondo", + "num" : 1, + "size" : 0, + "tags" : {}, + "type" : "dir", + "post" : { + "count" : 1, + "date" : "dt:2023-11-19 16:27:56", + "id" : "01deac62-f5d6-4d2b-7043-53b24cc0a038", + "title" : "Church of Kondo", + }, +}, + )