okmusic-api 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- okmusic_api-0.1.0/PKG-INFO +40 -0
- okmusic_api-0.1.0/README.md +25 -0
- okmusic_api-0.1.0/pyproject.toml +26 -0
- okmusic_api-0.1.0/setup.cfg +4 -0
- okmusic_api-0.1.0/src/okmusic_api/__init__.py +4 -0
- okmusic_api-0.1.0/src/okmusic_api/sdk.py +170 -0
- okmusic_api-0.1.0/src/okmusic_api.egg-info/PKG-INFO +40 -0
- okmusic_api-0.1.0/src/okmusic_api.egg-info/SOURCES.txt +9 -0
- okmusic_api-0.1.0/src/okmusic_api.egg-info/dependency_links.txt +1 -0
- okmusic_api-0.1.0/src/okmusic_api.egg-info/requires.txt +1 -0
- okmusic_api-0.1.0/src/okmusic_api.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: okmusic-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Неофициальный бронированный SDK для работы с музыкальным API Одноклассников (WMF) через jsessionid
|
|
5
|
+
Author-email: l0v3m0n3y <ffxckemweball@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/l0v3m0n3y/OkMusic
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/l0v3m0n3y/OkMusic/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
12
|
+
Requires-Python: >=3.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: curl_cffi>=0.7.0
|
|
15
|
+
|
|
16
|
+
# okmusic-api
|
|
17
|
+
|
|
18
|
+
Неофициальный Python SDK для работы со скрытым музыкальным API Одноклассников (WMF).
|
|
19
|
+
Использует `curl_cffi` для идеального обхода TLS-фингерпринтинга (JA3/JA4) распределенной сети CDN Одноклассников.
|
|
20
|
+
|
|
21
|
+
## Установка
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install okmusic-api
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Быстрый старт
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from okmusic_api import OKMusicSDK
|
|
31
|
+
|
|
32
|
+
# Твой jsessionid из кук браузера
|
|
33
|
+
sdk = OKMusicSDK(jsessionid="kdCHNeSo76je139vgu...")
|
|
34
|
+
|
|
35
|
+
# Поиск
|
|
36
|
+
results = sdk.search("jesus avgn")
|
|
37
|
+
|
|
38
|
+
# Скачивание трека целиком на диск (без пустых файлов в 0 байт)
|
|
39
|
+
sdk.download_track(123286133220562, "jesus_vssha.mp3")
|
|
40
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# okmusic-api
|
|
2
|
+
|
|
3
|
+
Неофициальный Python SDK для работы со скрытым музыкальным API Одноклассников (WMF).
|
|
4
|
+
Использует `curl_cffi` для идеального обхода TLS-фингерпринтинга (JA3/JA4) распределенной сети CDN Одноклассников.
|
|
5
|
+
|
|
6
|
+
## Установка
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install okmusic-api
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Быстрый старт
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from okmusic_api import OKMusicSDK
|
|
16
|
+
|
|
17
|
+
# Твой jsessionid из кук браузера
|
|
18
|
+
sdk = OKMusicSDK(jsessionid="kdCHNeSo76je139vgu...")
|
|
19
|
+
|
|
20
|
+
# Поиск
|
|
21
|
+
results = sdk.search("jesus avgn")
|
|
22
|
+
|
|
23
|
+
# Скачивание трека целиком на диск (без пустых файлов в 0 байт)
|
|
24
|
+
sdk.download_track(123286133220562, "jesus_vssha.mp3")
|
|
25
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "okmusic-api" # Имя для pip install okmusic-api
|
|
7
|
+
version = "0.1.0" # Начинаем историю релизов красиво с чистой 0.1.0
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "l0v3m0n3y", email = "ffxckemweball@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Неофициальный бронированный SDK для работы с музыкальным API Одноклассников (WMF) через jsessionid"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.7"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Multimedia :: Sound/Audio",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"curl_cffi>=0.7.0", # Зависимость, которая пробивает TLS-защиту CDN
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
"Homepage" = "https://github.com/l0v3m0n3y/OkMusic"
|
|
26
|
+
"Bug Tracker" = "https://github.com/l0v3m0n3y/OkMusic/issues"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from curl_cffi import requests
|
|
2
|
+
from typing import Optional, Dict, Any
|
|
3
|
+
import hashlib
|
|
4
|
+
import re
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OKMusicSDK:
|
|
9
|
+
def __init__(self, jsessionid: str):
|
|
10
|
+
self.jsessionid = jsessionid
|
|
11
|
+
self.base_url = "https://wmf.ok.ru"
|
|
12
|
+
|
|
13
|
+
self.session = requests.Session(impersonate="chrome")
|
|
14
|
+
|
|
15
|
+
self.common_headers = {
|
|
16
|
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36",
|
|
17
|
+
"Accept-Language": "ru-RU,ru;q=0.9,en;q=0.8",
|
|
18
|
+
"Origin": "https://ok.ru",
|
|
19
|
+
"sec-ch-ua": '"Google Chrome";v="155", "Chromium";v="155", "Not(A:Brand";v="24"',
|
|
20
|
+
"sec-ch-ua-mobile": "?0",
|
|
21
|
+
"sec-ch-ua-platform": '"Linux"',
|
|
22
|
+
}
|
|
23
|
+
self.session.headers.update(self.common_headers)
|
|
24
|
+
|
|
25
|
+
self.session.cookies.set("jsessionid", self.jsessionid, domain=".ok.ru")
|
|
26
|
+
self.session.cookies.set("jsessionid", self.jsessionid, domain=".okcdn.ru")
|
|
27
|
+
self.session.cookies.set("jsessionid", self.jsessionid, domain="wmf.ok.ru")
|
|
28
|
+
|
|
29
|
+
# Массив из JS
|
|
30
|
+
self._m = [4, 3, 5, 6, 1, 2, 8, 7, 2, 9, 3, 5, 7, 1, 4, 8, 8, 3, 4, 3, 1, 7, 3, 5, 9, 8, 1, 4, 3, 7, 2, 8]
|
|
31
|
+
|
|
32
|
+
def _generate_client_hash(self, stream_url: str) -> str:
|
|
33
|
+
"""Генерирует clientHash по алгоритму из shared.js"""
|
|
34
|
+
match = re.search(r"md5=([0-9a-fA-F]+)", stream_url)
|
|
35
|
+
if not match:
|
|
36
|
+
raise ValueError("md5 не найден в URL")
|
|
37
|
+
|
|
38
|
+
md5_value = match.group(1)
|
|
39
|
+
# MD5(md5 + "secret")
|
|
40
|
+
hashed = hashlib.md5((md5_value + "secret").encode()).hexdigest()
|
|
41
|
+
|
|
42
|
+
# Сумма всех hex-цифр
|
|
43
|
+
a = sum(int(ch, 16) for ch in hashed)
|
|
44
|
+
|
|
45
|
+
result = []
|
|
46
|
+
length = len(hashed)
|
|
47
|
+
for c in range(length):
|
|
48
|
+
u = int(hashed[c], 16)
|
|
49
|
+
n = u if c == length - 1 else int(hashed[c + 1], 16)
|
|
50
|
+
result.append(str(abs(a - u * n * self._m[c])))
|
|
51
|
+
|
|
52
|
+
return "".join(result)
|
|
53
|
+
|
|
54
|
+
def _add_client_hash(self, stream_url: str) -> str:
|
|
55
|
+
if "clientHash=" in stream_url:
|
|
56
|
+
return stream_url
|
|
57
|
+
client_hash = self._generate_client_hash(stream_url)
|
|
58
|
+
return f"{stream_url}&client=web&clientHash={client_hash}"
|
|
59
|
+
|
|
60
|
+
def get_stream_link(self, track_id: int, ctx: str = "my|1") -> str:
|
|
61
|
+
url = f"{self.base_url}/play;jsessionid={self.jsessionid}"
|
|
62
|
+
|
|
63
|
+
params = {
|
|
64
|
+
"tid": track_id,
|
|
65
|
+
"ctx": ctx,
|
|
66
|
+
"client": "web",
|
|
67
|
+
"locale": "ru",
|
|
68
|
+
"imgfmt": "base",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
headers = {
|
|
72
|
+
**self.common_headers,
|
|
73
|
+
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
74
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
75
|
+
"Referer": "https://ok.ru/music/profile/910722294189",
|
|
76
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
response = self.session.post(
|
|
81
|
+
url,
|
|
82
|
+
params=params,
|
|
83
|
+
data="",
|
|
84
|
+
headers=headers,
|
|
85
|
+
timeout=20,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
print(f"[OKMusic] play → {response.status_code}")
|
|
89
|
+
|
|
90
|
+
if response.status_code != 200:
|
|
91
|
+
print(response.text[:400])
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
data = response.json()
|
|
95
|
+
stream_url = data.get("play", "")
|
|
96
|
+
|
|
97
|
+
if not stream_url:
|
|
98
|
+
print("[OKMusic] Нет поля play в ответе:", data)
|
|
99
|
+
return ""
|
|
100
|
+
|
|
101
|
+
# Добавляем правильно сгенерированный clientHash
|
|
102
|
+
return self._add_client_hash(stream_url)
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"[OKMusic] Ошибка get_stream_link: {e}")
|
|
106
|
+
return ""
|
|
107
|
+
|
|
108
|
+
def download_track(self, track_id: int, output_filename: str = "track.mp3") -> bool:
|
|
109
|
+
stream_url = self.get_stream_link(track_id)
|
|
110
|
+
if not stream_url:
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
print(f"[OKMusic] Скачиваем:\n{stream_url}\n")
|
|
114
|
+
|
|
115
|
+
headers = {
|
|
116
|
+
**self.common_headers,
|
|
117
|
+
"Accept": "*/*",
|
|
118
|
+
"Accept-Encoding": "identity",
|
|
119
|
+
"Referer": "https://ok.ru/music/profile/910722294189",
|
|
120
|
+
"sec-fetch-dest": "audio",
|
|
121
|
+
"sec-fetch-mode": "no-cors",
|
|
122
|
+
"sec-fetch-site": "cross-site",
|
|
123
|
+
"Range": "bytes=0-",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
response = self.session.get(
|
|
128
|
+
stream_url,
|
|
129
|
+
headers=headers,
|
|
130
|
+
allow_redirects=True,
|
|
131
|
+
timeout=60,
|
|
132
|
+
stream=True,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
print(f"[OKMusic] CDN статус: {response.status_code}")
|
|
136
|
+
|
|
137
|
+
if response.status_code not in (200, 206):
|
|
138
|
+
print("Заголовки:", dict(response.headers))
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
with open(output_filename, "wb") as f:
|
|
142
|
+
for chunk in response.iter_content(chunk_size=65536):
|
|
143
|
+
if chunk:
|
|
144
|
+
f.write(chunk)
|
|
145
|
+
|
|
146
|
+
print(f"[OKMusic] Сохранено → {output_filename}")
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"[OKMusic] Ошибка скачивания: {e}")
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
def search(self, query: str) -> Dict[str, Any]:
|
|
154
|
+
url = f"{self.base_url}/queryMixedSuggestions;jsessionid={self.jsessionid}"
|
|
155
|
+
params = {"q": query, "client": "web", "locale": "ru"}
|
|
156
|
+
|
|
157
|
+
headers = {
|
|
158
|
+
**self.common_headers,
|
|
159
|
+
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
160
|
+
"Referer": "https://ok.ru/",
|
|
161
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
response = self.session.get(url, params=params, headers=headers, timeout=15)
|
|
166
|
+
response.raise_for_status()
|
|
167
|
+
return response.json()
|
|
168
|
+
except Exception as e:
|
|
169
|
+
print(f"[OKMusic] Ошибка поиска: {e}")
|
|
170
|
+
return {}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: okmusic-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Неофициальный бронированный SDK для работы с музыкальным API Одноклассников (WMF) через jsessionid
|
|
5
|
+
Author-email: l0v3m0n3y <ffxckemweball@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/l0v3m0n3y/OkMusic
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/l0v3m0n3y/OkMusic/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
12
|
+
Requires-Python: >=3.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: curl_cffi>=0.7.0
|
|
15
|
+
|
|
16
|
+
# okmusic-api
|
|
17
|
+
|
|
18
|
+
Неофициальный Python SDK для работы со скрытым музыкальным API Одноклассников (WMF).
|
|
19
|
+
Использует `curl_cffi` для идеального обхода TLS-фингерпринтинга (JA3/JA4) распределенной сети CDN Одноклассников.
|
|
20
|
+
|
|
21
|
+
## Установка
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install okmusic-api
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Быстрый старт
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from okmusic_api import OKMusicSDK
|
|
31
|
+
|
|
32
|
+
# Твой jsessionid из кук браузера
|
|
33
|
+
sdk = OKMusicSDK(jsessionid="kdCHNeSo76je139vgu...")
|
|
34
|
+
|
|
35
|
+
# Поиск
|
|
36
|
+
results = sdk.search("jesus avgn")
|
|
37
|
+
|
|
38
|
+
# Скачивание трека целиком на диск (без пустых файлов в 0 байт)
|
|
39
|
+
sdk.download_track(123286133220562, "jesus_vssha.mp3")
|
|
40
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/okmusic_api/__init__.py
|
|
4
|
+
src/okmusic_api/sdk.py
|
|
5
|
+
src/okmusic_api.egg-info/PKG-INFO
|
|
6
|
+
src/okmusic_api.egg-info/SOURCES.txt
|
|
7
|
+
src/okmusic_api.egg-info/dependency_links.txt
|
|
8
|
+
src/okmusic_api.egg-info/requires.txt
|
|
9
|
+
src/okmusic_api.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
curl_cffi>=0.7.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
okmusic_api
|