ymdantic 0.2.0__tar.gz → 0.2.2__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.
- ymdantic-0.2.2/.github/workflows/tests.yml +104 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/.pre-commit-config.yaml +5 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/PKG-INFO +9 -23
- {ymdantic-0.2.0 → ymdantic-0.2.2}/README.md +7 -21
- ymdantic-0.2.2/examples/chart.py +32 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/pyproject.toml +2 -2
- ymdantic-0.2.2/tests/account/test_settings.py +25 -0
- ymdantic-0.2.2/tests/search/test_search_instant_mixed.py +141 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/tests/tracks/test_get_track.py +1 -1
- ymdantic-0.2.2/tests/tracks/test_get_track_file_info.py +21 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/tests/tracks/test_get_tracks.py +2 -2
- ymdantic-0.2.2/uv.lock +1162 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/adapters/pydantic_factory.py +8 -4
- ymdantic-0.2.2/ymdantic/adapters/to_query_params.py +19 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/client/session/aiohttp_client.py +62 -28
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/client/yandex_music.py +87 -7
- ymdantic-0.2.2/ymdantic/exceptions/__init__.py +7 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/mixins.py +15 -2
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/__init__.py +9 -0
- ymdantic-0.2.2/ymdantic/models/account/__init__.py +3 -0
- ymdantic-0.2.2/ymdantic/models/account/settings.py +86 -0
- ymdantic-0.2.2/ymdantic/models/agent.py +11 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/albums/album.py +23 -2
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/artist.py +8 -3
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/ratings.py +3 -3
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/social_link.py +3 -3
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/base.py +11 -1
- ymdantic-0.2.2/ymdantic/models/concerts/__init__.py +0 -0
- ymdantic-0.2.2/ymdantic/models/content_restrictions.py +13 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/cover.py +21 -0
- ymdantic-0.2.2/ymdantic/models/custom_wave.py +31 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/album.py +9 -4
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/artist.py +5 -0
- ymdantic-0.2.2/ymdantic/models/landing/chart.py +9 -0
- ymdantic-0.2.2/ymdantic/models/landing/cover.py +24 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/editorial/new_releases.py +2 -2
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/in_style.py +4 -4
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/playlist.py +2 -2
- ymdantic-0.2.2/ymdantic/models/landing/waves.py +76 -0
- ymdantic-0.2.2/ymdantic/models/landing3/chart.py +21 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/playlists/playlist.py +61 -57
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/playlists/track.py +2 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/s3.py +1 -1
- ymdantic-0.2.2/ymdantic/models/search/__init__.py +6 -0
- ymdantic-0.2.2/ymdantic/models/search/base.py +76 -0
- ymdantic-0.2.2/ymdantic/models/search/results.py +60 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/__init__.py +4 -2
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/album.py +1 -0
- ymdantic-0.2.2/ymdantic/models/tracks/file_info.py +80 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/track.py +2 -1
- ymdantic-0.2.2/ymdantic/models/trailer.py +8 -0
- ymdantic-0.2.2/ymdantic/py.typed +0 -0
- ymdantic-0.2.0/.github/workflows/tests.yml +0 -35
- ymdantic-0.2.0/examples/chart.py +0 -23
- ymdantic-0.2.0/uv.lock +0 -1205
- ymdantic-0.2.0/ymdantic/exceptions/__init__.py +0 -3
- ymdantic-0.2.0/ymdantic/models/custom_wave.py +0 -18
- ymdantic-0.2.0/ymdantic/models/landing/chart.py +0 -30
- ymdantic-0.2.0/ymdantic/models/landing/cover.py +0 -44
- ymdantic-0.2.0/ymdantic/models/landing/waves.py +0 -47
- ymdantic-0.2.0/ymdantic/models/landing3/chart.py +0 -56
- {ymdantic-0.2.0 → ymdantic-0.2.2}/.editorconfig +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/.github/workflows/pypi-release.yml +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/.gitignore +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/LICENSE +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/tests/__init__.py +0 -0
- {ymdantic-0.2.0/tests/tracks → ymdantic-0.2.2/tests/account}/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/tests/conftest.py +0 -0
- {ymdantic-0.2.0/ymdantic/models/clips → ymdantic-0.2.2/tests/search}/__init__.py +0 -0
- {ymdantic-0.2.0/ymdantic/models/concerts → ymdantic-0.2.2/tests/tracks}/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/tests/tracks/test_get_track_download_info.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/adapters/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/adapters/to_camel.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/client/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/client/session/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/client/session/aiohttp_method.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/editorial_artists.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/editorial_compilation.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/editorial_new_releases.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/editorial_promotion.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/language.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/open_playlist.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/enums/special.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/exceptions/custom_exceptions.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/action_button.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/albums/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/albums/label.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/artist_data.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/artist_track_chart_position.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/band_link_scanner_link.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/concert.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/counts.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/description.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/extra_action.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/artists/video.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/chart_position.py +0 -0
- /ymdantic-0.2.0/ymdantic/py.typed → /ymdantic-0.2.2/ymdantic/models/clips/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/clips/clip.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/concerts/city_case_forms.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/concerts/concert.py +0 -0
- {ymdantic-0.2.0/ymdantic/models/tracks → ymdantic-0.2.2/ymdantic/models}/derived_colors.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/error.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/chart_block.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/editorial/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/editorial/response.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/liked_playlist.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/open_playlist.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/personal_playlist.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/promotion.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/skeleton_response.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/tab_block.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/tab_data.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/tab_source.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/tabs_block.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/skeleton/tabs_source.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/special.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing/view_all_action.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing3/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing3/block.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/landing3/menu.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/pager.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/playlists/__init__.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/playlists/cover.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/playlists/owner.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/playlists/tag.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/response.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/theme.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/download_info.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/fade.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/lyrics_info.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/major.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/podcast.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/position.py +0 -0
- {ymdantic-0.2.0 → ymdantic-0.2.2}/ymdantic/models/tracks/r128.py +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
name: Testing YMdantic
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
pull_request_target:
|
|
7
|
+
types: [opened, synchronize, reopened]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
if: github.event_name != 'pull_request_target'
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
17
|
+
check:
|
|
18
|
+
- name: Ruff format
|
|
19
|
+
cmd: ruff format --diff
|
|
20
|
+
- name: Ruff check
|
|
21
|
+
cmd: ruff check ymdantic tests
|
|
22
|
+
- name: Mypy
|
|
23
|
+
cmd: mypy ymdantic tests
|
|
24
|
+
|
|
25
|
+
name: "${{ matrix.check.name }} (py${{ matrix.python-version }})"
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
|
+
|
|
30
|
+
- name: Install uv
|
|
31
|
+
uses: astral-sh/setup-uv@v7
|
|
32
|
+
with:
|
|
33
|
+
version: "0.9.17"
|
|
34
|
+
python-version: ${{ matrix.python-version }}
|
|
35
|
+
enable-cache: true
|
|
36
|
+
|
|
37
|
+
- name: Install deps
|
|
38
|
+
run: uv sync --locked --all-groups
|
|
39
|
+
|
|
40
|
+
- name: Run check
|
|
41
|
+
run: uv run ${{ matrix.check.cmd }}
|
|
42
|
+
|
|
43
|
+
test_push:
|
|
44
|
+
if: github.event_name == 'push'
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
strategy:
|
|
47
|
+
fail-fast: false
|
|
48
|
+
matrix:
|
|
49
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
50
|
+
|
|
51
|
+
name: "Pytest push (py${{ matrix.python-version }})"
|
|
52
|
+
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v6
|
|
55
|
+
|
|
56
|
+
- name: Install uv
|
|
57
|
+
uses: astral-sh/setup-uv@v7
|
|
58
|
+
with:
|
|
59
|
+
version: "0.9.17"
|
|
60
|
+
python-version: ${{ matrix.python-version }}
|
|
61
|
+
enable-cache: true
|
|
62
|
+
|
|
63
|
+
- name: Install deps
|
|
64
|
+
run: uv sync --locked --all-groups
|
|
65
|
+
|
|
66
|
+
- name: Run Pytest
|
|
67
|
+
env:
|
|
68
|
+
YM_TOKEN: ${{ secrets.YM_TOKEN }}
|
|
69
|
+
run: uv run pytest -vv tests
|
|
70
|
+
|
|
71
|
+
test_pr_approved:
|
|
72
|
+
if: github.event_name == 'pull_request_target'
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
environment: pull_requests
|
|
75
|
+
strategy:
|
|
76
|
+
fail-fast: false
|
|
77
|
+
matrix:
|
|
78
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
79
|
+
|
|
80
|
+
name: "Pytest PR approved (py${{ matrix.python-version }})"
|
|
81
|
+
|
|
82
|
+
permissions:
|
|
83
|
+
contents: read
|
|
84
|
+
pull-requests: read
|
|
85
|
+
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v6
|
|
88
|
+
with:
|
|
89
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
90
|
+
|
|
91
|
+
- name: Install uv
|
|
92
|
+
uses: astral-sh/setup-uv@v7
|
|
93
|
+
with:
|
|
94
|
+
version: "0.9.17"
|
|
95
|
+
python-version: ${{ matrix.python-version }}
|
|
96
|
+
enable-cache: true
|
|
97
|
+
|
|
98
|
+
- name: Install deps
|
|
99
|
+
run: uv sync --locked --all-groups
|
|
100
|
+
|
|
101
|
+
- name: Run Pytest
|
|
102
|
+
env:
|
|
103
|
+
YM_TOKEN: ${{ secrets.YM_TOKEN }}
|
|
104
|
+
run: uv run pytest -vv tests
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ymdantic
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Фреймворк предоставляющий доступ к API Яндекс Музыки (с Pydantic валидацией)
|
|
5
5
|
Author-email: KatantDev <vlad26@mail.ru>
|
|
6
6
|
License-File: LICENSE
|
|
7
|
-
Requires-Python: <3.
|
|
7
|
+
Requires-Python: <3.15,>=3.12
|
|
8
8
|
Requires-Dist: aiohttp<4,>=3.9.0
|
|
9
9
|
Requires-Dist: certifi<2026,>=2025
|
|
10
10
|
Requires-Dist: dataclass-rest>=0.5.0
|
|
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
**YMDantic** - это клиентская библиотека на Python для работы с API Yandex Music.
|
|
19
19
|
Она позволяет получать информацию о треках, альбомах, плейлистах и других объектах сервиса.
|
|
20
20
|
|
|
21
|
-
Особенностью библиотеки является использование [Pydantic](https://
|
|
21
|
+
Особенностью библиотеки является использование [Pydantic](https://docs.pydantic.dev/) для валидации и сериализации данных.
|
|
22
22
|
|
|
23
23
|
## Технологии
|
|
24
24
|
Проект написан на Python и использует следующие библиотеки:
|
|
@@ -27,7 +27,7 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
- Pydantic для валидации и сериализации данных
|
|
28
28
|
|
|
29
29
|
## Установка и использование
|
|
30
|
-
Для установки библиотеки используйте pip/poetry:
|
|
30
|
+
Для установки библиотеки используйте pip/poetry/uv:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
pip install ymdantic
|
|
@@ -36,23 +36,9 @@ pip install ymdantic
|
|
|
36
36
|
```bash
|
|
37
37
|
poetry add ymdantic
|
|
38
38
|
```
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```python
|
|
44
|
-
import asyncio
|
|
45
|
-
from ymdantic import YMClient
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
async def get_chart(client: YMClient) -> None:
|
|
49
|
-
chart_block = await client.get_chart()
|
|
50
|
-
print(chart_block.title, end="\n\n")
|
|
51
|
-
print("Треки в чарте:")
|
|
52
|
-
for playlist_track in chart_block.chart.tracks:
|
|
53
|
-
print(f"{playlist_track.track.artists_names} - {playlist_track.track.title}")
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if __name__ == "__main__":
|
|
57
|
-
asyncio.run(get_chart(client=YMClient(token="y0_123")))
|
|
39
|
+
или
|
|
40
|
+
```bash
|
|
41
|
+
uv add ymdantic
|
|
58
42
|
```
|
|
43
|
+
|
|
44
|
+
## [Примеры использования](https://github.com/KatantDev/YMdantic/tree/main/examples)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
**YMDantic** - это клиентская библиотека на Python для работы с API Yandex Music.
|
|
5
5
|
Она позволяет получать информацию о треках, альбомах, плейлистах и других объектах сервиса.
|
|
6
6
|
|
|
7
|
-
Особенностью библиотеки является использование [Pydantic](https://
|
|
7
|
+
Особенностью библиотеки является использование [Pydantic](https://docs.pydantic.dev/) для валидации и сериализации данных.
|
|
8
8
|
|
|
9
9
|
## Технологии
|
|
10
10
|
Проект написан на Python и использует следующие библиотеки:
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- Pydantic для валидации и сериализации данных
|
|
14
14
|
|
|
15
15
|
## Установка и использование
|
|
16
|
-
Для установки библиотеки используйте pip/poetry:
|
|
16
|
+
Для установки библиотеки используйте pip/poetry/uv:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
pip install ymdantic
|
|
@@ -22,23 +22,9 @@ pip install ymdantic
|
|
|
22
22
|
```bash
|
|
23
23
|
poetry add ymdantic
|
|
24
24
|
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
```python
|
|
30
|
-
import asyncio
|
|
31
|
-
from ymdantic import YMClient
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
async def get_chart(client: YMClient) -> None:
|
|
35
|
-
chart_block = await client.get_chart()
|
|
36
|
-
print(chart_block.title, end="\n\n")
|
|
37
|
-
print("Треки в чарте:")
|
|
38
|
-
for playlist_track in chart_block.chart.tracks:
|
|
39
|
-
print(f"{playlist_track.track.artists_names} - {playlist_track.track.title}")
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if __name__ == "__main__":
|
|
43
|
-
asyncio.run(get_chart(client=YMClient(token="y0_123")))
|
|
25
|
+
или
|
|
26
|
+
```bash
|
|
27
|
+
uv add ymdantic
|
|
44
28
|
```
|
|
29
|
+
|
|
30
|
+
## [Примеры использования](https://github.com/KatantDev/YMdantic/tree/main/examples)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from ymdantic import YMClient
|
|
5
|
+
|
|
6
|
+
TOKEN = os.environ["TOKEN"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def get_chart() -> None:
|
|
10
|
+
"""Получаем чарт и выводим треки в консоль."""
|
|
11
|
+
client = YMClient(token=TOKEN)
|
|
12
|
+
|
|
13
|
+
chart_block = await client.get_chart()
|
|
14
|
+
print(chart_block.title)
|
|
15
|
+
for index, playlist_track in enumerate(chart_block.chart.tracks, start=1):
|
|
16
|
+
track = playlist_track.track
|
|
17
|
+
if track.available:
|
|
18
|
+
download_info = await track.get_download_info_direct()
|
|
19
|
+
direct_url = download_info[-1].direct_url
|
|
20
|
+
else:
|
|
21
|
+
direct_url = "Недоступен для скачивания"
|
|
22
|
+
print(
|
|
23
|
+
f"{index}. "
|
|
24
|
+
f"{playlist_track.track.artists_names} - {playlist_track.track.title}\n"
|
|
25
|
+
f"({direct_url})\n",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
await client.close()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
asyncio.run(get_chart())
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ymdantic"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "Фреймворк предоставляющий доступ к API Яндекс Музыки (с Pydantic валидацией)"
|
|
5
5
|
authors = [{ name = "KatantDev", email = "vlad26@mail.ru" }]
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.12,<3.15"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"aiohttp>=3.9.0,<4",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from ymdantic import YMClient
|
|
3
|
+
from ymdantic.models.account import SetAccountSettingsParams
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.anyio
|
|
7
|
+
async def test_set_account_settings_direct(client: YMClient) -> None:
|
|
8
|
+
old_settings = await client.get_account_settings()
|
|
9
|
+
|
|
10
|
+
settings = await client.set_account_settings_request(
|
|
11
|
+
SetAccountSettingsParams(
|
|
12
|
+
auto_play_radio=not old_settings.auto_play_radio,
|
|
13
|
+
),
|
|
14
|
+
)
|
|
15
|
+
assert settings.result.auto_play_radio != old_settings.auto_play_radio
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.anyio
|
|
19
|
+
async def test_set_account_settings(client: YMClient) -> None:
|
|
20
|
+
old_settings = await client.get_account_settings()
|
|
21
|
+
|
|
22
|
+
settings = await client.set_account_settings(
|
|
23
|
+
auto_play_radio=not old_settings.auto_play_radio,
|
|
24
|
+
)
|
|
25
|
+
assert settings.auto_play_radio != old_settings.auto_play_radio
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from ymdantic import YMClient
|
|
3
|
+
from ymdantic.models.search.results import (
|
|
4
|
+
AlbumSearchResult,
|
|
5
|
+
ArtistSearchResult,
|
|
6
|
+
TrackSearchResult,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
XOLIDAYBOY_ARTIST_ID = 10878798
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.anyio
|
|
13
|
+
async def test_search_instant_mixed(client: YMClient) -> None:
|
|
14
|
+
response = await client.search_instant_mixed(text="xolidayboy")
|
|
15
|
+
assert response.text == "xolidayboy"
|
|
16
|
+
assert len(response.results) > 0
|
|
17
|
+
assert response.total > 0
|
|
18
|
+
assert response.per_page > 0
|
|
19
|
+
assert isinstance(response.search_request_id, str)
|
|
20
|
+
assert isinstance(response.misspell_corrected, bool)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.anyio
|
|
24
|
+
async def test_search_has_artist_result(client: YMClient) -> None:
|
|
25
|
+
response = await client.search_instant_mixed(text="xolidayboy")
|
|
26
|
+
artist_results = [r for r in response.results if isinstance(r, ArtistSearchResult)]
|
|
27
|
+
assert len(artist_results) > 0, (
|
|
28
|
+
"Expected at least one artist result for 'xolidayboy'"
|
|
29
|
+
)
|
|
30
|
+
artist = artist_results[0].artist
|
|
31
|
+
assert artist.id == XOLIDAYBOY_ARTIST_ID
|
|
32
|
+
assert artist.name.lower() == "xolidayboy"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.mark.anyio
|
|
36
|
+
async def test_search_has_track_results(client: YMClient) -> None:
|
|
37
|
+
response = await client.search_instant_mixed(text="xolidayboy")
|
|
38
|
+
track_results = [r for r in response.results if isinstance(r, TrackSearchResult)]
|
|
39
|
+
assert len(track_results) > 0, "Expected at least one track result for 'xolidayboy'"
|
|
40
|
+
for track_result in track_results:
|
|
41
|
+
track = track_result.track
|
|
42
|
+
assert track.id > 0
|
|
43
|
+
assert track.title
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.mark.anyio
|
|
47
|
+
async def test_search_result_types(client: YMClient) -> None:
|
|
48
|
+
response = await client.search_instant_mixed(text="xolidayboy")
|
|
49
|
+
for result in response.results:
|
|
50
|
+
expected_types = (
|
|
51
|
+
"track",
|
|
52
|
+
"artist",
|
|
53
|
+
"album",
|
|
54
|
+
"podcast_episode",
|
|
55
|
+
"playlist",
|
|
56
|
+
)
|
|
57
|
+
assert result.type in expected_types
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.anyio
|
|
61
|
+
async def test_search_with_filter_artist(client: YMClient) -> None:
|
|
62
|
+
response = await client.search_instant_mixed(
|
|
63
|
+
text="xolidayboy",
|
|
64
|
+
filter="artist",
|
|
65
|
+
)
|
|
66
|
+
assert len(response.results) > 0
|
|
67
|
+
for result in response.results:
|
|
68
|
+
assert isinstance(result, ArtistSearchResult)
|
|
69
|
+
assert result.type == "artist"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.anyio
|
|
73
|
+
async def test_search_with_filter_track(client: YMClient) -> None:
|
|
74
|
+
response = await client.search_instant_mixed(
|
|
75
|
+
text="xolidayboy",
|
|
76
|
+
filter="track",
|
|
77
|
+
)
|
|
78
|
+
assert len(response.results) > 0
|
|
79
|
+
for result in response.results:
|
|
80
|
+
assert isinstance(result, TrackSearchResult)
|
|
81
|
+
assert result.type == "track"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@pytest.mark.anyio
|
|
85
|
+
async def test_search_with_filter_album(client: YMClient) -> None:
|
|
86
|
+
response = await client.search_instant_mixed(
|
|
87
|
+
text="xolidayboy",
|
|
88
|
+
filter="album",
|
|
89
|
+
)
|
|
90
|
+
assert len(response.results) > 0
|
|
91
|
+
for result in response.results:
|
|
92
|
+
assert isinstance(result, AlbumSearchResult)
|
|
93
|
+
assert result.type == "album"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@pytest.mark.anyio
|
|
97
|
+
async def test_search_filters_present(client: YMClient) -> None:
|
|
98
|
+
response = await client.search_instant_mixed(text="xolidayboy")
|
|
99
|
+
assert len(response.filters) > 0
|
|
100
|
+
for f in response.filters:
|
|
101
|
+
assert f.id
|
|
102
|
+
assert f.display_name
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@pytest.mark.anyio
|
|
106
|
+
async def test_search_nocorrect(client: YMClient) -> None:
|
|
107
|
+
response = await client.search_instant_mixed(
|
|
108
|
+
text="xolidayboy",
|
|
109
|
+
nocorrect=True,
|
|
110
|
+
)
|
|
111
|
+
assert response.misspell_corrected is False
|
|
112
|
+
assert response.text == "xolidayboy"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.anyio
|
|
116
|
+
async def test_search_with_page_size(client: YMClient) -> None:
|
|
117
|
+
page_size = 5
|
|
118
|
+
response = await client.search_instant_mixed(
|
|
119
|
+
text="xolidayboy",
|
|
120
|
+
page_size=page_size,
|
|
121
|
+
)
|
|
122
|
+
assert response.per_page == page_size
|
|
123
|
+
assert len(response.results) <= page_size
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@pytest.mark.anyio
|
|
127
|
+
async def test_search_misspelled_query(client: YMClient) -> None:
|
|
128
|
+
response = await client.search_instant_mixed(text="холидейбой")
|
|
129
|
+
assert len(response.results) > 0
|
|
130
|
+
artist_results = [r for r in response.results if isinstance(r, ArtistSearchResult)]
|
|
131
|
+
if artist_results:
|
|
132
|
+
assert any(a.artist.id == XOLIDAYBOY_ARTIST_ID for a in artist_results)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@pytest.mark.anyio
|
|
136
|
+
async def test_search_gibberish_returns_no_results(client: YMClient) -> None:
|
|
137
|
+
response = await client.search_instant_mixed(
|
|
138
|
+
text="zzzqqq999xxx888jjj777",
|
|
139
|
+
)
|
|
140
|
+
assert response.total == 0
|
|
141
|
+
assert len(response.results) == 0
|
|
@@ -3,7 +3,7 @@ import secrets
|
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
5
|
from ymdantic import YMClient
|
|
6
|
-
from ymdantic.exceptions
|
|
6
|
+
from ymdantic.exceptions import YMTrackNotFoundError
|
|
7
7
|
from ymdantic.models import Podcast, Track, UnavailablePodcast, UnavailableTrack
|
|
8
8
|
|
|
9
9
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from ymdantic import YMClient
|
|
3
|
+
from ymdantic.models.tracks.file_info import FileInfo
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.anyio
|
|
7
|
+
async def test_get_available_track_file_info_nq(client: YMClient) -> None:
|
|
8
|
+
track_id = 83313323
|
|
9
|
+
|
|
10
|
+
result = await client.get_track_file_info(track_id=track_id)
|
|
11
|
+
assert isinstance(result, FileInfo)
|
|
12
|
+
assert result.quality == "nq"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.anyio
|
|
16
|
+
async def test_get_available_track_file_info_lossless(client: YMClient) -> None:
|
|
17
|
+
track_id = 117708948
|
|
18
|
+
|
|
19
|
+
result = await client.get_track_file_info(track_id=track_id)
|
|
20
|
+
assert isinstance(result, FileInfo)
|
|
21
|
+
assert result.quality == "lossless"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import secrets
|
|
3
|
-
from typing import TypedDict
|
|
3
|
+
from typing import Type, TypedDict
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
from ymdantic import YMClient
|
|
@@ -14,7 +14,7 @@ class TrackExpected(TypedDict):
|
|
|
14
14
|
|
|
15
15
|
type: str
|
|
16
16
|
available: bool
|
|
17
|
-
instance_type:
|
|
17
|
+
instance_type: Type[BaseTrack]
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@pytest.mark.anyio
|