[whyp] add support (#8725)

This commit is contained in:
Mike Fährmann
2025-12-31 18:58:33 +01:00
parent 2087730b75
commit 8792d7faea
4 changed files with 248 additions and 0 deletions

View File

@@ -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>

View File

@@ -234,6 +234,7 @@ modules = [
"weebcentral",
"weebdex",
"weibo",
"whyp",
"wikiart",
"wikifeet",
"wikimedia",

View 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
View 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",
},
},
)