[whyp] add support (#8725)
This commit is contained in:
@@ -1231,6 +1231,12 @@ Consider all listed sites to potentially be NSFW.
|
||||
<td>Albums, Articles, Feeds, Images from Statuses, User Profiles, Videos</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr id="whyp" title="whyp">
|
||||
<td>Whyp</td>
|
||||
<td>https://whyp.it/</td>
|
||||
<td>Audio, Collections, User Profiles</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr id="wikiart" title="wikiart">
|
||||
<td>WikiArt.org</td>
|
||||
<td>https://www.wikiart.org/</td>
|
||||
|
||||
@@ -234,6 +234,7 @@ modules = [
|
||||
"weebcentral",
|
||||
"weebdex",
|
||||
"weibo",
|
||||
"whyp",
|
||||
"wikiart",
|
||||
"wikifeet",
|
||||
"wikimedia",
|
||||
|
||||
98
gallery_dl/extractor/whyp.py
Normal file
98
gallery_dl/extractor/whyp.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2025 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://whyp.it/"""
|
||||
|
||||
from .common import Extractor, Message
|
||||
from .. import text
|
||||
|
||||
BASE_PATTERN = r"(?:https?://)?(?:www\.)?whyp\.it"
|
||||
|
||||
|
||||
class WhypExtractor(Extractor):
|
||||
"""Base class for whyp extractors"""
|
||||
category = "whyp"
|
||||
root = "https://whyp.it"
|
||||
root_api = "https://api.whyp.it"
|
||||
directory_fmt = ("{category}", "{user[username]} ({user[id]})")
|
||||
filename_fmt = "{id} {title}.{extension}"
|
||||
archive_fmt = "{id}"
|
||||
|
||||
def _init(self):
|
||||
self.headers_api = {
|
||||
"Accept" : "application/json",
|
||||
"Origin" : self.root,
|
||||
"Referer": self.root + "/",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-site",
|
||||
}
|
||||
|
||||
def items(self):
|
||||
for track in self.tracks():
|
||||
if url := track.get("lossless_url"):
|
||||
track["original"] = True
|
||||
else:
|
||||
url = track["lossy_url"]
|
||||
track["original"] = False
|
||||
|
||||
if "created_at" in track:
|
||||
track["date"] = self.parse_datetime_iso(track["created_at"])
|
||||
|
||||
yield Message.Directory, "", track
|
||||
yield Message.Url, url, text.nameext_from_url(url, track)
|
||||
|
||||
|
||||
class WhypAudioExtractor(WhypExtractor):
|
||||
subcategory = "audio"
|
||||
pattern = BASE_PATTERN + r"/tracks/(\d+)"
|
||||
example = "https://whyp.it/tracks/12345/SLUG"
|
||||
|
||||
def tracks(self):
|
||||
url = f"{self.root_api}/api/tracks/{self.groups[0]}"
|
||||
track = self.request_json(url, headers=self.headers_api)["track"]
|
||||
return (track,)
|
||||
|
||||
|
||||
class WhypUserExtractor(WhypExtractor):
|
||||
subcategory = "user"
|
||||
pattern = BASE_PATTERN + r"/users/(\d+)"
|
||||
example = "https://whyp.it/users/123/NAME"
|
||||
|
||||
def tracks(self):
|
||||
url = f"{self.root_api}/api/users/{self.groups[0]}/tracks"
|
||||
params = {}
|
||||
headers = self.headers_api
|
||||
|
||||
while True:
|
||||
data = self.request_json(url, params=params, headers=headers)
|
||||
|
||||
yield from data["tracks"]
|
||||
|
||||
if not (cursor := data.get("next_cursor")):
|
||||
break
|
||||
params["cursor"] = cursor
|
||||
|
||||
|
||||
class WhypCollectionExtractor(WhypExtractor):
|
||||
subcategory = "collection"
|
||||
pattern = BASE_PATTERN + r"/collections/(\d+)"
|
||||
example = "https://whyp.it/collections/123/NAME"
|
||||
|
||||
def tracks(self):
|
||||
cid = self.groups[0]
|
||||
|
||||
url = f"{self.root_api}/api/collections/{cid}"
|
||||
headers = self.headers_api
|
||||
self.kwdict["collection"] = collection = self.request_json(
|
||||
url, headers=headers)["collection"]
|
||||
|
||||
url = f"{self.root_api}/api/collections/{cid}/tracks"
|
||||
params = {"token": collection["token"]}
|
||||
data = self.request_json(url, params=params, headers=headers)
|
||||
return data["tracks"]
|
||||
143
test/results/whyp.py
Normal file
143
test/results/whyp.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
from gallery_dl.extractor import whyp
|
||||
|
||||
|
||||
__tests__ = (
|
||||
{
|
||||
"#url" : "https://whyp.it/tracks/13721/fallout-3-intro-remake",
|
||||
"#class" : whyp.WhypAudioExtractor,
|
||||
"#pattern" : r"https://cdn.whyp.it/5e9de576-f33a-40ea-bd43-1693a568a6a0.mp3\?token=.+",
|
||||
|
||||
"allow_downloads": False,
|
||||
"artwork_url" : None,
|
||||
"artwork_url_fallback": "https://cdn.whyp.it/a46f3485-8d19-4753-98e0-76011c7e33b0.jpg",
|
||||
"comments_count" : int,
|
||||
"created_at" : "2025-11-24T16:59:50+00:00",
|
||||
"date" : "dt:2025-11-24 16:59:50",
|
||||
"description" : "",
|
||||
"duration" : 46.34,
|
||||
"extension" : "mp3",
|
||||
"filename" : "5e9de576-f33a-40ea-bd43-1693a568a6a0",
|
||||
"id" : 13721,
|
||||
"lossless_size" : None,
|
||||
"lossless_url" : None,
|
||||
"lossy_size" : 1853719,
|
||||
"lossy_url" : r"re:https://cdn.whyp.it/5e9de576-f33a-40ea-bd43-1693a568a6a0.mp3",
|
||||
"original" : False,
|
||||
"public" : True,
|
||||
"settings_comments": "users",
|
||||
"slug" : "fallout-3-intro-remake",
|
||||
"title" : "Fallout 3 Intro Remake",
|
||||
"token" : "k5E2z",
|
||||
"user_id" : 1,
|
||||
"waveform_url" : r"re:https://cdn.whyp.it/5e9de576-f33a-40ea-bd43-1693a568a6a0.json",
|
||||
"user" : {
|
||||
"avatar" : "https://cdn.whyp.it/a46f3485-8d19-4753-98e0-76011c7e33b0.jpg",
|
||||
"has_enterprise" : True,
|
||||
"has_pro" : True,
|
||||
"has_pro_lifetime": False,
|
||||
"id" : 1,
|
||||
"slug" : "brad",
|
||||
"status" : "Coding 👨🏻💻",
|
||||
"tracks_count" : 3,
|
||||
"username" : "Brad",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
"#url" : "https://whyp.it/users/1/brad",
|
||||
"#class" : whyp.WhypUserExtractor,
|
||||
"#pattern" : (
|
||||
r"https://cdn.whyp.it/5e9de576-f33a-40ea-bd43-1693a568a6a0.mp3\?token=.+",
|
||||
r"https://cdn.whyp.it/0d7a196b-3e1a-4510-a4a4-6189c56ecb27.flac\?token=.+",
|
||||
r"https://cdn.whyp.it/3d134d07-7c55-4a6b-b321-56ce90ee1fc8.flac\?token=.+",
|
||||
),
|
||||
|
||||
"allow_downloads": bool,
|
||||
"artwork_url" : {str, None},
|
||||
"artwork_url_fallback": str,
|
||||
"comments_count" : int,
|
||||
"created_at" : "iso:dt",
|
||||
"date" : "type:datetime",
|
||||
"description" : str,
|
||||
"duration" : float,
|
||||
"extension" : {"mp3", "flac"},
|
||||
"filename" : "iso:uuid",
|
||||
"id" : {13721, 18337, 324260},
|
||||
"lossless_size" : {int, None},
|
||||
"lossless_url" : {str, None},
|
||||
"lossy_size" : int,
|
||||
"lossy_url" : str,
|
||||
"original" : bool,
|
||||
"public" : True,
|
||||
"settings_comments": "users",
|
||||
"slug" : str,
|
||||
"title" : str,
|
||||
"token" : str,
|
||||
"user_id" : 1,
|
||||
"waveform_url" : str,
|
||||
"user" : {
|
||||
"avatar" : "https://cdn.whyp.it/a46f3485-8d19-4753-98e0-76011c7e33b0.jpg",
|
||||
"has_enterprise" : True,
|
||||
"has_pro" : True,
|
||||
"has_pro_lifetime": False,
|
||||
"id" : 1,
|
||||
"slug" : "brad",
|
||||
"status" : "Coding 👨🏻💻",
|
||||
"tracks_count" : 3,
|
||||
"username" : "Brad",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
"#url" : "https://whyp.it/collections/1/example-collection",
|
||||
"#class" : whyp.WhypCollectionExtractor,
|
||||
"#pattern" : (
|
||||
r"https://cdn.whyp.it/3d134d07-7c55-4a6b-b321-56ce90ee1fc8.flac\?token=.+",
|
||||
r"https://cdn.whyp.it/0d7a196b-3e1a-4510-a4a4-6189c56ecb27.flac\?token=.+",
|
||||
),
|
||||
|
||||
"extension" : "flac",
|
||||
"id" : {18337, 324260},
|
||||
"original" : True,
|
||||
"pivot_collection_id": 1,
|
||||
"pivot_created_at": "iso:dt",
|
||||
"pivot_order" : int,
|
||||
"public" : True,
|
||||
"collection" : {
|
||||
"artwork_url" : "https://cdn.whyp.it/60fff341-02ef-4fe2-86f7-f283b2df1557.jpg",
|
||||
"artwork_url_fallback": "https://cdn.whyp.it/b42b34d3-5839-4a26-9c32-41e917f31f6b.jpg",
|
||||
"created_at" : "2023-07-20T16:14:33+00:00",
|
||||
"description" : "This is an example collection on Whyp!",
|
||||
"duration" : 352.59,
|
||||
"hidden_tracks_count": 0,
|
||||
"id" : 1,
|
||||
"order" : 1,
|
||||
"public" : True,
|
||||
"slug" : "example-collection",
|
||||
"title" : "Example Collection",
|
||||
"token" : "VFc7Q",
|
||||
"tracks_count": 2,
|
||||
"updated_at" : "2025-11-17T19:45:01+00:00",
|
||||
"user_id" : 1,
|
||||
"user" : dict,
|
||||
},
|
||||
"user" : {
|
||||
"avatar" : "https://cdn.whyp.it/a46f3485-8d19-4753-98e0-76011c7e33b0.jpg",
|
||||
"has_enterprise" : True,
|
||||
"has_pro" : True,
|
||||
"has_pro_lifetime": False,
|
||||
"id" : 1,
|
||||
"slug" : "brad",
|
||||
"status" : "Coding 👨🏻💻",
|
||||
"tracks_count" : 3,
|
||||
"username" : "Brad",
|
||||
},
|
||||
},
|
||||
|
||||
)
|
||||
Reference in New Issue
Block a user