[audiochan] add initial support (#8602)

This commit is contained in:
Mike Fährmann
2025-11-28 21:46:22 +01:00
parent e589d6f6ee
commit 994cc34f75
5 changed files with 164 additions and 0 deletions

View File

@@ -121,6 +121,12 @@ Consider all listed sites to potentially be NSFW.
<td>Albums, Artwork Listings, Challenges, Collections, Followed Users, individual Images, Likes, Search Results, User Profiles</td>
<td></td>
</tr>
<tr id="audiochan" title="audiochan">
<td>Audiochan</td>
<td>https://audiochan.com/</td>
<td>Audios, Collections, User Profiles</td>
<td></td>
</tr>
<tr id="batoto" title="batoto">
<td>BATO.TO</td>
<td>https://bato.to/</td>

View File

@@ -29,6 +29,7 @@ modules = [
"arena",
"artstation",
"aryion",
"audiochan",
"batoto",
"bbc",
"behance",

View File

@@ -0,0 +1,115 @@
# -*- 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://audiochan.com/"""
from .common import Extractor, Message
from .. import text
BASE_PATTERN = r"(?:https?://)?(?:www\.)?audiochan\.com"
class AudiochanExtractor(Extractor):
"""Base class for audiochan extractors"""
category = "audiochan"
root = "https://audiochan.com"
root_api = "https://api.audiochan.com"
directory_fmt = ("{category}", "{user[display_name]}")
filename_fmt = "{title} ({slug}).{extension}"
archive_fmt = "{audioFile[id]}"
def _init(self):
self.headers_api = {
"content-type" : "application/json",
"Origin" : self.root,
"Sec-Fetch-Dest" : "empty",
"Sec-Fetch-Mode" : "cors",
"Sec-Fetch-Site" : "same-site",
}
self.headers_dl = {
"Accept": "audio/webm,audio/ogg,audio/wav,audio/*;q=0.9,"
"application/ogg;q=0.7,video/*;q=0.6,*/*;q=0.5",
# "Range" : "bytes=0-",
"Sec-Fetch-Dest" : "audio",
"Sec-Fetch-Mode" : "no-cors",
"Sec-Fetch-Site" : "same-site",
"Accept-Encoding": "identity",
}
def items(self):
for post in self.posts():
file = post["audioFile"]
post["_http_headers"] = self.headers_dl
post["date"] = self.parse_datetime_iso(file["created_at"])
post["date_updated"] = self.parse_datetime_iso(file["updated_at"])
post["tags"] = [f"{tag['category']}:{tag['name']}"
for tag in post["tags"]]
yield Message.Directory, post
text.nameext_from_name(file["filename"], post)
yield Message.Url, file["url"] or file["stream_url"], post
def request_api(self, endpoint, params=None):
url = self.root_api + endpoint
return self.request_json(url, params=params, headers=self.headers_api)
def _pagination(self, endpoint, params):
params["page"] = 1
params["limit"] = "12"
while True:
data = self.request_api(endpoint, params)
yield from data["data"]
if not data["has_more"]:
break
params["page"] += 1
class AudiochanAudioExtractor(AudiochanExtractor):
subcategory = "audio"
pattern = rf"{BASE_PATTERN}/a/(\w+)"
example = "https://audiochan.com/a/SLUG"
def posts(self):
audio = self.request_api("/audios/slug/" + self.groups[0])
audio["user"] = audio["credits"][0]["user"]
return (audio,)
class AudiochanUserExtractor(AudiochanExtractor):
subcategory = "user"
pattern = rf"{BASE_PATTERN}/u/(\w+)"
example = "https://audiochan.com/u/USER"
def posts(self):
endpoint = "/users/" + self.groups[0]
self.kwdict["user"] = self.request_api(endpoint)["data"]
params = {
"sfw_only": "false",
"sort" : "new",
}
return self._pagination(endpoint + "/audios", params)
class AudiochanCollectionExtractor(AudiochanExtractor):
subcategory = "collection"
pattern = rf"{BASE_PATTERN}/c/(\w+)"
example = "https://audiochan.com/c/SLUG"
def posts(self):
slug = self.groups[0]
endpoint = "/collections/" + slug
self.kwdict["collection"] = col = self.request_api(endpoint)
col.pop("audios", None)
endpoint = f"/collections/slug/{slug}/items"
return self._pagination(endpoint, {})

View File

@@ -245,6 +245,9 @@ SUBCATEGORY_MAP = {
"artwork": "Artwork Listings",
"collections": "",
},
"audiochan": {
"audio": "Audios",
},
"bilibili": {
"user-articles-favorite": "User Article Favorites",
},

39
test/results/audiochan.py Normal file
View File

@@ -0,0 +1,39 @@
# -*- 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 audiochan
__tests__ = (
{
"#url" : "https://audiochan.com/a/pBP1V1ODEV2od9CjLu",
"#class" : audiochan.AudiochanAudioExtractor,
"#pattern" : r"https://stream.audiochan.com/v\?token=YXVkaW9zL2Q4YjA1ZWEzLWU0ZGItNGU2NC05MzZiLTQzNmI3MmM4OTViMS9sOTBCOFI0ajhjS0NFSmNwa2kubXAz&exp=\d+&st=\w+",
"#count" : 1,
},
{
"#url" : "https://audiochan.com/u/lil_lovergirl",
"#class" : audiochan.AudiochanUserExtractor,
"#pattern" : r"https://stream\.audiochan\.com/v\?token=\w+\&exp=\d+\&st=\w+",
"#count" : 35,
},
{
"#url" : "https://audiochan.com/c/qzrByaXAwTLVXRgC9m",
"#class" : audiochan.AudiochanCollectionExtractor,
"#results" : (
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/l90B8R4j8cKCEJcpki.mp3",
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/IPI4XoXS1Z1Qn7oEiN.mp3",
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/6kwizqnvUHttvUkXm6.mp3",
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/zn81mtgXslfDd20Tu8.wav",
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/Q33gP6yAg8jEM1C4Ic.mp3",
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/Fwy5YxgK4zc7sQ9xx3.mp3",
"https://content.audiochan.com/audios/d8b05ea3-e4db-4e64-936b-436b72c895b1/P3YrtAdKVekYb3BTgy.mp3",
),
},
)