- commit
- 7c3d8594bbf68f494385eba5b71fdeb69a360531
- parent
- 0f1ee7b0f1aa0c8d4fba341b9f69ed12db584de8
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-03-27 10:29
add mixcloud provider
Diffstat
| M | quickstream/__init__.py | 8 | ++++++++ |
| A | quickstream/providers/mixcloud.py | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 56 insertions, 0 deletions
diff --git a/quickstream/__init__.py b/quickstream/__init__.py
@@ -7,6 +7,7 @@ from bs4 import BeautifulSoup 7 7 8 8 from .base import registry 9 9 from .providers import bandcamp # noqa -1 10 from .providers import mixcloud # noqa 10 11 from .providers import soundcloud # noqa 11 12 12 13 @@ -26,6 +27,13 @@ class Client: 26 27 r = await self.session.get(url, **kwargs) 27 28 return await r.json() 28 29 -1 30 async def graphql(self, url, query, **kwargs): -1 31 r = await self.session.post(url, json={ -1 32 'query': query, -1 33 'variables': kwargs, -1 34 }) -1 35 return await r.json() -1 36 29 37 30 38 async def extract(url): 31 39 for pattern, fn in registry:
diff --git a/quickstream/providers/mixcloud.py b/quickstream/providers/mixcloud.py
@@ -0,0 +1,48 @@
-1 1 import base64
-1 2 import itertools
-1 3 import json
-1 4
-1 5 from ..base import provider
-1 6
-1 7 DECRYPTION_KEY = b'IFYOUWANTTHEARTISTSTOGETPAIDDONOTDOWNLOADFROMMIXCLOUD'
-1 8 QUERY = """
-1 9 query cloudcastQuery($lookup: CloudcastLookup!) {
-1 10 cloudcast: cloudcastLookup(lookup: $lookup) {
-1 11 name
-1 12 audioLength
-1 13 streamInfo { url }
-1 14 }
-1 15 }
-1 16 """
-1 17
-1 18
-1 19 def decrypt(text):
-1 20 raw = base64.b64decode(text)
-1 21 pairs = zip(raw, itertools.cycle(DECRYPTION_KEY), strict=False)
-1 22 result = bytes([x ^ y for x, y in pairs])
-1 23 return result.decode('utf-8')
-1 24
-1 25
-1 26 @provider(
-1 27 r'https?://www.mixcloud\.com/([^/]+)/(?!stream|uploads|favorites|listens|playlists)([^/]+)/', # noqa
-1 28 tests={
-1 29 'http://www.mixcloud.com/dholbach/cryptkeeper/': {
-1 30 'id': 'dholbach_cryptkeeper',
-1 31 'title': 'Cryptkeeper',
-1 32 'duration': 3723,
-1 33 },
-1 34 },
-1 35 )
-1 36 async def mixcloud_track(client, url, username, slug):
-1 37 data = await client.graphql('https://app.mixcloud.com/graphql', QUERY, lookup={
-1 38 'username': username,
-1 39 'slug': slug,
-1 40 })
-1 41 trackinfo = data['data']['cloudcast']
-1 42
-1 43 return {
-1 44 'id': f'{username}_{slug}',
-1 45 'title': trackinfo['name'],
-1 46 'duration': trackinfo['audioLength'],
-1 47 'stream': decrypt(trackinfo['streamInfo']['url']),
-1 48 }