├── .all-contributorsrc ├── .codespellrc ├── .env.example ├── .github ├── CODE_OF_CONDUCT.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── 1-bug-report.yml │ ├── 2-feature-request.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md ├── labels.toml └── workflows │ ├── ci.yml │ ├── codeql.yml │ ├── hacktoberfest.yml │ ├── issue-manager.yml │ ├── labels.yml │ └── upgrader.yml ├── .gitignore ├── .gitpod.yml ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── .sourcery.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── commitlint.config.mjs ├── conftest.py ├── demo.ipynb ├── docs ├── Makefile ├── _static │ └── .placeholder ├── api_reference │ ├── client.rst │ ├── pagination.rst │ ├── resources.md │ ├── resources │ │ ├── album.rst │ │ ├── artist.rst │ │ ├── chart.rst │ │ ├── editorial.rst │ │ ├── episode.rst │ │ ├── genre.rst │ │ ├── playlist.rst │ │ ├── podcast.rst │ │ ├── radio.rst │ │ ├── track.rst │ │ └── user.rst │ └── toc.rst ├── changelog.md ├── conf.py ├── contributing.md ├── index.md ├── installation.md ├── make.bat ├── pagination.md └── usage.md ├── pyproject.toml ├── renovate.json ├── setup.py ├── src └── deezer │ ├── __init__.py │ ├── auth.py │ ├── client.py │ ├── dates.py │ ├── exceptions.py │ ├── pagination.py │ ├── resources │ ├── __init__.py │ ├── album.py │ ├── artist.py │ ├── chart.py │ ├── editorial.py │ ├── episode.py │ ├── genre.py │ ├── playlist.py │ ├── podcast.py │ ├── radio.py │ ├── resource.py │ ├── track.py │ └── user.py │ └── utils.py ├── templates ├── .changelog-old.md └── CHANGELOG.md.j2 ├── tests ├── __init__.py ├── cassettes │ ├── TestClient.test_add_user_album.yaml │ ├── TestClient.test_add_user_artist.yaml │ ├── TestClient.test_add_user_following.yaml │ ├── TestClient.test_add_user_playlist.yaml │ ├── TestClient.test_add_user_track.yaml │ ├── TestClient.test_advanced_search_complex_with_relation.yaml │ ├── TestClient.test_advanced_search_invalid.yaml │ ├── TestClient.test_create_playlist.yaml │ ├── TestClient.test_delete_playlist.yaml │ ├── TestClient.test_get_album.yaml │ ├── TestClient.test_get_albums_chart.yaml │ ├── TestClient.test_get_artist.yaml │ ├── TestClient.test_get_artists_chart.yaml │ ├── TestClient.test_get_chart.yaml │ ├── TestClient.test_get_current_user.yaml │ ├── TestClient.test_get_editorial.yaml │ ├── TestClient.test_get_episode.yaml │ ├── TestClient.test_get_genre.yaml │ ├── TestClient.test_get_overall_albums_chart.yaml │ ├── TestClient.test_get_overall_artists_chart.yaml │ ├── TestClient.test_get_overall_chart.yaml │ ├── TestClient.test_get_overall_playlists_chart.yaml │ ├── TestClient.test_get_overall_podcasts_chart.yaml │ ├── TestClient.test_get_overall_tracks_chart.yaml │ ├── TestClient.test_get_playlist.yaml │ ├── TestClient.test_get_playlists_chart.yaml │ ├── TestClient.test_get_podcast.yaml │ ├── TestClient.test_get_podcasts_chart.yaml │ ├── TestClient.test_get_radio.yaml │ ├── TestClient.test_get_radios_top.yaml │ ├── TestClient.test_get_track.yaml │ ├── TestClient.test_get_tracks_chart.yaml │ ├── TestClient.test_get_user.yaml │ ├── TestClient.test_get_user_albums[args0].yaml │ ├── TestClient.test_get_user_albums[args1].yaml │ ├── TestClient.test_get_user_artists[args0].yaml │ ├── TestClient.test_get_user_artists[args1].yaml │ ├── TestClient.test_get_user_flow.yaml │ ├── TestClient.test_get_user_followers[args0].yaml │ ├── TestClient.test_get_user_followers[args1].yaml │ ├── TestClient.test_get_user_followings[args0].yaml │ ├── TestClient.test_get_user_followings[args1].yaml │ ├── TestClient.test_get_user_history.yaml │ ├── TestClient.test_get_user_recommended_albums.yaml │ ├── TestClient.test_get_user_recommended_artists.yaml │ ├── TestClient.test_get_user_recommended_playlists.yaml │ ├── TestClient.test_get_user_recommended_tracks.yaml │ ├── TestClient.test_get_user_tracks[args0].yaml │ ├── TestClient.test_get_user_tracks[args1].yaml │ ├── TestClient.test_list_editorials.yaml │ ├── TestClient.test_list_genres.yaml │ ├── TestClient.test_list_radios.yaml │ ├── TestClient.test_no_album_raise.yaml │ ├── TestClient.test_no_artist_raise.yaml │ ├── TestClient.test_no_editorial_raise.yaml │ ├── TestClient.test_no_episode_raise.yaml │ ├── TestClient.test_no_genre_raise.yaml │ ├── TestClient.test_no_playlist_raise.yaml │ ├── TestClient.test_no_podcast_raise.yaml │ ├── TestClient.test_no_radio_raise.yaml │ ├── TestClient.test_no_track_raise.yaml │ ├── TestClient.test_no_user_raise.yaml │ ├── TestClient.test_remove_user_album.yaml │ ├── TestClient.test_remove_user_artist.yaml │ ├── TestClient.test_remove_user_following.yaml │ ├── TestClient.test_remove_user_playlist.yaml │ ├── TestClient.test_remove_user_track.yaml │ ├── TestClient.test_request_404.yaml │ ├── TestClient.test_request_unknown_resource.yaml │ ├── TestClient.test_search_advanced_multiple.yaml │ ├── TestClient.test_search_advanced_simple.yaml │ ├── TestClient.test_search_albums.yaml │ ├── TestClient.test_search_artists.yaml │ ├── TestClient.test_search_pagination[params0-2].yaml │ ├── TestClient.test_search_pagination[params1-3].yaml │ ├── TestClient.test_search_pagination[params2-4].yaml │ ├── TestClient.test_search_playlists.yaml │ ├── TestClient.test_search_results_ordering[ALBUM_ASC].yaml │ ├── TestClient.test_search_results_ordering[ALBUM_DESC].yaml │ ├── TestClient.test_search_results_ordering[ARTIST_ASC].yaml │ ├── TestClient.test_search_results_ordering[ARTIST_DESC].yaml │ ├── TestClient.test_search_results_ordering[DURATION_ASC].yaml │ ├── TestClient.test_search_results_ordering[DURATION_DESC].yaml │ ├── TestClient.test_search_results_ordering[RANKING].yaml │ ├── TestClient.test_search_results_ordering[RATING_ASC].yaml │ ├── TestClient.test_search_results_ordering[RATING_DESC].yaml │ ├── TestClient.test_search_results_ordering[TRACK_ASC].yaml │ ├── TestClient.test_search_results_ordering[TRACK_DESC].yaml │ ├── TestClient.test_search_simple.yaml │ ├── TestClient.test_search_strict.yaml │ ├── TestClient.test_with_language_header[fr].yaml │ ├── TestClient.test_with_language_header[ja].yaml │ ├── TestPaginatedList.test_authenticated_requests.yaml │ ├── TestPaginatedList.test_get_element[30-One More Time].yaml │ ├── TestPaginatedList.test_get_element[4-Alive 2007].yaml │ ├── TestPaginatedList.test_get_element_index_error.yaml │ ├── TestPaginatedList.test_iterate.yaml │ ├── TestPaginatedList.test_iterator.yaml │ ├── TestPaginatedList.test_repr_empty.yaml │ ├── TestPaginatedList.test_repr_little_results.yaml │ ├── TestPaginatedList.test_repr_many_results.yaml │ ├── TestPaginatedList.test_slicing_no_end.yaml │ ├── TestPaginatedList.test_slicing_no_start.yaml │ ├── TestPaginatedList.test_slicing_simple.yaml │ ├── TestPaginatedList.test_slicing_with_step.yaml │ └── TestPaginatedList.test_total.yaml ├── resources │ ├── __init__.py │ ├── cassettes │ │ ├── TestAlbum.test_as_dict.yaml │ │ ├── TestAlbum.test_basic.yaml │ │ ├── TestAlbum.test_contributors.yaml │ │ ├── TestAlbum.test_get_tracks.yaml │ │ ├── TestArtist.test_attributes.yaml │ │ ├── TestArtist.test_get_albums.yaml │ │ ├── TestArtist.test_get_playlists.yaml │ │ ├── TestArtist.test_get_radio.yaml │ │ ├── TestArtist.test_get_related.yaml │ │ ├── TestArtist.test_get_top.yaml │ │ ├── TestChart.test_get_albums.yaml │ │ ├── TestChart.test_get_artists.yaml │ │ ├── TestChart.test_get_playlists.yaml │ │ ├── TestChart.test_get_podcasts.yaml │ │ ├── TestChart.test_get_tracks.yaml │ │ ├── TestEditorial.test_attributes.yaml │ │ ├── TestEditorial.test_get_chart.yaml │ │ ├── TestEditorial.test_get_releases.yaml │ │ ├── TestEditorial.test_get_selection.yaml │ │ ├── TestEpisode.test_access_non_inferable_field.yaml │ │ ├── TestEpisode.test_add_bookmark.yaml │ │ ├── TestEpisode.test_as_dict.yaml │ │ ├── TestEpisode.test_get_episode.yaml │ │ ├── TestEpisode.test_remove_bookmark.yaml │ │ ├── TestGenre.test_attributes.yaml │ │ ├── TestGenre.test_get_artists.yaml │ │ ├── TestGenre.test_get_podcasts.yaml │ │ ├── TestGenre.test_get_radios.yaml │ │ ├── TestPlaylist.test_add_tracks.yaml │ │ ├── TestPlaylist.test_attributes.yaml │ │ ├── TestPlaylist.test_delete_tracks.yaml │ │ ├── TestPlaylist.test_get_fans.yaml │ │ ├── TestPlaylist.test_get_tracks.yaml │ │ ├── TestPlaylist.test_mark_seen.yaml │ │ ├── TestPlaylist.test_reorder_tracks.yaml │ │ ├── TestPodcast.test_get_episodes.yaml │ │ ├── TestRadio.test_attributes.yaml │ │ ├── TestRadio.test_get_tracks.yaml │ │ ├── TestResource.test_access_no_infinite_fetch.yaml │ │ ├── TestResource.test_access_non_inferable_field_simplified_objet.yaml │ │ ├── TestResource.test_resource_relation.yaml │ │ ├── TestTrack.test_contributors.yaml │ │ ├── TestTrack.test_track_attributes.yaml │ │ ├── TestUser.test_add_album_by_id.yaml │ │ ├── TestUser.test_add_album_obj.yaml │ │ ├── TestUser.test_add_artist_id.yaml │ │ ├── TestUser.test_add_artist_obj.yaml │ │ ├── TestUser.test_add_playlist_by_id.yaml │ │ ├── TestUser.test_add_playlist_failed.yaml │ │ ├── TestUser.test_add_playlist_obj.yaml │ │ ├── TestUser.test_add_track_id.yaml │ │ ├── TestUser.test_add_track_obj.yaml │ │ ├── TestUser.test_create_playlist.yaml │ │ ├── TestUser.test_follow_by_id_fail.yaml │ │ ├── TestUser.test_follow_by_id_ok.yaml │ │ ├── TestUser.test_follow_obj.yaml │ │ ├── TestUser.test_get_albums.yaml │ │ ├── TestUser.test_get_artists.yaml │ │ ├── TestUser.test_get_followers.yaml │ │ ├── TestUser.test_get_followings.yaml │ │ ├── TestUser.test_get_playlists.yaml │ │ ├── TestUser.test_get_tracks.yaml │ │ ├── TestUser.test_remove_album_by_id.yaml │ │ ├── TestUser.test_remove_album_obj.yaml │ │ ├── TestUser.test_remove_artist_id.yaml │ │ ├── TestUser.test_remove_artist_obj.yaml │ │ ├── TestUser.test_remove_playlist_by_id.yaml │ │ ├── TestUser.test_remove_playlist_obj.yaml │ │ ├── TestUser.test_remove_track_id.yaml │ │ ├── TestUser.test_remove_track_obj.yaml │ │ ├── TestUser.test_unfollow_by_id.yaml │ │ └── TestUser.test_unfollow_obj.yaml │ ├── test_album.py │ ├── test_artist.py │ ├── test_chart.py │ ├── test_editorial.py │ ├── test_episode.py │ ├── test_genre.py │ ├── test_playlist.py │ ├── test_podcast.py │ ├── test_radio.py │ ├── test_resource.py │ ├── test_track.py │ └── test_user.py ├── test_client.py ├── test_exceptions.py └── test_pagination.py └── uv.lock /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = */cassettes/* 3 | ignore-words-list = followings,lazer,socio-economic 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | API_TOKEN= 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [browniebroke] 4 | patreon: browniebroke 5 | polar: browniebroke 6 | ko_fi: browniebroke 7 | buy_me_a_coffee: browniebroke 8 | custom: ["https://paypal.me/browneibroke"] 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a report to help us improve 3 | labels: [bug] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Describe the bug 9 | description: A clear and concise description of what the bug is. 10 | placeholder: Describe the bug 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: reproduce 15 | attributes: 16 | label: To Reproduce 17 | description: Steps to reproduce the behavior. 18 | placeholder: To Reproduce 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: context 23 | attributes: 24 | label: Additional context 25 | description: Add any other context about the problem here. 26 | placeholder: Additional context 27 | - type: input 28 | id: version 29 | attributes: 30 | label: Version 31 | description: Version of the project. 32 | placeholder: Version 33 | validations: 34 | required: true 35 | - type: input 36 | id: platform 37 | attributes: 38 | label: Platform 39 | description: Platform where the bug was found. 40 | placeholder: "Example: Windows 11 / macOS 12.0.1 / Ubuntu 20.04" 41 | validations: 42 | required: true 43 | - type: checkboxes 44 | id: terms 45 | attributes: 46 | label: Code of Conduct 47 | description: By submitting this issue, you agree to follow our 48 | [Code of Conduct](https://github.com/browniebroke/deezer-python/blob/main/.github/CODE_OF_CONDUCT.md). 49 | options: 50 | - label: I agree to follow this project's Code of Conduct. 51 | required: true 52 | - type: checkboxes 53 | id: no-duplicate 54 | attributes: 55 | label: No Duplicate 56 | description: Please check [existing issues](https://github.com/browniebroke/deezer-python/issues) to avoid duplicates. 57 | options: 58 | - label: I have checked existing issues to avoid duplicates. 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: 👋 Have a great day and thank you for the bug report! 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | labels: [enhancement] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Is your feature request related to a problem? Please describe. 9 | description: A clear and concise description of what the problem is. 10 | value: I'm always frustrated when 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: solution 15 | attributes: 16 | label: Describe alternatives you've considered 17 | description: A clear and concise description of any alternative solutions or features you've considered. 18 | placeholder: Describe alternatives you've considered 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: context 23 | attributes: 24 | label: Additional context 25 | description: Add any other context or screenshots about the feature request here. 26 | placeholder: Additional context 27 | - type: checkboxes 28 | id: terms 29 | attributes: 30 | label: Code of Conduct 31 | description: By submitting this issue, you agree to follow our 32 | [Code of Conduct](https://github.com/browniebroke/deezer-python/blob/main/.github/CODE_OF_CONDUCT.md). 33 | options: 34 | - label: I agree to follow this project's Code of Conduct 35 | required: true 36 | - type: checkboxes 37 | id: willing 38 | attributes: 39 | label: Are you willing to resolve this issue by submitting a Pull Request? 40 | description: Remember that first-time contributors are welcome! 🙌 41 | options: 42 | - label: Yes, I have the time, and I know how to start. 43 | - label: Yes, I have the time, but I don't know how to start. I would need guidance. 44 | - label: No, I don't have the time, although I believe I could do it if I had the time... 45 | - label: No, I don't have the time and I wouldn't even know how to start. 46 | - type: markdown 47 | attributes: 48 | value: 👋 Have a great day and thank you for the feature request! 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # Disabling blank issues to ensure all necessary information is provided 2 | # Users should use the provided templates for specific issues 3 | # For general questions, please refer to the contact links section 4 | blank_issues_enabled: false 5 | contact_links: 6 | - name: Questions 7 | url: https://github.com/browniebroke/deezer-python/discussions/categories/q-a 8 | about: Please ask and answer questions here. 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ### Description of change 11 | 12 | 25 | 26 | ### Pull-Request Checklist 27 | 28 | 35 | 36 | - [ ] Code is up-to-date with the `main` branch 37 | - [ ] This pull request follows the [contributing guidelines](https://github.com/browniebroke/deezer-python/blob/main/CONTRIBUTING.md). 38 | - [ ] This pull request links relevant issues as `Fixes #0000` (replace `0000` with the actual issue number) 39 | - [ ] There are new or updated unit tests validating the change 40 | - [ ] Documentation has been updated to reflect this change 41 | - [ ] The new commits follow conventions outlined in the [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/), such as "fix(api): prevent racing of requests". 42 | 43 | > - If pre-commit.ci is failing, try `pre-commit run -a` for further information. 44 | > - If CI / test is failing, try `poetry run pytest` for further information. 45 | 46 | 49 | -------------------------------------------------------------------------------- /.github/labels.toml: -------------------------------------------------------------------------------- 1 | [breaking] 2 | color = "ffcc00" 3 | name = "breaking" 4 | description = "Breaking change." 5 | 6 | [bug] 7 | color = "d73a4a" 8 | name = "bug" 9 | description = "Something isn't working" 10 | 11 | [dependencies] 12 | color = "0366d6" 13 | name = "dependencies" 14 | description = "Pull requests that update a dependency file" 15 | 16 | [github_actions] 17 | color = "000000" 18 | name = "github_actions" 19 | description = "Update of github actions" 20 | 21 | [documentation] 22 | color = "1bc4a5" 23 | name = "documentation" 24 | description = "Improvements or additions to documentation" 25 | 26 | [duplicate] 27 | color = "cfd3d7" 28 | name = "duplicate" 29 | description = "This issue or pull request already exists" 30 | 31 | [enhancement] 32 | color = "a2eeef" 33 | name = "enhancement" 34 | description = "New feature or request" 35 | 36 | ["good first issue"] 37 | color = "7057ff" 38 | name = "good first issue" 39 | description = "Good for newcomers" 40 | 41 | ["help wanted"] 42 | color = "008672" 43 | name = "help wanted" 44 | description = "Extra attention is needed" 45 | 46 | [invalid] 47 | color = "e4e669" 48 | name = "invalid" 49 | description = "This doesn't seem right" 50 | 51 | [nochangelog] 52 | color = "555555" 53 | name = "nochangelog" 54 | description = "Exclude pull requests from changelog" 55 | 56 | [question] 57 | color = "d876e3" 58 | name = "question" 59 | description = "Further information is requested" 60 | 61 | [removed] 62 | color = "e99695" 63 | name = "removed" 64 | description = "Removed piece of functionalities." 65 | 66 | [tests] 67 | color = "bfd4f2" 68 | name = "tests" 69 | description = "CI, CD and testing related changes" 70 | 71 | [wontfix] 72 | color = "ffffff" 73 | name = "wontfix" 74 | description = "This will not be worked on" 75 | 76 | [discussion] 77 | color = "c2e0c6" 78 | name = "discussion" 79 | description = "Some discussion around the project" 80 | 81 | [hacktoberfest] 82 | color = "ffa663" 83 | name = "hacktoberfest" 84 | description = "Good issues for Hacktoberfest" 85 | 86 | [answered] 87 | color = "0ee2b6" 88 | name = "answered" 89 | description = "Automatically closes as answered after a delay" 90 | 91 | [waiting] 92 | color = "5f7972" 93 | name = "waiting" 94 | description = "Automatically closes if no answer after a delay" 95 | 96 | [fund] 97 | color = "0E8A16" 98 | name = "fund" 99 | description = "Add a section linking to polar.sh for funding the issue." 100 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - stable/* 8 | pull_request: 9 | 10 | concurrency: 11 | group: ${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | python-version: 21 | - "3.9" 22 | - "3.10" 23 | - "3.11" 24 | - "3.12" 25 | - "3.13" 26 | os: 27 | - ubuntu-latest 28 | - windows-latest 29 | # - macOS-latest 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/setup-python@v5 34 | with: 35 | python-version: ${{ matrix.python-version }} 36 | - uses: astral-sh/setup-uv@v6 37 | - run: uv sync --no-python-downloads 38 | shell: bash 39 | - run: uv run pytest --cov-report=xml 40 | shell: bash 41 | - uses: codecov/codecov-action@v5 42 | with: 43 | token: ${{ secrets.CODECOV_TOKEN }} 44 | 45 | commitlint: 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v4 49 | with: 50 | fetch-depth: 0 51 | - uses: wagoid/commitlint-github-action@v6.2.1 52 | 53 | release: 54 | needs: 55 | - test 56 | - commitlint 57 | 58 | runs-on: ubuntu-latest 59 | environment: release 60 | concurrency: release 61 | permissions: 62 | id-token: write 63 | attestations: write 64 | contents: write 65 | 66 | steps: 67 | - uses: actions/checkout@v4 68 | with: 69 | fetch-depth: 0 70 | ref: ${{ github.sha }} 71 | 72 | - name: Checkout commit for release 73 | run: | 74 | git checkout -B ${{ github.ref_name }} ${{ github.sha }} 75 | 76 | # Do a dry run of PSR 77 | - name: Test release 78 | uses: python-semantic-release/python-semantic-release@v10 79 | if: ${{ github.ref_name != 'main' && !startsWith(github.ref_name, 'stable/') }} 80 | with: 81 | no_operation_mode: true 82 | 83 | # On main/stable branch: actual PSR + upload to PyPI & GitHub 84 | - name: Release 85 | uses: python-semantic-release/python-semantic-release@v10 86 | id: release 87 | if: ${{ github.ref_name == 'main' || startsWith(github.ref_name, 'stable/') }} 88 | with: 89 | github_token: ${{ secrets.GITHUB_TOKEN }} 90 | 91 | - name: Attest build provenance 92 | uses: actions/attest-build-provenance@v2 93 | if: steps.release.outputs.released == 'true' 94 | with: 95 | subject-path: "dist/*" 96 | 97 | - name: Publish package distributions to PyPI 98 | uses: pypa/gh-action-pypi-publish@release/v1 99 | if: steps.release.outputs.released == 'true' 100 | - name: Publish package distributions to GitHub Releases 101 | uses: python-semantic-release/publish-action@v10 102 | if: steps.release.outputs.released == 'true' 103 | with: 104 | github_token: ${{ secrets.GITHUB_TOKEN }} 105 | tag: ${{ steps.release.outputs.tag }} 106 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | schedule: 9 | - cron: "15 2 * * 3" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [python] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v3 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v3 40 | with: 41 | category: "/language:${{ matrix.language }}" 42 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | # Run every day in October 7 | - cron: "0 0 * 10 *" 8 | # Run on the 1st of November to revert 9 | - cron: "0 13 1 11 *" 10 | 11 | jobs: 12 | label: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: browniebroke/hacktoberfest-labeler-action@v2.3.0 17 | with: 18 | github_token: ${{ secrets.CPR_GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/issue-manager.yml: -------------------------------------------------------------------------------- 1 | name: Issue Manager 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | issue_comment: 7 | types: 8 | - created 9 | issues: 10 | types: 11 | - labeled 12 | pull_request_target: 13 | types: 14 | - labeled 15 | workflow_dispatch: 16 | 17 | jobs: 18 | issue-manager: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: tiangolo/issue-manager@0.5.1 22 | with: 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | config: > 25 | { 26 | "answered": { 27 | "message": "Assuming the original issue was solved, it will be automatically closed now." 28 | }, 29 | "waiting": { 30 | "message": "Automatically closing. To re-open, please provide the additional information requested." 31 | }, 32 | "wontfix": { 33 | "message": "This is considered out of scope or not worth doing." 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | name: Sync Github labels 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - ".github/**" 9 | 10 | jobs: 11 | labels: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: 3.x 19 | - name: Install labels 20 | run: pip install labels 21 | - name: Sync config with Github 22 | run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GITHUB_TOKEN }} sync -f .github/labels.toml 23 | -------------------------------------------------------------------------------- /.github/workflows/upgrader.yml: -------------------------------------------------------------------------------- 1 | name: Upgrader 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "26 21 10 1-9,11-12 *" 7 | 8 | jobs: 9 | upgrade: 10 | uses: browniebroke/github-actions/.github/workflows/uv-upgrade.yml@v1 11 | secrets: 12 | gh_pat: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | .coverage 4 | .tox 5 | 6 | build 7 | dist 8 | 9 | # Sphinx documentation 10 | docs/_build/ 11 | 12 | #Virtualenvs 13 | ENV* 14 | 15 | # Coverage 16 | .coveralls.yml 17 | htmlcov 18 | 19 | # Node stuff from all-contributors 20 | node_modules 21 | package.json 22 | yarn.lock 23 | 24 | # For secrets 25 | .env 26 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - command: | 3 | pip install uv 4 | PIP_USER=false uv sync 5 | - command: | 6 | pip install pre-commit 7 | pre-commit install 8 | PIP_USER=false pre-commit install-hooks 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | exclude: "CHANGELOG.md|tests/cassettes/|.all-contributorsrc" 4 | default_stages: [pre-commit] 5 | 6 | ci: 7 | autofix_commit_msg: "chore(pre-commit.ci): auto fixes" 8 | autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate" 9 | 10 | repos: 11 | - repo: https://github.com/pre-commit/pre-commit-hooks 12 | rev: v5.0.0 13 | hooks: 14 | - id: debug-statements 15 | - id: check-builtin-literals 16 | - id: check-case-conflict 17 | - id: check-docstring-first 18 | - id: check-json 19 | - id: check-toml 20 | - id: check-xml 21 | - id: check-yaml 22 | - id: detect-private-key 23 | - id: end-of-file-fixer 24 | - id: trailing-whitespace 25 | - repo: https://github.com/tox-dev/pyproject-fmt 26 | rev: "v2.6.0" 27 | hooks: 28 | - id: pyproject-fmt 29 | - repo: https://github.com/astral-sh/uv-pre-commit 30 | rev: 0.7.11 31 | hooks: 32 | - id: uv-lock 33 | - repo: https://github.com/pre-commit/mirrors-prettier 34 | rev: v3.1.0 35 | hooks: 36 | - id: prettier 37 | args: ["--tab-width", "2"] 38 | - repo: https://github.com/astral-sh/ruff-pre-commit 39 | rev: v0.11.13 40 | hooks: 41 | - id: ruff 42 | args: [--fix, --exit-non-zero-on-fix] 43 | - id: ruff-format 44 | - repo: https://github.com/codespell-project/codespell 45 | rev: v2.4.1 46 | hooks: 47 | - id: codespell 48 | - repo: https://github.com/commitizen-tools/commitizen 49 | rev: v4.8.2 50 | hooks: 51 | - id: commitizen 52 | stages: [commit-msg] 53 | - repo: https://github.com/pre-commit/mirrors-mypy 54 | rev: v1.16.0 55 | hooks: 56 | - id: mypy 57 | additional_dependencies: 58 | - types-requests 59 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the version of Python and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | commands: 13 | - asdf plugin add uv 14 | - asdf install uv latest 15 | - asdf global uv latest 16 | - uv sync --only-group docs --frozen 17 | - uv run -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html 18 | 19 | # Build documentation in the docs directory with Sphinx 20 | sphinx: 21 | configuration: docs/conf.py 22 | -------------------------------------------------------------------------------- /.sourcery.yaml: -------------------------------------------------------------------------------- 1 | rule_settings: 2 | python_version: "3.8" 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Bruno Alla 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include deezer *.py 2 | include *.txt 3 | include *.rst 4 | include *.md 5 | recursive-include deezer *.json 6 | recursive-include docs *.bat 7 | recursive-include docs *.placeholder 8 | recursive-include docs *.py 9 | recursive-include docs *.rst 10 | recursive-include docs *.md 11 | recursive-include docs Makefile 12 | -------------------------------------------------------------------------------- /commitlint.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: ["@commitlint/config-conventional"], 3 | rules: { 4 | "header-max-length": [0, "always", Infinity], 5 | "body-max-line-length": [0, "always", Infinity], 6 | "footer-max-line-length": [0, "always", Infinity], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from environs import Env 3 | 4 | import deezer 5 | 6 | env = Env() 7 | env.read_env() 8 | 9 | 10 | @pytest.fixture() 11 | def client(): 12 | """Create an unauthenticated client for tests.""" 13 | with deezer.Client( 14 | # This is to get human-readable response output in VCR cassettes 15 | headers={"Accept-Encoding": "identity"}, 16 | ) as client: 17 | yield client 18 | 19 | 20 | @pytest.fixture() 21 | def client_token(): 22 | """Create an authenticated client for tests.""" 23 | with deezer.Client( 24 | access_token=env("API_TOKEN", "dummy"), 25 | headers={"Accept-Encoding": "identity"}, 26 | ) as client: 27 | yield client 28 | 29 | 30 | @pytest.fixture(scope="module", autouse=True) 31 | def vcr_config(): 32 | """Clean up some headers from cassettes.""" 33 | return { 34 | "filter_query_parameters": [("access_token", "dummy")], 35 | "before_record_response": _clean_response, 36 | } 37 | 38 | 39 | def _clean_response(response): 40 | """Remove some info from the response before writing cassettes.""" 41 | remove_headers = {"Set-Cookie", "Date", "P3P"} 42 | if isinstance(response["headers"], dict): 43 | # Normal client stores headers as dict 44 | for header_name in remove_headers: 45 | response["headers"].pop(header_name, None) 46 | elif isinstance(response["headers"], list): 47 | # Tornado client stores headers as a list of 2-tuples 48 | response["headers"] = [(name, value) for name, value in response["headers"] if name not in remove_headers] 49 | return response 50 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/browniebroke/deezer-python/fc941a3fbcdb3033aeadc814702ec7dcfe745d7e/docs/_static/.placeholder -------------------------------------------------------------------------------- /docs/api_reference/client.rst: -------------------------------------------------------------------------------- 1 | Client 2 | ------ 3 | 4 | The client class is the main entry point to start querying the `Deezer API `_ 5 | 6 | .. autoclass:: deezer.Client 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/api_reference/pagination.rst: -------------------------------------------------------------------------------- 1 | .. _pagination-reference: 2 | 3 | Pagination 4 | ---------- 5 | 6 | .. autoclass:: deezer.PaginatedList 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/api_reference/resources.md: -------------------------------------------------------------------------------- 1 | (resources-reference)= 2 | 3 | # Resources 4 | 5 | A collection of Python classes modelling each type of content that is returned by the Deezer API. 6 | 7 | ```{toctree} 8 | :maxdepth: 1 9 | 10 | resources/album 11 | resources/artist 12 | resources/chart 13 | resources/editorial 14 | resources/episode 15 | resources/genre 16 | resources/playlist 17 | resources/podcast 18 | resources/radio 19 | resources/track 20 | resources/user 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/api_reference/resources/album.rst: -------------------------------------------------------------------------------- 1 | Album 2 | ----- 3 | 4 | .. autoclass:: deezer.Album 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/artist.rst: -------------------------------------------------------------------------------- 1 | Artist 2 | ------ 3 | 4 | .. autoclass:: deezer.Artist 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/chart.rst: -------------------------------------------------------------------------------- 1 | Chart 2 | ----- 3 | 4 | .. autoclass:: deezer.Chart 5 | :members: 6 | :undoc-members: 7 | :exclude-members: id, type 8 | -------------------------------------------------------------------------------- /docs/api_reference/resources/editorial.rst: -------------------------------------------------------------------------------- 1 | Editorial 2 | --------- 3 | 4 | .. autoclass:: deezer.Editorial 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/episode.rst: -------------------------------------------------------------------------------- 1 | Episode 2 | ------- 3 | 4 | .. autoclass:: deezer.Episode 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/genre.rst: -------------------------------------------------------------------------------- 1 | Genre 2 | ----- 3 | 4 | .. autoclass:: deezer.Genre 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/playlist.rst: -------------------------------------------------------------------------------- 1 | Playlist 2 | -------- 3 | 4 | .. autoclass:: deezer.Playlist 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/podcast.rst: -------------------------------------------------------------------------------- 1 | Podcast 2 | ------- 3 | 4 | .. autoclass:: deezer.Podcast 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/radio.rst: -------------------------------------------------------------------------------- 1 | Radio 2 | ----- 3 | 4 | .. autoclass:: deezer.Radio 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/track.rst: -------------------------------------------------------------------------------- 1 | Track 2 | ----- 3 | 4 | .. autoclass:: deezer.Track 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/resources/user.rst: -------------------------------------------------------------------------------- 1 | User 2 | ---- 3 | 4 | .. autoclass:: deezer.User 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/api_reference/toc.rst: -------------------------------------------------------------------------------- 1 | Reference 2 | ========= 3 | 4 | This is the auto-generated documentation for all the main modules in the library. 5 | 6 | :Release: |version| 7 | :Date: |today| 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | client 13 | pagination 14 | resources 15 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | ```{include} ../CHANGELOG.md 2 | 3 | ``` 4 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | ```{include} ../CONTRIBUTING.md 2 | 3 | ``` 4 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Deezer Python documentation 2 | 3 | Deezer-Python: a friendly wrapper around the [Deezer API](http://developers.deezer.com/api). 4 | 5 | --- 6 | 7 | **Source Code**: https://github.com/browniebroke/deezer-python 8 | 9 | --- 10 | 11 | ```{toctree} 12 | :caption: Installation & Usage 13 | :maxdepth: 2 14 | 15 | installation 16 | usage 17 | pagination 18 | ``` 19 | 20 | ```{toctree} 21 | :caption: API Reference 22 | :maxdepth: 3 23 | 24 | api_reference/toc 25 | ``` 26 | 27 | ```{toctree} 28 | :caption: Project Info 29 | :maxdepth: 2 30 | 31 | changelog 32 | contributing 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | The package is published on [PyPI](https://pypi.org/project/deezer-python/) and can be installed with `pip` (or any equivalent): 4 | 5 | ```bash 6 | pip install deezer-python 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/pagination.md: -------------------------------------------------------------------------------- 1 | (pagination-guide)= 2 | 3 | # Pagination 4 | 5 | For endpoints returning a paginated response, the list of items are wrapped in a {class}`PaginatedList ` class which makes working with pagination more Pythonic while doing the necessary API calls transparently. 6 | 7 | ## Iterating over elements 8 | 9 | The class is an iterator, meaning that you can go through instances in the list with a for loop, or by calling `next()` to get the following item: 10 | 11 | ```python 12 | artist_albums = artist.get_albums() 13 | 14 | # Iterable style 15 | for album in artist_albums: 16 | print(album.title) 17 | 18 | # Iterator 19 | album_1 = next(artist_albums) 20 | album_2 = next(artist_albums) 21 | album_3 = next(artist_albums) 22 | ``` 23 | 24 | This will take care or fetching extra pages if needed. Once all the elements have been fetched, no further network calls will happen. This will work if you iterate over the same paginated response again: 25 | 26 | ```python 27 | # No API calls: artist_albums is reused from above 28 | for album in artist_albums: 29 | print(album.title) 30 | ``` 31 | 32 | However, API calls would be repeated if you get a fresh paginated response again: 33 | 34 | ```python 35 | # New API calls: artist.get_albums() returns a fresh paginated list 36 | for album in artist.get_albums(): 37 | print(album.title) 38 | ``` 39 | 40 | Be mindful of that when writing your code otherwise you'll consume your API quota quickly! 41 | 42 | ## Total number 43 | 44 | If you want to know the total number of items in the list, you can either use the `total` property, which is mirroring what's returned by Deezer, or use the more Pythonic `len()` built-in: 45 | 46 | ```python 47 | # total property 48 | print(artist_albums.total) 49 | # with len() built-in 50 | len(artist_albums) 51 | ``` 52 | 53 | ## Indexing 54 | 55 | You can also access elements by index: 56 | 57 | ```python 58 | second_album = artist_albums[1] 59 | ``` 60 | 61 | Beware that accessing a large index may produce some extra network calls to the Deezer API as pages preceding the given index will be fetched. For example, assuming the page size is 25, this will perform 5 API calls: 62 | 63 | ```python 64 | artist_albums[110] 65 | ``` 66 | 67 | In case the index is too big, an `IndexError` will be raised, as if it were a list. Unlike list, this feature doesn't support negative values at the time. 68 | 69 | ## Slicing 70 | 71 | Slicing is also supported, you may take a slice with or without a start or end, and also provide a custom step number, like with a regular list: 72 | 73 | ```python 74 | # With start & end 75 | artist_albums[2:5] 76 | 77 | # Without start 78 | artist_albums[:5] 79 | 80 | # Without end 81 | artist_albums[5:] 82 | 83 | # With start, end & step 84 | artist_albums[2:10:2] 85 | ``` 86 | 87 | As with the rest, not providing an end, or providing a large value as end may produce extra network calls to the Deezer API. Like indexing, negative values aren't supported at the moment. 88 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["github>browniebroke/renovate-configs:python"], 3 | "packageRules": [ 4 | { 5 | "packageNames": ["httpx"], 6 | "semanticCommitType": "chore" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This is a shim to allow GitHub to detect the package, build is done with poetry 4 | # Taken from https://github.com/Textualize/rich 5 | 6 | import setuptools 7 | 8 | if __name__ == "__main__": 9 | setuptools.setup(name="deezer-python") 10 | -------------------------------------------------------------------------------- /src/deezer/__init__.py: -------------------------------------------------------------------------------- 1 | from deezer.client import Client 2 | from deezer.pagination import PaginatedList 3 | from deezer.resources import ( 4 | Album, 5 | Artist, 6 | Chart, 7 | Editorial, 8 | Episode, 9 | Genre, 10 | Playlist, 11 | Podcast, 12 | Radio, 13 | Resource, 14 | Track, 15 | User, 16 | ) 17 | 18 | __version__ = "7.1.1" 19 | __all__ = [ 20 | "Album", 21 | "Artist", 22 | "Chart", 23 | "Client", 24 | "Editorial", 25 | "Episode", 26 | "Genre", 27 | "PaginatedList", 28 | "Playlist", 29 | "Podcast", 30 | "Radio", 31 | "Resource", 32 | "Track", 33 | "User", 34 | ] 35 | 36 | USER_AGENT = f"Deezer Python API Wrapper v{__version__}" 37 | -------------------------------------------------------------------------------- /src/deezer/auth.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | import httpx 4 | 5 | 6 | class DeezerQueryAuth(httpx.Auth): 7 | """ 8 | Deezer Auth for httpx. 9 | 10 | Attach given access token to the request, 11 | by passing it as query parameter. 12 | 13 | :param access_token: Your Deezer access token. 14 | """ 15 | 16 | def __init__(self, access_token: str): 17 | self.access_token = access_token 18 | 19 | def auth_flow(self, request: httpx.Request) -> typing.Generator[httpx.Request, None, None]: 20 | """ 21 | Add authentication query parameter to the request. 22 | 23 | :param request: httpx.Request 24 | :return: httpx.Request 25 | """ 26 | request.url = request.url.copy_merge_params({"access_token": self.access_token}) 27 | yield request 28 | -------------------------------------------------------------------------------- /src/deezer/dates.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime as dt 4 | 5 | DATE_FORMAT = "%Y-%m-%d" 6 | DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" 7 | 8 | 9 | def parse_date(date_str: str) -> dt.date | None: 10 | """Parse a date from a string to a date object.""" 11 | datetime = parse_datetime(date_str, DATE_FORMAT) 12 | return datetime.date() if datetime else None 13 | 14 | 15 | def parse_datetime( 16 | datetime_str: str, 17 | date_format: str = DATETIME_FORMAT, 18 | ) -> dt.datetime | None: 19 | """Parse a datetime from a string to a datetime object.""" 20 | if not datetime_str or datetime_str.startswith("0000-00-00"): 21 | return None 22 | return dt.datetime.strptime(datetime_str, date_format) 23 | -------------------------------------------------------------------------------- /src/deezer/exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import httpx 4 | 5 | 6 | class DeezerAPIException(Exception): 7 | """Base exception for API errors.""" 8 | 9 | 10 | class DeezerRetryableException(DeezerAPIException): 11 | """A request failing with this might work if retried.""" 12 | 13 | 14 | class DeezerHTTPError(DeezerAPIException): 15 | """Specialization wrapping HTTPError from the httpx library.""" 16 | 17 | def __init__(self, http_exception: httpx.HTTPStatusError, *args: object) -> None: 18 | if http_exception.response is not None and http_exception.response.text: 19 | url = http_exception.response.request.url 20 | status_code = http_exception.response.status_code 21 | text = http_exception.response.text 22 | super().__init__(status_code, url, text, *args) 23 | else: 24 | super().__init__(http_exception, *args) 25 | 26 | @classmethod 27 | def from_http_error(cls, exc: httpx.HTTPStatusError) -> DeezerHTTPError: 28 | """Initialize the appropriate internal exception from a HTTPError.""" 29 | if exc.response is not None: 30 | if exc.response.status_code in {502, 503, 504}: 31 | return DeezerRetryableHTTPError(exc) 32 | if exc.response.status_code == 403: 33 | return DeezerForbiddenError(exc) 34 | if exc.response.status_code == 404: 35 | return DeezerNotFoundError(exc) 36 | return DeezerHTTPError(exc) 37 | 38 | 39 | class DeezerRetryableHTTPError(DeezerRetryableException, DeezerHTTPError): 40 | """An HTTP error due to a potentially temporary issue.""" 41 | 42 | 43 | class DeezerForbiddenError(DeezerHTTPError): 44 | """A HTTP error cause by permission denied error.""" 45 | 46 | 47 | class DeezerNotFoundError(DeezerHTTPError): 48 | """For 404 HTTP errors.""" 49 | 50 | 51 | class DeezerErrorResponse(DeezerAPIException): 52 | """A functional error when the API doesn't accept the request.""" 53 | 54 | def __init__(self, json_data: dict[str, str]) -> None: 55 | self.json_data = json_data 56 | 57 | 58 | class DeezerUnknownResource(DeezerAPIException): 59 | """The resource type couldn't be determined.""" 60 | -------------------------------------------------------------------------------- /src/deezer/resources/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from .album import Album 4 | from .artist import Artist 5 | from .chart import Chart 6 | from .editorial import Editorial 7 | from .episode import Episode 8 | from .genre import Genre 9 | from .playlist import Playlist 10 | from .podcast import Podcast 11 | from .radio import Radio 12 | from .resource import Resource 13 | from .track import Track 14 | from .user import User 15 | 16 | __all__ = [ 17 | "Album", 18 | "Artist", 19 | "Chart", 20 | "Editorial", 21 | "Episode", 22 | "Genre", 23 | "Playlist", 24 | "Podcast", 25 | "Radio", 26 | "Resource", 27 | "Track", 28 | "User", 29 | ] 30 | -------------------------------------------------------------------------------- /src/deezer/resources/album.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime as dt 4 | from typing import TYPE_CHECKING 5 | 6 | from ..dates import parse_date 7 | from ..pagination import PaginatedList 8 | from .artist import Artist 9 | from .resource import Resource 10 | 11 | if TYPE_CHECKING: 12 | from .genre import Genre 13 | from .track import Track 14 | 15 | 16 | class Album(Resource): 17 | """ 18 | To work with an album object. 19 | 20 | Check the :deezer-api:`Deezer documentation ` 21 | for more details about each field. 22 | """ 23 | 24 | id: int 25 | title: str 26 | upc: str 27 | link: str 28 | share: str 29 | cover: str 30 | cover_small: str 31 | cover_medium: str 32 | cover_big: str 33 | cover_xl: str 34 | md5_image: str 35 | 36 | genre_id: int 37 | genres: list[Genre] 38 | label: str 39 | nb_tracks: int 40 | duration: int 41 | fans: int 42 | release_date: dt.date 43 | record_type: str 44 | available: bool 45 | 46 | alternative: Album 47 | tracklist: str 48 | explicit_lyrics: bool 49 | 50 | explicit_content_lyrics: int 51 | explicit_content_cover: int 52 | contributors: list[Artist] 53 | 54 | artist: Artist 55 | tracks: list[Track] 56 | 57 | _parse_release_date = staticmethod(parse_date) 58 | 59 | def _parse_contributors(self, raw_value): 60 | return [Artist(client=self.client, json=val) for val in raw_value] 61 | 62 | def get_artist(self) -> Artist: 63 | """ 64 | Get the artist of the Album. 65 | 66 | :returns: the :class:`Artist ` of the Album 67 | """ 68 | return self.client.get_artist(self.artist.id) 69 | 70 | def get_tracks(self, **kwargs) -> PaginatedList[Track]: 71 | """ 72 | Get a list of album's tracks. 73 | 74 | :returns: a :class:`PaginatedList ` 75 | of :class:`Track `. 76 | """ 77 | return self.get_paginated_list("tracks", **kwargs) 78 | -------------------------------------------------------------------------------- /src/deezer/resources/artist.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from ..pagination import PaginatedList 6 | from .resource import Resource 7 | 8 | if TYPE_CHECKING: 9 | from .album import Album 10 | from .playlist import Playlist 11 | from .track import Track 12 | 13 | 14 | class Artist(Resource): 15 | """ 16 | To work with Deezer artist objects. 17 | 18 | Check the :deezer-api:`Deezer documentation ` 19 | for more details about each field. 20 | """ 21 | 22 | id: int 23 | name: str 24 | link: str 25 | share: str 26 | picture: str 27 | picture_small: str 28 | picture_medium: str 29 | picture_big: str 30 | picture_xl: str 31 | nb_album: int 32 | nb_fan: int 33 | radio: bool 34 | tracklist: str 35 | 36 | def get_top(self, **kwargs) -> PaginatedList[Track]: 37 | """ 38 | Get the top tracks of an artist. 39 | 40 | :returns: a :class:`PaginatedList ` 41 | of :class:`Track ` instances. 42 | """ 43 | return self.get_paginated_list("top", **kwargs) 44 | 45 | def get_related(self, **kwargs) -> PaginatedList[Artist]: 46 | """ 47 | Get a list of related artists. 48 | 49 | :returns: a :class:`PaginatedList ` 50 | of :class:`Artist ` instances 51 | """ 52 | return self.get_paginated_list("related", **kwargs) 53 | 54 | def get_radio(self, **kwargs) -> list[Track]: 55 | """ 56 | Get a list of tracks. 57 | 58 | :returns: list of :class:`Track ` instances 59 | """ 60 | # radio returns tracks from different artists -> no fwd parent 61 | return self.get_relation("radio", fwd_parent=False, **kwargs) 62 | 63 | def get_albums(self, **kwargs) -> PaginatedList[Album]: 64 | """ 65 | Get a list of artist's albums. 66 | 67 | :returns: a :class:`PaginatedList ` 68 | of :class:`Album ` instances 69 | """ 70 | return self.get_paginated_list("albums", **kwargs) 71 | 72 | def get_playlists(self, **kwargs) -> PaginatedList[Playlist]: 73 | """ 74 | Get a list of artist's playlists. 75 | 76 | :returns: a :class:`PaginatedList ` 77 | of :class:`Playlist ` instances 78 | """ 79 | return self.get_paginated_list("playlists", **kwargs) 80 | -------------------------------------------------------------------------------- /src/deezer/resources/chart.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from .resource import Resource 6 | 7 | if TYPE_CHECKING: 8 | from ..pagination import PaginatedList 9 | from .album import Album 10 | from .artist import Artist 11 | from .playlist import Playlist 12 | from .podcast import Podcast 13 | from .track import Track 14 | 15 | 16 | class Chart(Resource): 17 | """ 18 | To work with Deezer chart objects. 19 | 20 | Check the :deezer-api:`Deezer documentation ` 21 | for more details about each field. 22 | """ 23 | 24 | type = "chart" 25 | 26 | id: int 27 | tracks: list[Track] 28 | albums: list[Album] 29 | artists: list[Artist] 30 | playlists: list[Playlist] 31 | podcasts: list[Podcast] 32 | 33 | def get_tracks(self, **kwargs) -> PaginatedList[Track]: 34 | """ 35 | Return the chart for tracks. 36 | 37 | :returns: a :class:`PaginatedList ` 38 | of :class:`Track ` instances 39 | """ 40 | return self.get_paginated_list("tracks", **kwargs) 41 | 42 | def get_albums(self, **kwargs) -> PaginatedList[Album]: 43 | """ 44 | Return the chart for albums. 45 | 46 | :returns: a :class:`PaginatedList ` 47 | of :class:`Album ` instances 48 | """ 49 | return self.get_paginated_list("albums", **kwargs) 50 | 51 | def get_artists(self, **kwargs) -> PaginatedList[Artist]: 52 | """ 53 | Return the chart for artists. 54 | 55 | :returns: a :class:`PaginatedList ` 56 | of :class:`Artist ` instances 57 | """ 58 | return self.get_paginated_list("artists", **kwargs) 59 | 60 | def get_playlists(self, **kwargs) -> PaginatedList[Playlist]: 61 | """ 62 | Return the chart for playlists. 63 | 64 | :returns: a :class:`PaginatedList ` 65 | of :class:`Playlist ` instances 66 | """ 67 | return self.get_paginated_list("playlists", **kwargs) 68 | 69 | def get_podcasts(self, **kwargs) -> PaginatedList[Podcast]: 70 | """ 71 | Return the chart for podcasts. 72 | 73 | :returns: a :class:`PaginatedList ` 74 | of :class:`Podcast ` instances 75 | """ 76 | return self.get_paginated_list("podcasts", **kwargs) 77 | -------------------------------------------------------------------------------- /src/deezer/resources/editorial.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from .chart import Chart 6 | from .resource import Resource 7 | 8 | if TYPE_CHECKING: 9 | from ..pagination import PaginatedList 10 | from .album import Album 11 | 12 | 13 | class Editorial(Resource): 14 | """ 15 | To work with Deezer editorial objects. 16 | 17 | Check the :deezer-api:`Deezer documentation ` 18 | for more details about each field. 19 | """ 20 | 21 | id: int 22 | name: str 23 | picture: str 24 | picture_small: str 25 | picture_medium: str 26 | picture_big: str 27 | picture_xl: str 28 | 29 | def get_selection(self) -> list[Album]: 30 | """ 31 | Get a list of albums selected every week by the Deezer Team. 32 | 33 | :returns: a list of :class:`Album ` instances 34 | """ 35 | return self.get_relation("selection") 36 | 37 | def get_chart(self) -> Chart: 38 | """ 39 | Get top charts for tracks, albums, artists and playlists. 40 | 41 | :returns: a :class:`~deezer.Chart` instance 42 | """ 43 | return self.get_relation("charts", resource_type=Chart) 44 | 45 | def get_releases(self, **kwargs) -> PaginatedList[Album]: 46 | """ 47 | Get the new releases per genre for the current country. 48 | 49 | :returns: a :class:`PaginatedList ` 50 | of :class:`Album ` instances 51 | """ 52 | return self.get_paginated_list("releases", **kwargs) 53 | -------------------------------------------------------------------------------- /src/deezer/resources/episode.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime as dt 4 | from typing import TYPE_CHECKING, Any 5 | 6 | from ..dates import parse_datetime 7 | from .resource import Resource 8 | 9 | if TYPE_CHECKING: 10 | from .podcast import Podcast 11 | 12 | 13 | class Episode(Resource): 14 | """ 15 | To work with Deezer episode objects. 16 | 17 | Check the :deezer-api:`Deezer documentation ` 18 | for more details about each field. 19 | """ 20 | 21 | id: int 22 | title: str 23 | description: str 24 | available: bool 25 | release_date: dt.datetime 26 | duration: int 27 | link: str 28 | share: str 29 | picture: str 30 | picture_small: str 31 | picture_medium: str 32 | picture_big: str 33 | picture_xl: str 34 | podcast: Podcast 35 | 36 | _parse_release_date = staticmethod(parse_datetime) 37 | 38 | def _infer_missing_field(self, item) -> Any: 39 | if item == "link": 40 | return f"https://www.deezer.com/episode/{self.id}" 41 | elif item == "share": 42 | return f"{self.link}?utm_source=deezer&utm_content=episode-{self.id}&utm_medium=web" 43 | return super()._infer_missing_field(item) 44 | 45 | def add_bookmark(self, offset: int) -> bool: 46 | """ 47 | Sets a bookmark on the episode. 48 | 49 | :param offset: The offset where the bookmark is set, must be between 0 and 100. 50 | :returns: a boolean that tells if the operation was successful 51 | """ 52 | return self.client.request( 53 | "POST", 54 | f"episode/{self.id}/bookmark", 55 | params={"offset": offset}, 56 | ) 57 | 58 | def remove_bookmark(self) -> bool: 59 | """ 60 | Removes the bookmark on the episode. 61 | 62 | :returns: a boolean that tells if the operation was successful 63 | """ 64 | return self.client.request("DELETE", f"episode/{self.id}/bookmark") 65 | -------------------------------------------------------------------------------- /src/deezer/resources/genre.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from .resource import Resource 6 | 7 | if TYPE_CHECKING: 8 | from ..pagination import PaginatedList 9 | from .artist import Artist 10 | from .podcast import Podcast 11 | from .radio import Radio 12 | 13 | 14 | class Genre(Resource): 15 | """ 16 | To work with Deezer genre objects. 17 | 18 | Check the :deezer-api:`Deezer documentation ` 19 | for more details about each field. 20 | """ 21 | 22 | id: int 23 | name: str 24 | picture: str 25 | picture_small: str 26 | picture_medium: str 27 | picture_big: str 28 | picture_xl: str 29 | 30 | def get_artists(self, **kwargs) -> list[Artist]: 31 | """ 32 | Get all artists for a genre. 33 | 34 | :returns: list of :class:`Artist ` instances 35 | """ 36 | return self.get_relation("artists", **kwargs) 37 | 38 | def get_podcasts(self, **kwargs) -> PaginatedList[Podcast]: 39 | """ 40 | Get all podcasts for a genre. 41 | 42 | :returns: a :class:`PaginatedList ` 43 | of :class:`Podcast ` instances 44 | """ 45 | return self.get_paginated_list("podcasts", **kwargs) 46 | 47 | def get_radios(self, **kwargs) -> list[Radio]: 48 | """ 49 | Get all radios for a genre. 50 | 51 | :returns: list of :class:`Radio ` instances 52 | """ 53 | return self.get_relation("radios", **kwargs) 54 | -------------------------------------------------------------------------------- /src/deezer/resources/playlist.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from collections.abc import Iterable 4 | from typing import TYPE_CHECKING 5 | 6 | from ..utils import gen_ids 7 | from .resource import Resource 8 | 9 | if TYPE_CHECKING: 10 | from ..pagination import PaginatedList 11 | from .track import Track 12 | from .user import User 13 | 14 | 15 | class Playlist(Resource): 16 | """ 17 | To work with Deezer playlist objects. 18 | 19 | Check the :deezer-api:`Deezer documentation ` 20 | for more details about each field. 21 | """ 22 | 23 | id: int 24 | title: str 25 | description: str 26 | duration: int 27 | public: bool 28 | is_loved_track: bool 29 | collaborative: bool 30 | nb_tracks: int 31 | unseen_track_count: int 32 | fans: int 33 | link: str 34 | share: str 35 | picture: str 36 | picture_small: str 37 | picture_medium: str 38 | picture_big: str 39 | picture_xl: str 40 | checksum: str 41 | creator: User 42 | tracks: list[Track] 43 | 44 | def get_tracks(self, **kwargs) -> PaginatedList[Track]: 45 | """ 46 | Get tracks from a playlist. 47 | 48 | :returns: a :class:`PaginatedList ` 49 | of :class:`Track ` instances 50 | """ 51 | return self.get_paginated_list("tracks", **kwargs) 52 | 53 | def get_fans(self, **kwargs) -> PaginatedList[User]: 54 | """ 55 | Get fans from a playlist. 56 | 57 | :returns: a :class:`PaginatedList ` 58 | of :class:`User ` instances 59 | """ 60 | return self.get_paginated_list("fans", **kwargs) 61 | 62 | def mark_seen(self) -> bool: 63 | """ 64 | Mark the playlist as seen. 65 | 66 | :returns: a boolean that tells if the operation was successful 67 | """ 68 | return self.client.request("POST", f"playlist/{self.id}/seen") 69 | 70 | def add_tracks(self, tracks: Iterable[int | Track]) -> bool: 71 | """ 72 | Add tracks to a playlist. 73 | 74 | :param tracks: An iterable of :class:`Track ` instances 75 | or their IDs to add to the playlist 76 | :returns: a boolean that tells if the operation was successful 77 | """ 78 | track_ids_str = ",".join(str(tid) for tid in gen_ids(tracks)) 79 | return self.client.request("POST", f"playlist/{self.id}/tracks", params={"songs": track_ids_str}) 80 | 81 | def delete_tracks(self, tracks: Iterable[int | Track]) -> bool: 82 | """ 83 | Delete tracks from a playlist. 84 | 85 | :param tracks: An iterable of :class:`Track ` instances 86 | or their IDs to remove from the playlist. 87 | :returns: a boolean that tells if the operation was successful 88 | """ 89 | track_ids_str = ",".join(map(str, gen_ids(tracks))) 90 | return self.client.request("DELETE", f"playlist/{self.id}/tracks", params={"songs": track_ids_str}) 91 | 92 | def reorder_tracks(self, order: Iterable[int | Track]) -> bool: 93 | """ 94 | Reorder the tracks of a playlist. 95 | 96 | :param order: An iterable of :class:`Track ` instances 97 | or their IDs in the wished order. 98 | :returns: a boolean that tells if the operation was successful 99 | """ 100 | order_track_ids_str = ",".join(map(str, gen_ids(order))) 101 | return self.client.request("POST", f"playlist/{self.id}/tracks", params={"order": order_track_ids_str}) 102 | -------------------------------------------------------------------------------- /src/deezer/resources/podcast.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from .resource import Resource 6 | 7 | if TYPE_CHECKING: 8 | from ..pagination import PaginatedList 9 | from .episode import Episode 10 | 11 | 12 | class Podcast(Resource): 13 | """ 14 | To work with Deezer podcast objects. 15 | 16 | Check the :deezer-api:`Deezer documentation ` 17 | for more details about each field. 18 | """ 19 | 20 | id: int 21 | title: str 22 | description: str 23 | available: bool 24 | fans: int 25 | link: str 26 | share: str 27 | picture: str 28 | picture_small: str 29 | picture_medium: str 30 | picture_big: str 31 | picture_xl: str 32 | 33 | def get_episodes(self, **kwargs) -> PaginatedList[Episode]: 34 | """ 35 | Get episodes from a podcast. 36 | 37 | :returns: a :class:`PaginatedList ` 38 | of :class:`Episode ` instances 39 | """ 40 | return self.get_paginated_list("episodes", **kwargs) 41 | -------------------------------------------------------------------------------- /src/deezer/resources/radio.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from .resource import Resource 6 | 7 | if TYPE_CHECKING: 8 | from .track import Track 9 | 10 | 11 | class Radio(Resource): 12 | """ 13 | To work with Deezer radio objects. 14 | 15 | Check the :deezer-api:`Deezer documentation ` 16 | for more details about each field. 17 | """ 18 | 19 | id: int 20 | title: str 21 | description: str 22 | share: str 23 | picture: str 24 | picture_small: str 25 | picture_medium: str 26 | picture_big: str 27 | picture_xl: str 28 | tracklist: str 29 | md5_image: str 30 | 31 | def get_tracks(self) -> list[Track]: 32 | """ 33 | Get first 40 tracks in the radio. 34 | 35 | Note that this endpoint is NOT paginated. 36 | 37 | :returns: a list of :class:`Track ` instances. 38 | """ 39 | return self.get_relation("tracks") 40 | -------------------------------------------------------------------------------- /src/deezer/resources/track.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime as dt 4 | from typing import TYPE_CHECKING 5 | 6 | from ..dates import parse_date 7 | from .artist import Artist 8 | from .resource import Resource 9 | 10 | if TYPE_CHECKING: 11 | from .album import Album 12 | 13 | 14 | class Track(Resource): 15 | """ 16 | To work with Deezer track objects. 17 | 18 | Check the :deezer-api:`Deezer documentation ` 19 | for more details about each field. 20 | """ 21 | 22 | id: int 23 | readable: bool 24 | title: str 25 | title_short: str 26 | title_version: str 27 | unseen: bool 28 | isrc: str 29 | link: str 30 | share: str 31 | duration: int 32 | track_position: int 33 | disk_number: int 34 | rank: int 35 | release_date: dt.date 36 | explicit_lyrics: bool 37 | explicit_content_lyrics: int 38 | explicit_content_cover: int 39 | preview: str 40 | bpm: float 41 | gain: float 42 | available_countries: list[str] 43 | alternative: Track 44 | contributors: list[Artist] 45 | md5_image: str 46 | artist: Artist 47 | album: Album 48 | 49 | _parse_release_date = staticmethod(parse_date) 50 | 51 | def _parse_contributors(self, raw_value): 52 | return [Artist(client=self.client, json=val) for val in raw_value] 53 | 54 | def get_artist(self) -> Artist: 55 | """ 56 | Get the artist of the Track. 57 | 58 | :returns: the :class:`Artist ` of the Album 59 | """ 60 | return self.client.get_artist(self.artist.id) 61 | 62 | def get_album(self) -> Album: 63 | """ 64 | Get the album of the Track. 65 | 66 | :returns: the :class:`Album ` instance 67 | """ 68 | return self.client.get_album(self.album.id) 69 | -------------------------------------------------------------------------------- /src/deezer/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from collections.abc import Generator, Iterable 4 | from typing import TYPE_CHECKING 5 | 6 | if TYPE_CHECKING: 7 | from deezer import Resource 8 | 9 | 10 | def gen_ids(item_list: Iterable[int | Resource]) -> Generator[int, None, None]: 11 | """Get IDs for an iterable of `int` or `Resources`.""" 12 | for item in item_list: 13 | yield get_id(item) 14 | 15 | 16 | def get_id(item: int | Resource) -> int: 17 | """Get ID for an `int` or `Resource`.""" 18 | if isinstance(item, int): 19 | return item 20 | if hasattr(item, "id"): 21 | return item.id 22 | raise NotImplementedError(f"Unknown type for {item}") 23 | -------------------------------------------------------------------------------- /templates/CHANGELOG.md.j2: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | {%- for version, release in context.history.released.items() %} 4 | {%- if version.as_tag() > "v6.0.0" %} 5 | 6 | ## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) 7 | 8 | {%- for category, commits in release["elements"].items() %}{% if category != "unknown" %} 9 | {# Category title: Breaking, Fix, Documentation #} 10 | ### {{ category | capitalize }} 11 | {# List actual changes in the category #} 12 | {%- for commit in commits %} 13 | - {{ commit.descriptions[0] | capitalize }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) 14 | {%- endfor %}{# for commit #} 15 | 16 | {%- endif %}{% endfor %}{# for category, commits #} 17 | 18 | {%- endif %}{# if version.as_tag() #} 19 | 20 | {%- endfor %}{# for version, release #} 21 | 22 | {% include ".changelog-old.md" %}{# include old changelog at the end -#} 23 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/browniebroke/deezer-python/fc941a3fbcdb3033aeadc814702ec7dcfe745d7e/tests/__init__.py -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_add_user_album.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.26.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/albums?album_id=302127&access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | P3P: 40 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-158 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_add_user_artist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.26.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/artists?artist_id=243&access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Date: 40 | - Sat, 09 Oct 2021 17:43:02 GMT 41 | Expires: 42 | - Thu, 19 Nov 1981 08:52:00 GMT 43 | P3P: 44 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 45 | Pragma: 46 | - no-cache 47 | Server: 48 | - Apache 49 | Set-Cookie: 50 | - dzr_uniq_id=dzr_uniq_id_frb734f69afd1d53c8c9deb697d7f2c51fc622ce; expires=Thu, 51 | 07-Apr-2022 17:43:02 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure; 52 | HttpOnly; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | X-Host: 56 | - blm-web-156 57 | x-org: 58 | - FR 59 | status: 60 | code: 200 61 | message: OK 62 | version: 1 63 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_add_user_following.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.28.1 15 | method: POST 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=2640689 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-35 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_add_user_playlist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.29.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/playlists?access_token=dummy&playlist_id=8749345882 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-76 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_add_user_track.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.26.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/tracks?track_id=1374789602&access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | P3P: 40 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-160 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_advanced_search_complex_with_relation.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/search/track?index=0&limit=25&q=artist%3A%22Lou+Doillon%22+track%3A%22Joke%22 11 | response: 12 | body: {string: '{"data":[{"id":623719262,"readable":true,"title":"The joke","title_short":"The 13 | joke","title_version":"","link":"https:\/\/www.deezer.com\/track\/623719262","duration":200,"rank":761992,"explicit_lyrics":false,"explicit_content_lyrics":0,"explicit_content_cover":0,"preview":"https:\/\/cdns-preview-1.dzcdn.net\/stream\/c-13de758485a7cc167fbc081306d57b3b-4.mp3","artist":{"id":1701297,"name":"Lou 14 | Doillon","link":"https:\/\/www.deezer.com\/artist\/1701297","picture":"https:\/\/api.deezer.com\/artist\/1701297\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/bb461ed99b3de8c83959069d81af5e17\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/bb461ed99b3de8c83959069d81af5e17\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/bb461ed99b3de8c83959069d81af5e17\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/bb461ed99b3de8c83959069d81af5e17\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/artist\/1701297\/top?limit=50","type":"artist"},"album":{"id":85607212,"title":"Soliloquy","cover":"https:\/\/api.deezer.com\/album\/85607212\/image","cover_small":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/880c64b16d6984e10a56617c244bf29c\/56x56-000000-80-0-0.jpg","cover_medium":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/880c64b16d6984e10a56617c244bf29c\/250x250-000000-80-0-0.jpg","cover_big":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/880c64b16d6984e10a56617c244bf29c\/500x500-000000-80-0-0.jpg","cover_xl":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/880c64b16d6984e10a56617c244bf29c\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/album\/85607212\/tracks","type":"album"},"type":"track"}],"total":1}'} 15 | headers: 16 | Content-Length: ['1809'] 17 | Content-Type: [application/json; charset=utf-8] 18 | Date: ['Wed, 13 Feb 2019 10:35:33 GMT'] 19 | P3P: [policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 20 | STA"] 21 | Server: [Apache] 22 | Set-Cookie: ['dzr_uniq_id=dzr_uniq_id_fread9bf25213bcb495877baf725223ab3a07374; 23 | expires=Mon, 12-Aug-2019 10:35:33 GMT; Max-Age=15552000; path=/; domain=.deezer.com', 24 | 'ADRUM_BT=R%3A0%7Cg%3A65f20f8c-dc2f-4093-9211-0633daf704001560%7Cn%3Adeezer_4d9fde9f-5114-49cf-ae7c-978ae57772c1%7Ci%3A342331%7Cd%3A19%7Ce%3A82; 25 | expires=Wed, 13-Feb-2019 10:36:03 GMT; Max-Age=30; path=/'] 26 | Vary: [Accept-Encoding] 27 | X-Host: [blm-web-106] 28 | status: {code: 200, message: OK} 29 | version: 1 30 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_create_playlist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.29.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/playlists?access_token=dummy&title=CoolPlaylist 17 | response: 18 | body: 19 | string: '{"id":11336219744}' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '18' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-83 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_delete_playlist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.29.0 15 | method: DELETE 16 | uri: https://api.deezer.com/playlist/11336219744?access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-38 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_artist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/artist/27 11 | response: 12 | body: {string: '{"id":27,"name":"Daft Punk","link":"https:\/\/www.deezer.com\/artist\/27","share":"https:\/\/www.deezer.com\/artist\/27?utm_source=deezer&utm_content=artist-27&utm_term=0_1549974227&utm_medium=web","picture":"https:\/\/api.deezer.com\/artist\/27\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/1000x1000-000000-80-0-0.jpg","nb_album":32,"nb_fan":3677952,"radio":true,"tracklist":"https:\/\/api.deezer.com\/artist\/27\/top?limit=50","type":"artist"}'} 13 | headers: 14 | Content-Length: ['891'] 15 | Content-Type: [application/json; charset=utf-8] 16 | Date: ['Tue, 12 Feb 2019 12:23:47 GMT'] 17 | P3P: [policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 18 | STA"] 19 | Server: [Apache] 20 | Set-Cookie: ['dzr_uniq_id=dzr_uniq_id_fr9f8baabe0d53a4b08415b091bda1bd5cfdbad7; 21 | expires=Sun, 11-Aug-2019 12:23:47 GMT; Max-Age=15552000; path=/; domain=.deezer.com'] 22 | Vary: [Accept-Encoding] 23 | X-Host: [blm-web-125] 24 | status: {code: 200, message: OK} 25 | version: 1 26 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_current_user.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.26.0 13 | method: GET 14 | uri: https://api.deezer.com/user/me?access_token=dummy 15 | response: 16 | body: 17 | string: '{"id":359622,"name":"Bruno Alla","lastname":"Alla","firstname":"Bruno","email":"bruno.alla@example.com","status":0,"birthday":"0000-00-00","inscription_date":"2007-08-26","gender":"","link":"https:\/\/www.deezer.com\/profile\/359622","picture":"https:\/\/api.deezer.com\/user\/359622\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/1000x1000-000000-80-0-0.jpg","country":"GB","lang":"fr","is_kid":false,"explicit_content_level":"explicit_display","explicit_content_levels_available":["explicit_display","explicit_no_recommendation","explicit_hide"],"tracklist":"https:\/\/api.deezer.com\/user\/359622\/flow","type":"user"}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Cache-Control: 30 | - no-store, no-cache, must-revalidate 31 | Connection: 32 | - keep-alive 33 | Content-Length: 34 | - '1056' 35 | Content-Type: 36 | - application/json; charset=utf-8 37 | Expires: 38 | - Thu, 19 Nov 1981 08:52:00 GMT 39 | Pragma: 40 | - no-cache 41 | Server: 42 | - Apache 43 | Vary: 44 | - Accept-Encoding 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-155 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_editorial.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.27.1 13 | method: GET 14 | uri: https://api.deezer.com/editorial/0 15 | response: 16 | body: 17 | string: '{"id":0,"name":"All","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/\/1000x1000-000000-80-0-0.jpg","type":"editorial"}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Content-Length: 30 | - '505' 31 | Content-Type: 32 | - application/json; charset=utf-8 33 | Server: 34 | - Apache 35 | Vary: 36 | - Accept-Encoding,Origin 37 | X-Content-Type-Options: 38 | - nosniff 39 | X-Host: 40 | - blm-web-12 41 | status: 42 | code: 200 43 | message: OK 44 | version: 1 45 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_episode.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.24.0 13 | method: GET 14 | uri: https://api.deezer.com/episode/238455362 15 | response: 16 | body: 17 | string: '{"id":238455362,"title":"Episode 9: Follow the money","description":"The 18 | search for Dr Ruja is back on. A lot has happened since Episode 8. Original 19 | music and sound design: Phil Channell Original Music and vocals: Dessislava 20 | Stefanova and the London Bulgarian Choir This audio was updated on 12\/08\/2020 21 | to reflect additional correspondence with Chelgate.","available":true,"release_date":"2020-08-06 22 | 09:00:00","duration":2755,"link":"https:\/\/www.deezer.com\/episode\/238455362","share":"https:\/\/www.deezer.com\/episode\/238455362?utm_source=deezer&utm_content=episode-238455362&utm_term=0_1597915623&utm_medium=web","picture":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/1000x1000-000000-80-0-0.jpg","podcast":{"id":699612,"title":"The 23 | Missing Cryptoqueen","link":"https:\/\/www.deezer.com\/show\/699612","picture":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/1000x1000-000000-80-0-0.jpg","type":"podcast"},"type":"episode"}' 24 | headers: 25 | Content-Length: 26 | - '1994' 27 | Content-Type: 28 | - application/json; charset=utf-8 29 | Date: 30 | - Thu, 20 Aug 2020 09:27:03 GMT 31 | P3P: 32 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 33 | Server: 34 | - Apache 35 | Set-Cookie: 36 | - dzr_uniq_id=dzr_uniq_id_frf4ec25bbbe333607bc2293723d55ce03f709c4; expires=Tue, 37 | 16-Feb-2021 09:27:03 GMT; Max-Age=15552000; path=/;SameSite=None; domain=.deezer.com; 38 | secure; HttpOnly 39 | Vary: 40 | - Accept-Encoding 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Host: 44 | - blm-web-143 45 | status: 46 | code: 200 47 | message: OK 48 | version: 1 49 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_genre.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/genre/106 11 | response: 12 | body: {string: '{"id":106,"name":"Electro","picture":"https:\/\/api.deezer.com\/genre\/106\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/1000x1000-000000-80-0-0.jpg","type":"genre"}'} 13 | headers: 14 | Content-Length: ['602'] 15 | Content-Type: [application/json; charset=utf-8] 16 | Date: ['Tue, 12 Feb 2019 12:23:48 GMT'] 17 | P3P: [policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 18 | STA"] 19 | Server: [Apache] 20 | Set-Cookie: ['dzr_uniq_id=dzr_uniq_id_frf6a59229fe8f19e7f760aa3051d1fc5e7d014e; 21 | expires=Sun, 11-Aug-2019 12:23:48 GMT; Max-Age=15552000; path=/; domain=.deezer.com'] 22 | Vary: [Accept-Encoding] 23 | X-Host: [blm-web-66] 24 | status: {code: 200, message: OK} 25 | version: 1 26 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_podcast.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.24.0 13 | method: GET 14 | uri: https://api.deezer.com/podcast/699612 15 | response: 16 | body: 17 | string: '{"id":699612,"title":"The Missing Cryptoqueen","description":"Dr Ruja 18 | Ignatova persuaded millions to join her financial revolution. Then she disappeared. 19 | Why? Jamie Bartlett presents a story of greed, deceit and herd madness.","available":true,"rating":0,"fans":159,"link":"https:\/\/www.deezer.com\/show\/699612","share":"https:\/\/www.deezer.com\/show\/699612?utm_source=deezer&utm_content=show-699612&utm_term=0_1597915624&utm_medium=web","picture":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/talk\/34df19abb7d60668cf967d1d866943b3\/1000x1000-000000-80-0-0.jpg","type":"podcast"}' 20 | headers: 21 | Content-Length: 22 | - '1074' 23 | Content-Type: 24 | - application/json; charset=utf-8 25 | Date: 26 | - Thu, 20 Aug 2020 09:27:04 GMT 27 | P3P: 28 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 29 | Server: 30 | - Apache 31 | Set-Cookie: 32 | - dzr_uniq_id=dzr_uniq_id_fr4352bb520686d0df8a87224e972c04b235cade; expires=Tue, 33 | 16-Feb-2021 09:27:04 GMT; Max-Age=15552000; path=/;SameSite=None; domain=.deezer.com; 34 | secure; HttpOnly 35 | Vary: 36 | - Accept-Encoding 37 | X-Content-Type-Options: 38 | - nosniff 39 | X-Host: 40 | - blm-web-148 41 | status: 42 | code: 200 43 | message: OK 44 | version: 1 45 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_radio.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/radio/23261 11 | response: 12 | body: {string: '{"id":23261,"title":"Telegraph Classical","description":"Telegraph 13 | Classical","share":"https:\/\/www.deezer.com\/mixes\/genre\/23261?utm_source=deezer&utm_content=mixes-genre-23261&utm_term=0_1549974229&utm_medium=web","picture":"https:\/\/api.deezer.com\/radio\/23261\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/radio\/23261\/tracks","type":"radio"}'} 14 | headers: 15 | Content-Length: ['857'] 16 | Content-Type: [application/json; charset=utf-8] 17 | Date: ['Tue, 12 Feb 2019 12:23:49 GMT'] 18 | P3P: [policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 19 | STA"] 20 | Server: [Apache] 21 | Set-Cookie: ['dzr_uniq_id=dzr_uniq_id_fr8c9965ebcc84063a174d4435f8c1b0e16eedb9; 22 | expires=Sun, 11-Aug-2019 12:23:49 GMT; Max-Age=15552000; path=/; domain=.deezer.com'] 23 | Vary: [Accept-Encoding] 24 | X-Host: [blm-web-59] 25 | status: {code: 200, message: OK} 26 | version: 1 27 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_user.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ['*/*'] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/user/359622 11 | response: 12 | body: {string: '{"id":359622,"name":"Bruno Alla","link":"https:\/\/www.deezer.com\/profile\/359622","picture":"https:\/\/api.deezer.com\/user\/359622\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/user\/d4bb710fd346218d4dbb5e1a08ffde93\/1000x1000-000000-80-0-0.jpg","country":"DE","tracklist":"https:\/\/api.deezer.com\/user\/359622\/flow","type":"user"}'} 13 | headers: 14 | Content-Length: ['726'] 15 | Content-Type: [application/json; charset=utf-8] 16 | Date: ['Tue, 12 Feb 2019 12:23:51 GMT'] 17 | P3P: [policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 18 | STA"] 19 | Server: [Apache] 20 | Set-Cookie: ['dzr_uniq_id=dzr_uniq_id_fr5480426e878dcd4a67178b5a002e51a1251652; 21 | expires=Sun, 11-Aug-2019 12:23:51 GMT; Max-Age=15552000; path=/; domain=.deezer.com'] 22 | Vary: [Accept-Encoding] 23 | X-Host: [blm-web-78] 24 | status: {code: 200, message: OK} 25 | version: 1 26 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_user_followers[args0].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.28.1 13 | method: GET 14 | uri: https://api.deezer.com/user/me/followers?access_token=dummy 15 | response: 16 | body: 17 | string: '{"data":[{"id":5100920542,"name":"John Doe","picture":"https:\/\/api.deezer.com\/user\/5100920542\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5100920542\/flow","type":"user"},{"id":5139545882,"name":"Jane 18 | Doe","picture":"https:\/\/api.deezer.com\/user\/5139545882\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5139545882\/flow","type":"user"}],"total":2}' 19 | headers: 20 | Access-Control-Allow-Credentials: 21 | - 'true' 22 | Access-Control-Allow-Headers: 23 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 24 | Access-Control-Allow-Methods: 25 | - POST, GET, OPTIONS, DELETE, PUT 26 | Access-Control-Expose-Headers: 27 | - Location 28 | Access-Control-Max-Age: 29 | - '86400' 30 | Cache-Control: 31 | - no-store, no-cache, must-revalidate 32 | Connection: 33 | - keep-alive 34 | Content-Length: 35 | - '1122' 36 | Content-Type: 37 | - application/json; charset=utf-8 38 | Expires: 39 | - Thu, 19 Nov 1981 08:52:00 GMT 40 | Pragma: 41 | - no-cache 42 | Server: 43 | - Apache 44 | Strict-Transport-Security: 45 | - max-age=31536000 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-40 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_user_followers[args1].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.28.1 13 | method: GET 14 | uri: https://api.deezer.com/user/359622/followers?access_token=dummy 15 | response: 16 | body: 17 | string: '{"data":[{"id":5100920542,"name":"John Doe","picture":"https:\/\/api.deezer.com\/user\/5100920542\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5100920542\/flow","type":"user"},{"id":5139545882,"name":"Jane 18 | Doe","picture":"https:\/\/api.deezer.com\/user\/5139545882\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5139545882\/flow","type":"user"}],"total":2}' 19 | headers: 20 | Access-Control-Allow-Credentials: 21 | - 'true' 22 | Access-Control-Allow-Headers: 23 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 24 | Access-Control-Allow-Methods: 25 | - POST, GET, OPTIONS, DELETE, PUT 26 | Access-Control-Expose-Headers: 27 | - Location 28 | Access-Control-Max-Age: 29 | - '86400' 30 | Cache-Control: 31 | - no-store, no-cache, must-revalidate 32 | Connection: 33 | - keep-alive 34 | Content-Length: 35 | - '1122' 36 | Content-Type: 37 | - application/json; charset=utf-8 38 | Expires: 39 | - Thu, 19 Nov 1981 08:52:00 GMT 40 | Pragma: 41 | - no-cache 42 | Server: 43 | - Apache 44 | Strict-Transport-Security: 45 | - max-age=31536000 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-40 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_user_followings[args0].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.28.1 13 | method: GET 14 | uri: https://api.deezer.com/user/me/followings?access_token=dummy 15 | response: 16 | body: 17 | string: '{"data":[{"id":5100920542,"name":"John Doe","picture":"https:\/\/api.deezer.com\/user\/5100920542\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5100920542\/flow","type":"user"},{"id":5139545882,"name":"Jane 18 | Doe","picture":"https:\/\/api.deezer.com\/user\/5139545882\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5139545882\/flow","type":"user"}],"total":2}' 19 | headers: 20 | Access-Control-Allow-Credentials: 21 | - 'true' 22 | Access-Control-Allow-Headers: 23 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 24 | Access-Control-Allow-Methods: 25 | - POST, GET, OPTIONS, DELETE, PUT 26 | Access-Control-Expose-Headers: 27 | - Location 28 | Access-Control-Max-Age: 29 | - '86400' 30 | Cache-Control: 31 | - no-store, no-cache, must-revalidate 32 | Connection: 33 | - keep-alive 34 | Content-Length: 35 | - '1122' 36 | Content-Type: 37 | - application/json; charset=utf-8 38 | Expires: 39 | - Thu, 19 Nov 1981 08:52:00 GMT 40 | Pragma: 41 | - no-cache 42 | Server: 43 | - Apache 44 | Strict-Transport-Security: 45 | - max-age=31536000 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-40 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_get_user_followings[args1].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.28.1 13 | method: GET 14 | uri: https://api.deezer.com/user/359622/followings?access_token=dummy 15 | response: 16 | body: 17 | string: '{"data":[{"id":5100920542,"name":"John Doe","picture":"https:\/\/api.deezer.com\/user\/5100920542\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5100920542\/flow","type":"user"},{"id":5139545882,"name":"Jane 18 | Doe","picture":"https:\/\/api.deezer.com\/user\/5139545882\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5139545882\/flow","type":"user"}],"total":2}' 19 | headers: 20 | Access-Control-Allow-Credentials: 21 | - 'true' 22 | Access-Control-Allow-Headers: 23 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 24 | Access-Control-Allow-Methods: 25 | - POST, GET, OPTIONS, DELETE, PUT 26 | Access-Control-Expose-Headers: 27 | - Location 28 | Access-Control-Max-Age: 29 | - '86400' 30 | Cache-Control: 31 | - no-store, no-cache, must-revalidate 32 | Connection: 33 | - keep-alive 34 | Content-Length: 35 | - '1122' 36 | Content-Type: 37 | - application/json; charset=utf-8 38 | Expires: 39 | - Thu, 19 Nov 1981 08:52:00 GMT 40 | Pragma: 41 | - no-cache 42 | Server: 43 | - Apache 44 | Strict-Transport-Security: 45 | - max-age=31536000 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-03 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_album_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/album/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"DataException","message":"no data","code":800}}' 18 | headers: 19 | Content-Length: 20 | - '65' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_fr761e7d6d903672585e283abeb2d0e942b3d9c4; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | X-Host: 33 | - blm-web-49 34 | status: 35 | code: 200 36 | message: OK 37 | version: 1 38 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_artist_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/artist/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"DataException","message":"no data","code":800}}' 18 | headers: 19 | Content-Length: 20 | - '65' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_fra50c22608ba89c514271f03f4a4c885f786f91; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | X-Host: 33 | - blm-web-15 34 | status: 35 | code: 200 36 | message: OK 37 | version: 1 38 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_editorial_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.27.1 13 | method: GET 14 | uri: https://api.deezer.com/editorial/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"ParameterException","message":"Wrong parameter","code":500}}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Content-Length: 30 | - '78' 31 | Content-Type: 32 | - application/json; charset=utf-8 33 | Server: 34 | - Apache 35 | Vary: 36 | - Accept-Encoding,Origin 37 | X-Content-Type-Options: 38 | - nosniff 39 | X-Host: 40 | - blm-web-134 41 | status: 42 | code: 200 43 | message: OK 44 | version: 1 45 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_episode_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.24.0 13 | method: GET 14 | uri: https://api.deezer.com/episode/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"Exception","message":"An error has occured"}}' 18 | headers: 19 | Content-Length: 20 | - '63' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Thu, 20 Aug 2020 09:27:03 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_frb4df369d3350c71e2d28a0bd170b497aee3126; expires=Tue, 31 | 16-Feb-2021 09:27:03 GMT; Max-Age=15552000; path=/;SameSite=None; domain=.deezer.com; 32 | secure; HttpOnly 33 | X-Content-Type-Options: 34 | - nosniff 35 | X-Host: 36 | - blm-web-86 37 | status: 38 | code: 200 39 | message: OK 40 | version: 1 41 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_genre_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/genre/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"ParameterException","message":"Wrong parameter","code":500}}' 18 | headers: 19 | Content-Length: 20 | - '78' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_fr9bff60c8f2d435cf52f5787b759ec66b31db95; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | Vary: 33 | - Accept-Encoding 34 | X-Host: 35 | - blm-web-107 36 | status: 37 | code: 200 38 | message: OK 39 | version: 1 40 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_playlist_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/playlist/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"DataException","message":"no data","code":800}}' 18 | headers: 19 | Content-Length: 20 | - '65' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_frf7420c4cdbc18d81be11c36dd3d145470a54e5; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | X-Host: 33 | - blm-web-25 34 | status: 35 | code: 200 36 | message: OK 37 | version: 1 38 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_podcast_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.24.0 13 | method: GET 14 | uri: https://api.deezer.com/podcast/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"Exception","message":"An error has occured"}}' 18 | headers: 19 | Content-Length: 20 | - '63' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Thu, 20 Aug 2020 09:27:04 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_fr6d1ba4e79fef242c32077dfb30454763c64dd0; expires=Tue, 31 | 16-Feb-2021 09:27:04 GMT; Max-Age=15552000; path=/;SameSite=None; domain=.deezer.com; 32 | secure; HttpOnly 33 | X-Content-Type-Options: 34 | - nosniff 35 | X-Host: 36 | - blm-web-139 37 | status: 38 | code: 200 39 | message: OK 40 | version: 1 41 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_radio_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/radio/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"ParameterException","message":"Wrong parameter","code":500}}' 18 | headers: 19 | Content-Length: 20 | - '78' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_fr0aced95862b3fb5db16f49c7639d7b73907eaf; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | Vary: 33 | - Accept-Encoding 34 | X-Host: 35 | - blm-web-10 36 | status: 37 | code: 200 38 | message: OK 39 | version: 1 40 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_track_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/track/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"DataException","message":"no data","code":800}}' 18 | headers: 19 | Content-Length: 20 | - '65' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_fr01907354058fa9c07a8c76911d7bad87bcc54a; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | X-Host: 33 | - blm-web-110 34 | status: 35 | code: 200 36 | message: OK 37 | version: 1 38 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_no_user_raise.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.22.0 13 | method: GET 14 | uri: https://api.deezer.com/user/-1 15 | response: 16 | body: 17 | string: '{"error":{"type":"DataException","message":"no data","code":800}}' 18 | headers: 19 | Content-Length: 20 | - '65' 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Date: 24 | - Wed, 25 Sep 2019 21:04:30 GMT 25 | P3P: 26 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 27 | Server: 28 | - Apache 29 | Set-Cookie: 30 | - dzr_uniq_id=dzr_uniq_id_frb04041173ecc934112f2f6e26fb124dd0ca08c; expires=Mon, 31 | 23-Mar-2020 21:04:30 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 32 | X-Host: 33 | - blm-web-149 34 | status: 35 | code: 200 36 | message: OK 37 | version: 1 38 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_remove_user_album.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.26.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/albums?album_id=302127&access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | P3P: 40 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-25 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_remove_user_artist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.26.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/artists?artist_id=243&access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Date: 40 | - Sat, 09 Oct 2021 17:43:32 GMT 41 | Expires: 42 | - Thu, 19 Nov 1981 08:52:00 GMT 43 | P3P: 44 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 45 | Pragma: 46 | - no-cache 47 | Server: 48 | - Apache 49 | Set-Cookie: 50 | - dzr_uniq_id=dzr_uniq_id_frd7b2eb37b1a3451792ba47c2c45505db432cc6; expires=Thu, 51 | 07-Apr-2022 17:43:32 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure; 52 | HttpOnly; SameSite=None 53 | X-Content-Type-Options: 54 | - nosniff 55 | X-Host: 56 | - blm-web-52 57 | x-org: 58 | - FR 59 | status: 60 | code: 200 61 | message: OK 62 | version: 1 63 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_remove_user_following.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.28.1 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=2640689 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-107 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_remove_user_playlist.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.29.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/playlists?access_token=dummy&playlist_id=8749345882 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-152 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_remove_user_track.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '0' 13 | User-Agent: 14 | - python-requests/2.26.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/tracks?track_id=1374789602&access_token=dummy 17 | response: 18 | body: 19 | string: 'true' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - 'true' 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - '86400' 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - '4' 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | P3P: 40 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-76 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_request_404.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.26.0 13 | method: GET 14 | uri: https://api.deezer.com/does-not-exists 15 | response: 16 | body: 17 | string: '{"error":"Not Found"}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Connection: 30 | - keep-alive 31 | Content-Length: 32 | - '109' 33 | Content-Type: 34 | - application/json; charset=utf-8 35 | P3P: 36 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 37 | Server: 38 | - Apache 39 | Vary: 40 | - Accept-Encoding 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Host: 44 | - blm-web-149 45 | x-org: 46 | - FR 47 | status: 48 | code: 404 49 | message: OK 50 | version: 1 51 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_with_language_header[fr].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - gzip, deflate 9 | Accept-Language: 10 | - fr 11 | Connection: 12 | - keep-alive 13 | User-Agent: 14 | - python-requests/2.23.0 15 | method: GET 16 | uri: https://api.deezer.com/genre/52 17 | response: 18 | body: 19 | string: !!binary | 20 | H4sIAAAAAAAAA7WSwWrDMAyGX2X4XNuq0zhrrnsNQ3FtJfWoXRM74LXs3eeEHToYvUVCAsH/8yGh 21 | B3GW9K3YkaA9kp58XHRIt/A2TDqoGQA77RKSHYnO5HlaJJecY+oVV1xHxyziHSdmbl7xEcOEirdC 22 | cef1+GQ7Ja+v1z9mpMaGRFdhYvZeJxYw/1qT4t4lU2VgUYLeCzNIgwMYtI00x3PTyU4cmkPFydJK 23 | CmvQd6A12Wccn+AerZv9NnTRQqn1kn9240arA5RaL+Flo7PvK7Es7V96/orLr6wfQb5/AF3HVF5n 24 | AgAA 25 | headers: 26 | Connection: 27 | - keep-alive 28 | Content-Encoding: 29 | - gzip 30 | Content-Length: 31 | - '231' 32 | Content-Type: 33 | - application/json; charset=utf-8 34 | Date: 35 | - Wed, 01 Jul 2020 20:58:03 GMT 36 | P3P: 37 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 38 | Server: 39 | - Apache 40 | Set-Cookie: 41 | - dzr_uniq_id=dzr_uniq_id_fr841e38ada3b49a16246d33a56ad063c872255c; expires=Mon, 42 | 28-Dec-2020 20:58:03 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 43 | Vary: 44 | - Accept-Encoding 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-103 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestClient.test_with_language_header[ja].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - gzip, deflate 9 | Accept-Language: 10 | - ja 11 | Connection: 12 | - keep-alive 13 | User-Agent: 14 | - python-requests/2.23.0 15 | method: GET 16 | uri: https://api.deezer.com/genre/52 17 | response: 18 | body: 19 | string: !!binary | 20 | H4sIAAAAAAAAA7WS0WrEIBBF/8XnNRpdTZtvEUoyTlLLmpVoIN3Sf6/awrZQ9i3KXEZmrgdlPoiz 21 | pFfiRJbBI+mJ2SS3qihC0UkWhbbmY9Gxq1V5r472OycnEhykbS0XvaYUYm+YYUNwjUW84drA1Rs2 22 | 47KiYUoY5vww4932Ev1wufwxIwW7RFobY2Nv+dQsmH6s0TDvIuQ2blHzoRUwacCJA1qp4XmUne7E 23 | WZ4zTu9KU14XfeI07+YtzL/gHq3b/DF0ofie4yF/dPNBT+d8z/EQvh/07W0m7kX+paf3UGalTgT5 24 | /AL7LFjrjQIAAA== 25 | headers: 26 | Connection: 27 | - keep-alive 28 | Content-Encoding: 29 | - gzip 30 | Content-Length: 31 | - '238' 32 | Content-Type: 33 | - application/json; charset=utf-8 34 | Date: 35 | - Wed, 01 Jul 2020 20:58:03 GMT 36 | P3P: 37 | - policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA" 38 | Server: 39 | - Apache 40 | Set-Cookie: 41 | - dzr_uniq_id=dzr_uniq_id_fr6bba983c91acd3a9e18c35ffdf5c371e512a4a; expires=Mon, 42 | 28-Dec-2020 20:58:03 GMT; Max-Age=15552000; path=/; domain=.deezer.com; secure 43 | Vary: 44 | - Accept-Encoding 45 | X-Content-Type-Options: 46 | - nosniff 47 | X-Host: 48 | - blm-web-159 49 | x-org: 50 | - FR 51 | status: 52 | code: 200 53 | message: OK 54 | version: 1 55 | -------------------------------------------------------------------------------- /tests/cassettes/TestPaginatedList.test_repr_empty.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.28.1 13 | method: GET 14 | uri: https://api.deezer.com/search/artist?q=something+very+complicated+without+results 15 | response: 16 | body: 17 | string: '{"data":[],"total":0}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Connection: 30 | - keep-alive 31 | Content-Length: 32 | - '21' 33 | Content-Type: 34 | - application/json; charset=utf-8 35 | Server: 36 | - Apache 37 | Strict-Transport-Security: 38 | - max-age=31536000 39 | X-Content-Type-Options: 40 | - nosniff 41 | X-Host: 42 | - blm-web-54 43 | x-org: 44 | - FR 45 | status: 46 | code: 200 47 | message: OK 48 | version: 1 49 | -------------------------------------------------------------------------------- /tests/cassettes/TestPaginatedList.test_repr_little_results.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.28.1 13 | method: GET 14 | uri: https://api.deezer.com/search/artist?q=rouquine 15 | response: 16 | body: 17 | string: '{"data":[{"id":96110972,"name":"Rouquine","link":"https:\/\/www.deezer.com\/artist\/96110972","picture":"https:\/\/api.deezer.com\/artist\/96110972\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/7d0fc155b30dcb43c6c5085afaf45d6d\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/7d0fc155b30dcb43c6c5085afaf45d6d\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/7d0fc155b30dcb43c6c5085afaf45d6d\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/7d0fc155b30dcb43c6c5085afaf45d6d\/1000x1000-000000-80-0-0.jpg","nb_album":8,"nb_fan":3107,"radio":true,"tracklist":"https:\/\/api.deezer.com\/artist\/96110972\/top?limit=50","type":"artist"},{"id":174941387,"name":"Rouquined","link":"https:\/\/www.deezer.com\/artist\/174941387","picture":"https:\/\/api.deezer.com\/artist\/174941387\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3ba24d95905d77ea43ecd27385121c7c\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3ba24d95905d77ea43ecd27385121c7c\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3ba24d95905d77ea43ecd27385121c7c\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3ba24d95905d77ea43ecd27385121c7c\/1000x1000-000000-80-0-0.jpg","nb_album":1,"nb_fan":2,"radio":true,"tracklist":"https:\/\/api.deezer.com\/artist\/174941387\/top?limit=50","type":"artist"}],"total":2}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Connection: 30 | - keep-alive 31 | Content-Length: 32 | - '1596' 33 | Content-Type: 34 | - application/json; charset=utf-8 35 | Server: 36 | - Apache 37 | Strict-Transport-Security: 38 | - max-age=31536000 39 | Vary: 40 | - Accept-Encoding 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Host: 44 | - blm-web-03 45 | x-org: 46 | - FR 47 | status: 48 | code: 200 49 | message: OK 50 | version: 1 51 | -------------------------------------------------------------------------------- /tests/cassettes/TestPaginatedList.test_total.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - '*/*' 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.26.0 13 | method: GET 14 | uri: https://api.deezer.com/artist/27/albums?limit=1 15 | response: 16 | body: 17 | string: '{"data":[{"id":8244118,"title":"Human After All (Remixes)","link":"https:\/\/www.deezer.com\/album\/8244118","cover":"https:\/\/api.deezer.com\/album\/8244118\/image","cover_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/f6a4dbf47cb8828c281ed4e63364f99e\/56x56-000000-80-0-0.jpg","cover_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/f6a4dbf47cb8828c281ed4e63364f99e\/250x250-000000-80-0-0.jpg","cover_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/f6a4dbf47cb8828c281ed4e63364f99e\/500x500-000000-80-0-0.jpg","cover_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/f6a4dbf47cb8828c281ed4e63364f99e\/1000x1000-000000-80-0-0.jpg","md5_image":"f6a4dbf47cb8828c281ed4e63364f99e","genre_id":113,"fans":29723,"release_date":"2005-03-20","record_type":"album","tracklist":"https:\/\/api.deezer.com\/album\/8244118\/tracks","explicit_lyrics":false,"type":"album"}],"total":32,"next":"https:\/\/api.deezer.com\/artist\/27\/albums?limit=1&index=1"}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - 'true' 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - '86400' 29 | Connection: 30 | - keep-alive 31 | Content-Length: 32 | - '977' 33 | Content-Type: 34 | - application/json; charset=utf-8 35 | Server: 36 | - Apache 37 | Vary: 38 | - Accept-Encoding 39 | X-Content-Type-Options: 40 | - nosniff 41 | X-Host: 42 | - blm-web-64 43 | x-org: 44 | - FR 45 | status: 46 | code: 200 47 | message: OK 48 | version: 1 49 | -------------------------------------------------------------------------------- /tests/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/browniebroke/deezer-python/fc941a3fbcdb3033aeadc814702ec7dcfe745d7e/tests/resources/__init__.py -------------------------------------------------------------------------------- /tests/resources/cassettes/TestArtist.test_attributes.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ["*/*"] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/artist/27 11 | response: 12 | body: 13 | { 14 | string: '{"id":27,"name":"Daft Punk","link":"https:\/\/www.deezer.com\/artist\/27","share":"https:\/\/www.deezer.com\/artist\/27?utm_source=deezer&utm_content=artist-27&utm_term=0_1549975181&utm_medium=web","picture":"https:\/\/api.deezer.com\/artist\/27\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/f2bc007e9133c946ac3c3907ddc5d2ea\/1000x1000-000000-80-0-0.jpg","nb_album":32,"nb_fan":3677954,"radio":true,"tracklist":"https:\/\/api.deezer.com\/artist\/27\/top?limit=50","type":"artist"}', 15 | } 16 | headers: 17 | Content-Length: ["883"] 18 | Content-Type: [application/json; charset=utf-8] 19 | Date: ["Tue, 12 Feb 2019 12:39:41 GMT"] 20 | P3P: [ 21 | policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 22 | STA", 23 | ] 24 | Server: [Apache] 25 | Set-Cookie: [ 26 | "dzr_uniq_id=dzr_uniq_id_fr4fb23a88184858382fb248866f863e9b8f3bd3; 27 | expires=Sun, 11-Aug-2019 12:39:41 GMT; Max-Age=15552000; path=/; domain=.deezer.com", 28 | ] 29 | Vary: [Accept-Encoding] 30 | X-Host: [blm-web-80] 31 | status: { code: 200, message: OK } 32 | version: 1 33 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestEditorial.test_attributes.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.27.1 13 | method: GET 14 | uri: https://api.deezer.com/editorial/106 15 | response: 16 | body: 17 | string: '{"id":106,"name":"Electro","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/1000x1000-000000-80-0-0.jpg","type":"editorial"}' 18 | headers: 19 | Access-Control-Allow-Credentials: 20 | - "true" 21 | Access-Control-Allow-Headers: 22 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 23 | Access-Control-Allow-Methods: 24 | - POST, GET, OPTIONS, DELETE, PUT 25 | Access-Control-Expose-Headers: 26 | - Location 27 | Access-Control-Max-Age: 28 | - "86400" 29 | Content-Length: 30 | - "671" 31 | Content-Type: 32 | - application/json; charset=utf-8 33 | Server: 34 | - Apache 35 | Vary: 36 | - Accept-Encoding,Origin 37 | X-Content-Type-Options: 38 | - nosniff 39 | X-Host: 40 | - blm-web-137 41 | status: 42 | code: 200 43 | message: OK 44 | version: 1 45 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestEpisode.test_access_non_inferable_field.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.27.1 13 | method: GET 14 | uri: https://api.deezer.com/episode/343457312 15 | response: 16 | body: 17 | string: 18 | '{"id":343457312,"title":"Stuart Hogg and the GOAT","description":"There 19 | are big questions to ponder on this week''s pod; who hacked Chris'' Twitter 20 | account? Was England''s victory over South Africa the best of Eddie Jones'' 21 | reign? And has New Zealand''s aura been vaporised? Danny, Ugo and Chris also 22 | chat to Scotland captain Stuart Hogg about his new teeth, breaking the try 23 | record and the importance of Chris Harris both on and off the pitch. They 24 | also look to the Six Nations with the northern hemisphere teams ending the 25 | autumn on a high.","available":true,"release_date":"2021-11-22 23:42:00","duration":3254,"link":"https:\/\/www.deezer.com\/episode\/343457312","share":"https:\/\/www.deezer.com\/episode\/343457312?utm_source=deezer&utm_content=episode-343457312&utm_term=0_1658864197&utm_medium=web","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/1000x1000-000000-80-0-0.jpg","podcast":{"id":700072,"title":"Rugby 26 | Union Weekly","link":"https:\/\/www.deezer.com\/show\/700072","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/f2a011a6e87bd793c8e77240ab36dfaa\/1000x1000-000000-80-0-0.jpg","type":"podcast"},"type":"episode"}' 27 | headers: 28 | Access-Control-Allow-Credentials: 29 | - "true" 30 | Access-Control-Allow-Headers: 31 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 32 | Access-Control-Allow-Methods: 33 | - POST, GET, OPTIONS, DELETE, PUT 34 | Access-Control-Expose-Headers: 35 | - Location 36 | Access-Control-Max-Age: 37 | - "86400" 38 | Connection: 39 | - keep-alive 40 | Content-Length: 41 | - "2188" 42 | Content-Type: 43 | - application/json; charset=utf-8 44 | Server: 45 | - Apache 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-11 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestEpisode.test_add_bookmark.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/episode/343457312/bookmark?access_token=dummy&offset=55 17 | response: 18 | body: 19 | string: '{"error":[],"results":true}' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "27" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-136 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestEpisode.test_as_dict.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.26.0 13 | method: GET 14 | uri: https://api.deezer.com/episode/343457312 15 | response: 16 | body: 17 | string: 18 | '{"id":343457312,"title":"Stuart Hogg and the GOAT","description":"There 19 | are big questions to ponder on this week''s pod; who hacked Chris'' Twitter 20 | account? Was England''s victory over South Africa the best of Eddie Jones'' 21 | reign? And has New Zealand''s aura been vaporised? Danny, Ugo and Chris also 22 | chat to Scotland captain Stuart Hogg about his new teeth, breaking the try 23 | record and the importance of Chris Harris both on and off the pitch. They 24 | also look to the Six Nations with the northern hemisphere teams ending the 25 | autumn on a high.","available":true,"release_date":"2021-11-22 23:42:00","duration":3254,"link":"https:\/\/www.deezer.com\/episode\/343457312","share":"https:\/\/www.deezer.com\/episode\/343457312?utm_source=deezer&utm_content=episode-343457312&utm_term=0_1638903611&utm_medium=web","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/1000x1000-000000-80-0-0.jpg","podcast":{"id":700072,"title":"Rugby 26 | Union Weekly","link":"https:\/\/www.deezer.com\/show\/700072","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/1000x1000-000000-80-0-0.jpg","type":"podcast"},"type":"episode"}' 27 | headers: 28 | Access-Control-Allow-Credentials: 29 | - "true" 30 | Access-Control-Allow-Headers: 31 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 32 | Access-Control-Allow-Methods: 33 | - POST, GET, OPTIONS, DELETE, PUT 34 | Access-Control-Expose-Headers: 35 | - Location 36 | Access-Control-Max-Age: 37 | - "86400" 38 | Connection: 39 | - keep-alive 40 | Content-Length: 41 | - "2188" 42 | Content-Type: 43 | - application/json; charset=utf-8 44 | Server: 45 | - Apache 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-128 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestEpisode.test_get_episode.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | User-Agent: 12 | - python-requests/2.26.0 13 | method: GET 14 | uri: https://api.deezer.com/episode/343457312 15 | response: 16 | body: 17 | string: 18 | '{"id":343457312,"title":"Stuart Hogg and the GOAT","description":"There 19 | are big questions to ponder on this week''s pod; who hacked Chris'' Twitter 20 | account? Was England''s victory over South Africa the best of Eddie Jones'' 21 | reign? And has New Zealand''s aura been vaporised? Danny, Ugo and Chris also 22 | chat to Scotland captain Stuart Hogg about his new teeth, breaking the try 23 | record and the importance of Chris Harris both on and off the pitch. They 24 | also look to the Six Nations with the northern hemisphere teams ending the 25 | autumn on a high.","available":true,"release_date":"2021-11-22 23:42:00","duration":3254,"link":"https:\/\/www.deezer.com\/episode\/343457312","share":"https:\/\/www.deezer.com\/episode\/343457312?utm_source=deezer&utm_content=episode-343457312&utm_term=0_1638903611&utm_medium=web","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/1000x1000-000000-80-0-0.jpg","podcast":{"id":700072,"title":"Rugby 26 | Union Weekly","link":"https:\/\/www.deezer.com\/show\/700072","picture":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/180x180-000000-80-0-0.jpg","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/talk\/1d7e05f8805be58e6dd5044cb2d9dfe5\/1000x1000-000000-80-0-0.jpg","type":"podcast"},"type":"episode"}' 27 | headers: 28 | Access-Control-Allow-Credentials: 29 | - "true" 30 | Access-Control-Allow-Headers: 31 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 32 | Access-Control-Allow-Methods: 33 | - POST, GET, OPTIONS, DELETE, PUT 34 | Access-Control-Expose-Headers: 35 | - Location 36 | Access-Control-Max-Age: 37 | - "86400" 38 | Connection: 39 | - keep-alive 40 | Content-Length: 41 | - "2188" 42 | Content-Type: 43 | - application/json; charset=utf-8 44 | Server: 45 | - Apache 46 | Vary: 47 | - Accept-Encoding 48 | X-Content-Type-Options: 49 | - nosniff 50 | X-Host: 51 | - blm-web-77 52 | x-org: 53 | - FR 54 | status: 55 | code: 200 56 | message: OK 57 | version: 1 58 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestEpisode.test_remove_bookmark.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/episode/343457312/bookmark?access_token=dummy 17 | response: 18 | body: 19 | string: '{"error":[],"results":true}' 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "27" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-171 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestGenre.test_attributes.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ["*/*"] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/genre/106 11 | response: 12 | body: 13 | { 14 | string: '{"id":106,"name":"Electro","picture":"https:\/\/api.deezer.com\/genre\/106\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/15df4502c1c58137dae5bdd1cc6f0251\/1000x1000-000000-80-0-0.jpg","type":"genre"}', 15 | } 16 | headers: 17 | Content-Length: ["602"] 18 | Content-Type: [application/json; charset=utf-8] 19 | Date: ["Tue, 12 Feb 2019 12:39:45 GMT"] 20 | P3P: [ 21 | policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 22 | STA", 23 | ] 24 | Server: [Apache] 25 | Set-Cookie: [ 26 | "dzr_uniq_id=dzr_uniq_id_fred510706cd44040e8bdf3ddb50fd7108ac0afc; 27 | expires=Sun, 11-Aug-2019 12:39:46 GMT; Max-Age=15552000; path=/; domain=.deezer.com", 28 | ] 29 | Vary: [Accept-Encoding] 30 | X-Host: [blm-web-53] 31 | status: { code: 200, message: OK } 32 | version: 1 33 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestPlaylist.test_mark_seen.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | Cookie: 14 | - _abck=565E04291DA5E6337D9F9D14FC40C1B8~-1~YAAQPZp6XGpoj/GHAQAAlXvS/wlX33ydP12Fg7D+VWQ2ah1reKSqutNYGgL3qlRmJU/GeuegAiRxTXKeJSJwjK8Xdeq54aybLayvOW0UNx/oqh3TQV6Jn+qvKtLen2KnyQgL97AP4Bla4MDqNdU6rCdHNgv3VH3moolvhHNp7ljNmCE+CbRuXEef4pe5ruFYvGdOKr3LeCVuZXLB6wGt/D4hWO8SNpK0CA6BZN5ZBrICiJdsCBqSTK+doh0N5N8iCQN+wzDLPWnsZ9vNPjvFm8rIzyYzdny1uPcueMMcN8aHMrl8PqlAu7zaPeGPmIA7rz3dmOGGjXSCaU9e3mLPsXIBgGG52JTruBgHKRl5poeDp9sBzrEsed8=~-1~-1~-1; 15 | bm_sz=4111DAE2A226B10FABFC2DB566349E04~YAAQPZp6XGtoj/GHAQAAlXvS/xPOwjfn4oiGbznFlpOCCnTA8ysZ+Xr0VAxHUYju83BiI4UUdQIQJflEy+HHJcNWOVPpmUD1zsFxoYL16J6mgFPrJUllun6RyOk+VkNpcQk4RYkMdhEPNT1H4LK69tLTBIpUb+ZyrIZLIEiRMuoQnJD8r8cM5Zy0lRqi+pbC6R3KmpXtUk6Jd88XC4m7efQBE3SkLYEnnRxMMGixXu5s23cGlOUEr3B8Z0WaI+9hJm3upoeG9J5jJYGN8UacDnWh/c4fgdTBnh9A9vUMVTaLaf0=~3682617~4599875; 16 | dzr_uniq_id=dzr_uniq_id_fra34de71ea95540560cdd2cf4dd3b8dd8581045 17 | User-Agent: 18 | - python-requests/2.30.0 19 | method: POST 20 | uri: https://api.deezer.com/playlist/9200461/seen?access_token=dummy 21 | response: 22 | body: 23 | string: "true" 24 | headers: 25 | Access-Control-Allow-Credentials: 26 | - "true" 27 | Access-Control-Allow-Headers: 28 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 29 | Access-Control-Allow-Methods: 30 | - POST, GET, OPTIONS, DELETE, PUT 31 | Access-Control-Expose-Headers: 32 | - Location 33 | Access-Control-Max-Age: 34 | - "86400" 35 | Cache-Control: 36 | - no-store, no-cache, must-revalidate 37 | Connection: 38 | - keep-alive 39 | Content-Length: 40 | - "4" 41 | Content-Type: 42 | - application/json; charset=utf-8 43 | Expires: 44 | - Thu, 19 Nov 1981 08:52:00 GMT 45 | Pragma: 46 | - no-cache 47 | Server: 48 | - Apache 49 | Strict-Transport-Security: 50 | - max-age=31536000; includeSubDomains 51 | X-Content-Type-Options: 52 | - nosniff 53 | X-Host: 54 | - blm-web-66 55 | x-org: 56 | - FR 57 | status: 58 | code: 200 59 | message: OK 60 | version: 1 61 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestPlaylist.test_reorder_tracks.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | Cookie: 14 | - _abck=CFA3BF239B437A173E8C31CC012F1C20~-1~YAAQpa0TAjQiFtaHAQAAu4o24glDMHG4PaEIDepbSLH/55Uz+NppSaEZeGbJQhkbtE9msKl/Nk5mdzL0Qu3to6+IZs0sdMNYdvkDP3gM/85KNhMsmpQjmOseCBEStpRXICosROK2b9B1BwPaAY0ax563VTAlCHt6knJxbCf0VxN7gpvCiTvqGgDSsgsZ5c+LkA0FmuZKaeMorjNVpyy/b3uaKjBgSBXeqayCd+99pkndxaKFV4hij7TrRJzF/mnN+rDbxdI/XjlTYNM5LKWerpMnvT45Eeq5Egxj081GkDYE4OzORDh5LI6TdMuEM79VyRczyD+7lc2KFk32ogUwbc/knEu5LfEEnmU42NJH25Y4RSfV6Ml3PSQ=~-1~-1~-1; 15 | bm_sz=11E9AC9FA1A93DD24DDF4E94D2546BD2~YAAQpa0TAjUiFtaHAQAAu4o24hOGQuWWkiwjkdvbfzA8LSezTO3KRkIZjukMWgJeei7d+BBw+ecJhoZy3xwQQDayUVdLq1gg2vetEuX12UelwA/SwyX7brUsxEC1UKey8WaE49Hmikjb4NicJghf1hmJSU46HTEubZcKjcvL1vKU7ny2S5cbwXCeIVqdG35qS7nMaMriQkyayJb2gG8GZbfYAQFUoX+E5IeyDUIB6PyEBQScKvr/WVD7aP49njLR3fKw/bNc+tB6c6vCSazR2sOjlemjmbQysPbLY9BrytIIu50=~4605510~4273218; 16 | dzr_uniq_id=dzr_uniq_id_fr34aa8e2432bfad9b22f64b40741346452eb32b 17 | User-Agent: 18 | - python-requests/2.29.0 19 | method: POST 20 | uri: https://api.deezer.com/playlist/11336462844/tracks?access_token=dummy&order=79875044%2C79875050%2C142986210 21 | response: 22 | body: 23 | string: "true" 24 | headers: 25 | Access-Control-Allow-Credentials: 26 | - "true" 27 | Access-Control-Allow-Headers: 28 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 29 | Access-Control-Allow-Methods: 30 | - POST, GET, OPTIONS, DELETE, PUT 31 | Access-Control-Expose-Headers: 32 | - Location 33 | Access-Control-Max-Age: 34 | - "86400" 35 | Cache-Control: 36 | - no-store, no-cache, must-revalidate 37 | Connection: 38 | - keep-alive 39 | Content-Length: 40 | - "4" 41 | Content-Type: 42 | - application/json; charset=utf-8 43 | Expires: 44 | - Thu, 19 Nov 1981 08:52:00 GMT 45 | Pragma: 46 | - no-cache 47 | Server: 48 | - Apache 49 | Strict-Transport-Security: 50 | - max-age=31536000; includeSubDomains 51 | X-Content-Type-Options: 52 | - nosniff 53 | X-Host: 54 | - blm-web-157 55 | x-org: 56 | - FR 57 | status: 58 | code: 200 59 | message: OK 60 | version: 1 61 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestRadio.test_attributes.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: ["*/*"] 6 | Accept-Encoding: [identity] 7 | Connection: [keep-alive] 8 | User-Agent: [python-requests/2.21.0] 9 | method: GET 10 | uri: https://api.deezer.com/radio/23261 11 | response: 12 | body: 13 | { 14 | string: 15 | '{"id":23261,"title":"Telegraph Classical","description":"Telegraph 16 | Classical","share":"https:\/\/www.deezer.com\/mixes\/genre\/23261?utm_source=deezer&utm_content=mixes-genre-23261&utm_term=0_1549975186&utm_medium=web","picture":"https:\/\/api.deezer.com\/radio\/23261\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/misc\/aad5b7e9ecd6e72e6171c9e65ddf150d\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/radio\/23261\/tracks","type":"radio"}', 17 | } 18 | headers: 19 | Content-Length: ["857"] 20 | Content-Type: [application/json; charset=utf-8] 21 | Date: ["Tue, 12 Feb 2019 12:39:46 GMT"] 22 | P3P: [ 23 | policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM 24 | STA", 25 | ] 26 | Server: [Apache] 27 | Set-Cookie: [ 28 | "dzr_uniq_id=dzr_uniq_id_fra686a3da0c1a4018414f369f27dc87b06d6386; 29 | expires=Sun, 11-Aug-2019 12:39:46 GMT; Max-Age=15552000; path=/; domain=.deezer.com", 30 | ] 31 | Vary: [Accept-Encoding] 32 | X-Host: [blm-web-118] 33 | status: { code: 200, message: OK } 34 | version: 1 35 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_add_artist_id.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/artists?access_token=dummy&artist_id=27 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-200 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_add_artist_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/artists?access_token=dummy&artist_id=27 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-174 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_add_playlist_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/playlists?access_token=dummy&playlist_id=4460913144 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-138 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_add_track_id.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/tracks?access_token=dummy&track_id=3135556 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-15 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_add_track_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/tracks?access_token=dummy&track_id=3135556 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-174 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_follow_by_id_fail.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=27 17 | response: 18 | body: 19 | string: "false" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "5" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-189 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_follow_by_id_ok.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=315641 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-182 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_follow_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: POST 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=315641 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-200 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_get_followers.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Cookie: 12 | - _abck=45944847D4E8C9EBF6E936C8686DF26C~-1~YAAQZ+57XLzyKFKEAQAAGKpAVAjrbfONERrWjrjlO/byler0D/HfPh30W0MnLfhvXfvb6KbCUyom6EdAEuguH5Xp/hRbQ7Hw9BJAlVHwyoCVS/DCCgxyJ551LLbNta7oc6yI0EfX9m9MQuOV0E+4AXZUWw0Lczc7aHTj3SqG2J9Wf/80Qkxs0Y8gioCb9j3SI2oB/wrqPcA/rDbP77CGpMFmujmDGJw4rBOBfJau7HTSZzftX1msjLlRzbD0ah+EiTsmFB1t3ihx38Ly7HcVgex08ytYU83Hbmq7XgX4l9ZDx15RFHLGybdMA6I7ADMw0Dg4GAevTtbSk3KqHXSub2bmdcNOOnbxJDZKlePg6qH5+LOnnTgf5M0=~-1~-1~-1; 13 | bm_sz=38BE3B745B4CAA0D73A6F30D5A248230~YAAQZ+57XL3yKFKEAQAAGKpAVBFRRvpNh3xDNDdsk9QsrdNu92Ly5XrdHOEIitnmhLZuHP0NW68L6mJ/TZuADkKzI+DweKAitfmLD/rG0vAq5odmQ1WOEn9Xd4jty34eZ3h/Xd7X5GuGsOSN4lOWam+gBHIwej/ST6oOPmqZUvlrk2sYraRs1KArN/YHAuA5ihrCGtMhzMkHhVY0eR8LZydGMSKdASDkUgGXRmeWlfLb/wuW7wPPDqttciKZU6NcxUmW8ylGPLmW94k3RbhlJ7EkgC5IZ8sX5cqcXyRd2MNy5II=~4272449~3486003; 14 | dzr_uniq_id=dzr_uniq_id_fr635bd4a6b85c0fd6edc466903bbdf627c9b23b 15 | User-Agent: 16 | - python-requests/2.28.1 17 | method: GET 18 | uri: https://api.deezer.com/user/359622/followers 19 | response: 20 | body: 21 | string: 22 | '{"data":[{"id":5100920542,"name":"John Doe","picture":"https:\/\/api.deezer.com\/user\/5100920542\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5100920542\/flow","type":"user"},{"id":5139545882,"name":"Jane 23 | Doe","picture":"https:\/\/api.deezer.com\/user\/5139545882\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5139545882\/flow","type":"user"}],"total":2}' 24 | headers: 25 | Access-Control-Allow-Credentials: 26 | - "true" 27 | Access-Control-Allow-Headers: 28 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 29 | Access-Control-Allow-Methods: 30 | - POST, GET, OPTIONS, DELETE, PUT 31 | Access-Control-Expose-Headers: 32 | - Location 33 | Access-Control-Max-Age: 34 | - "86400" 35 | Cache-Control: 36 | - no-store, no-cache, must-revalidate 37 | Connection: 38 | - keep-alive 39 | Content-Length: 40 | - "1122" 41 | Content-Type: 42 | - application/json; charset=utf-8 43 | Expires: 44 | - Thu, 19 Nov 1981 08:52:00 GMT 45 | Pragma: 46 | - no-cache 47 | Server: 48 | - Apache 49 | Strict-Transport-Security: 50 | - max-age=31536000 51 | Vary: 52 | - Accept-Encoding 53 | X-Content-Type-Options: 54 | - nosniff 55 | X-Host: 56 | - blm-web-40 57 | x-org: 58 | - FR 59 | status: 60 | code: 200 61 | message: OK 62 | version: 1 63 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_get_followings.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Cookie: 12 | - _abck=92F0B44126E08335BF209FAB29874D28~-1~YAAQvMRNF900MVGEAQAA4pQjVAg+WiQRHB9aAzcZlUdYi75fGzoqQnXEnYxrskqfLVBJIjx+OPM0vSVIUhE0AxerwBynrFCZf++7IQOmbDfpohZVy0aOsF+NaOKYWHWrvCUBWZO8p08TfcqeORdOoApqOM2e3pPr38+yatyG8q0SZ+HNFEXqNzGC2jDnyeRxFPxmIgoY92aDc38uMsI2pQYoahrzIdRaPdJvUtBsNUxxyZObA0CpjBEeJFHzp1gk7yyARSGCSFUc55nhDnwn59hXsRbGevOjjCN3Qdd9fnv36VuIPpmD9CasgYXJcvGG6VnwmHwPfwfS2MnfQqQ4bg4kKpwKwFg5iyyw15h5I8scTRK6lIFVZPA=~-1~-1~-1; 13 | bm_sz=D36F49AF9EE838F86F0B325A94A1D684~YAAQvMRNF940MVGEAQAA4pQjVBHlSS3j4GQUyL4kITCNuySR31jcCh77CMBMX9bOa8bP1pJexjKFE47KqsTtsIXfUhuWORdO9h3QGjlCKk1+vPXF2uKOuswyli9bMlc4Oe2reBZbdhkkIP34skpFRJgnZRqkEB0i8QLWXeGoS+T8UZWik7+xOfxmYYpNkncwHAguoq/GjlcZsxgbrg+SrUgGViHeOtUY9BCH/n/g6L4qBa6n5bdvIDsM5Oxp3LMB6JfreCkiPsc2GzYRLO7fBTppnwMSsYlnxFqum70oRVcQRIM=~3425606~3551797; 14 | dzr_uniq_id=dzr_uniq_id_frb0cd269a6ca6ec0cc44f1fe8876205d1827c70 15 | User-Agent: 16 | - python-requests/2.28.1 17 | method: GET 18 | uri: https://api.deezer.com/user/359622/followings 19 | response: 20 | body: 21 | string: 22 | '{"data":[{"id":5100920542,"name":"John Doe","picture":"https:\/\/api.deezer.com\/user\/5100920542\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5100920542\/flow","type":"user"},{"id":5139545882,"name":"Jane 23 | Doe","picture":"https:\/\/api.deezer.com\/user\/5139545882\/image","picture_small":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/e-cdns-images.dzcdn.net\/images\/user\/\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/user\/5139545882\/flow","type":"user"}],"total":2}' 24 | headers: 25 | Access-Control-Allow-Credentials: 26 | - "true" 27 | Access-Control-Allow-Headers: 28 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 29 | Access-Control-Allow-Methods: 30 | - POST, GET, OPTIONS, DELETE, PUT 31 | Access-Control-Expose-Headers: 32 | - Location 33 | Access-Control-Max-Age: 34 | - "86400" 35 | Connection: 36 | - keep-alive 37 | Content-Length: 38 | - "1122" 39 | Content-Type: 40 | - application/json; charset=utf-8 41 | Server: 42 | - Apache 43 | Strict-Transport-Security: 44 | - max-age=31536000 45 | Vary: 46 | - Accept-Encoding 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-37 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_remove_artist_id.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/artists?access_token=dummy&artist_id=27 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-135 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_remove_artist_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/artists?access_token=dummy&artist_id=27 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-92 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_remove_playlist_by_id.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/playlists?access_token=dummy&playlist_id=3110421322 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-17 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_remove_playlist_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/playlists?access_token=dummy&playlist_id=4460913144 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-40 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_remove_track_id.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/tracks?access_token=dummy&track_id=3135556 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-173 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_remove_track_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/tracks?access_token=dummy&track_id=3135556 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-192 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_unfollow_by_id.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=315641 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-158 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/cassettes/TestUser.test_unfollow_obj.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: null 4 | headers: 5 | Accept: 6 | - "*/*" 7 | Accept-Encoding: 8 | - identity 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - "0" 13 | User-Agent: 14 | - python-requests/2.30.0 15 | method: DELETE 16 | uri: https://api.deezer.com/user/me/followings?access_token=dummy&user_id=315641 17 | response: 18 | body: 19 | string: "true" 20 | headers: 21 | Access-Control-Allow-Credentials: 22 | - "true" 23 | Access-Control-Allow-Headers: 24 | - X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding 25 | Access-Control-Allow-Methods: 26 | - POST, GET, OPTIONS, DELETE, PUT 27 | Access-Control-Expose-Headers: 28 | - Location 29 | Access-Control-Max-Age: 30 | - "86400" 31 | Cache-Control: 32 | - no-store, no-cache, must-revalidate 33 | Connection: 34 | - keep-alive 35 | Content-Length: 36 | - "4" 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | Expires: 40 | - Thu, 19 Nov 1981 08:52:00 GMT 41 | Pragma: 42 | - no-cache 43 | Server: 44 | - Apache 45 | Strict-Transport-Security: 46 | - max-age=31536000; includeSubDomains 47 | X-Content-Type-Options: 48 | - nosniff 49 | X-Host: 50 | - blm-web-177 51 | x-org: 52 | - FR 53 | status: 54 | code: 200 55 | message: OK 56 | version: 1 57 | -------------------------------------------------------------------------------- /tests/resources/test_album.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestAlbum: 11 | def test_basic(self, client): 12 | """Test basic Album resource.""" 13 | album = client.get_album(302127) 14 | assert hasattr(album, "title") 15 | assert repr(album) == "" 16 | 17 | artist = album.get_artist() 18 | assert isinstance(artist, deezer.Artist) 19 | assert repr(artist) == "" 20 | 21 | def test_get_tracks(self, client): 22 | album = client.get_album(302127) 23 | 24 | # tests pagination 25 | tracks = album.get_tracks() 26 | assert isinstance(tracks, deezer.PaginatedList) 27 | track = tracks[0] 28 | assert isinstance(track, deezer.Track) 29 | assert repr(track) == "" 30 | assert len(tracks) == 14 31 | 32 | def test_contributors(self, client): 33 | album = client.get_album(302128) 34 | 35 | contributors = album.contributors 36 | assert isinstance(contributors, list) 37 | assert len(contributors) == 2 38 | assert all(isinstance(c, deezer.Artist) for c in contributors) 39 | assert [c.id for c in contributors] == [123021, 6159602] 40 | 41 | def test_as_dict(self, client): 42 | album = client.get_album(302127) 43 | album_dict = album.as_dict() 44 | assert album_dict["id"] == 302127 45 | assert album_dict["release_date"] == "2001-03-07" 46 | -------------------------------------------------------------------------------- /tests/resources/test_artist.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestArtist: 11 | @pytest.fixture() 12 | def daft_punk(self, client): 13 | return client.get_artist(27) 14 | 15 | def test_attributes(self, daft_punk): 16 | assert hasattr(daft_punk, "name") 17 | assert isinstance(daft_punk, deezer.Artist) 18 | assert repr(daft_punk) == "" 19 | 20 | def test_get_albums(self, daft_punk): 21 | albums = daft_punk.get_albums() 22 | assert isinstance(albums, deezer.PaginatedList) 23 | album = albums[0] 24 | assert isinstance(album, deezer.Album) 25 | assert repr(album) == "" 26 | assert len(albums) == 32 27 | 28 | def test_get_top(self, daft_punk): 29 | tracks = daft_punk.get_top() 30 | assert isinstance(tracks, deezer.PaginatedList) 31 | track = tracks[0] 32 | assert isinstance(track, deezer.Track) 33 | assert repr(track) == "" 34 | assert len(tracks) == 100 35 | 36 | def test_get_radio(self, daft_punk): 37 | tracks = daft_punk.get_radio() 38 | assert isinstance(tracks, list) 39 | assert len(tracks) == 25 40 | track = tracks[0] 41 | assert isinstance(track, deezer.Track) 42 | assert repr(track) == "" 43 | assert any(track_.artist.name != daft_punk.name for track_ in tracks) 44 | 45 | def test_get_related(self, daft_punk): 46 | related_artists = daft_punk.get_related() 47 | assert isinstance(related_artists, deezer.PaginatedList) 48 | related_artist = related_artists[0] 49 | assert isinstance(related_artist, deezer.Artist) 50 | assert repr(related_artist) == "" 51 | assert len(related_artists) == 39 52 | 53 | def test_get_playlists(self, daft_punk): 54 | playlists = daft_punk.get_playlists() 55 | assert isinstance(playlists, deezer.PaginatedList) 56 | playlist = playlists[0] 57 | assert isinstance(playlist, deezer.Playlist) 58 | assert repr(playlist) == "" 59 | assert len(playlists) == 100 60 | -------------------------------------------------------------------------------- /tests/resources/test_chart.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestChart: 11 | @pytest.fixture() 12 | def chart(self, client): 13 | return client.get_chart(0) 14 | 15 | def test_get_tracks(self, chart): 16 | tracks = chart.get_tracks() 17 | assert isinstance(tracks, deezer.PaginatedList) 18 | track = tracks[0] 19 | assert isinstance(track, deezer.Track) 20 | assert repr(track) == "" 21 | assert len(tracks) == 10 22 | 23 | def test_get_artists(self, chart): 24 | artists = chart.get_artists() 25 | assert isinstance(artists, deezer.PaginatedList) 26 | artist = artists[0] 27 | assert isinstance(artist, deezer.Artist) 28 | assert repr(artist) == "" 29 | assert len(artists) == 10 30 | 31 | def test_get_albums(self, chart): 32 | albums = chart.get_albums() 33 | assert isinstance(albums, deezer.PaginatedList) 34 | album = albums[0] 35 | assert isinstance(album, deezer.Album) 36 | assert repr(album) == "" 37 | assert len(albums) == 10 38 | 39 | def test_get_playlists(self, chart): 40 | playlists = chart.get_playlists() 41 | assert isinstance(playlists, deezer.PaginatedList) 42 | playlist = playlists[0] 43 | assert isinstance(playlist, deezer.Playlist) 44 | assert repr(playlist) == "" 45 | assert len(playlists) == 10 46 | 47 | def test_get_podcasts(self, chart): 48 | podcasts = chart.get_podcasts() 49 | assert isinstance(podcasts, deezer.PaginatedList) 50 | podcast = podcasts[0] 51 | assert isinstance(podcast, deezer.Podcast) 52 | assert repr(podcast) == "" 53 | assert len(podcasts) == 10 54 | -------------------------------------------------------------------------------- /tests/resources/test_editorial.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestEditorial: 11 | @pytest.fixture 12 | def editorial(self, client): 13 | return client.get_editorial(106) 14 | 15 | def test_attributes(self, editorial): 16 | assert hasattr(editorial, "name") 17 | assert isinstance(editorial, deezer.Editorial) 18 | assert repr(editorial) == "" 19 | 20 | def test_get_selection(self, editorial): 21 | albums = editorial.get_selection() 22 | assert isinstance(albums, list) 23 | assert len(albums) == 10 24 | album = albums[0] 25 | assert isinstance(album, deezer.Album) 26 | assert album.title == "Terre Promise" 27 | 28 | def test_get_chart(self, editorial): 29 | charts = editorial.get_chart() 30 | assert isinstance(charts, deezer.Chart) 31 | 32 | assert isinstance(charts.tracks[0], deezer.Track) 33 | assert isinstance(charts.albums[0], deezer.Album) 34 | assert isinstance(charts.artists[0], deezer.Artist) 35 | assert isinstance(charts.playlists[0], deezer.Playlist) 36 | 37 | def test_get_releases(self, editorial): 38 | albums = editorial.get_releases() 39 | assert isinstance(albums, deezer.PaginatedList) 40 | album = albums[0] 41 | assert isinstance(album, deezer.Album) 42 | assert repr(album) == "" 43 | assert len(albums) == 199 44 | -------------------------------------------------------------------------------- /tests/resources/test_episode.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestEpisode: 11 | def test_get_episode(self, client): 12 | episode = client.get_episode(343457312) 13 | assert isinstance(episode, deezer.Episode) 14 | assert episode.title == "Stuart Hogg and the GOAT" 15 | 16 | def test_as_dict(self, client): 17 | """Test resource conversion to dict.""" 18 | episode = client.get_episode(343457312) 19 | episode_dict = episode.as_dict() 20 | assert episode_dict["id"] == 343457312 21 | assert episode_dict["release_date"] == "2021-11-22 23:42:00" 22 | 23 | def test_access_inferable_fields(self, client): 24 | """Accessing a missing inferable field doesn't do any API calls.""" 25 | episode = deezer.Episode( 26 | client, 27 | json={ 28 | "id": 343457312, 29 | "type": "episode", 30 | }, 31 | ) 32 | assert episode.link == "https://www.deezer.com/episode/343457312" 33 | assert episode.share == ( 34 | "https://www.deezer.com/episode/343457312?utm_source=deezer&utm_content=episode-343457312&utm_medium=web" 35 | ) 36 | 37 | def test_access_non_inferable_field(self, client): 38 | episode = deezer.Episode( 39 | client, 40 | json={ 41 | "id": 343457312, 42 | "type": "episode", 43 | }, 44 | ) 45 | assert episode.duration == 3254 46 | 47 | def test_add_bookmark(self, client_token): 48 | episode = deezer.Episode( 49 | client_token, 50 | json={ 51 | "id": 343457312, 52 | "type": "episode", 53 | }, 54 | ) 55 | result = episode.add_bookmark(55) 56 | assert result is True 57 | 58 | def test_remove_bookmark(self, client_token): 59 | episode = deezer.Episode( 60 | client_token, 61 | json={ 62 | "id": 343457312, 63 | "type": "episode", 64 | }, 65 | ) 66 | result = episode.remove_bookmark() 67 | assert result is True 68 | -------------------------------------------------------------------------------- /tests/resources/test_genre.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestGenre: 11 | @pytest.fixture 12 | def electro(self, client): 13 | return client.get_genre(106) 14 | 15 | def test_attributes(self, electro): 16 | assert hasattr(electro, "name") 17 | assert isinstance(electro, deezer.Genre) 18 | assert repr(electro) == "" 19 | 20 | def test_get_artists(self, electro): 21 | artists = electro.get_artists() 22 | assert isinstance(artists, list) 23 | assert len(artists) == 48 24 | artist = artists[0] 25 | assert isinstance(artist, deezer.Artist) 26 | assert repr(artist) == "" 27 | 28 | def test_get_podcasts(self, client): 29 | technology = client.get_genre(232) 30 | podcasts = technology.get_podcasts() 31 | assert isinstance(podcasts, deezer.PaginatedList) 32 | podcast = podcasts[0] 33 | assert isinstance(podcast, deezer.Podcast) 34 | assert repr(podcast) == "" 35 | assert len(podcasts) == 15 36 | 37 | def test_get_radios(self, electro): 38 | radios = electro.get_radios() 39 | assert isinstance(radios, list) 40 | assert len(radios) == 32 41 | radio = radios[0] 42 | assert isinstance(radio, deezer.Radio) 43 | assert repr(radio) == "" 44 | -------------------------------------------------------------------------------- /tests/resources/test_playlist.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestPlaylist: 11 | @pytest.fixture 12 | def playlist(self, client): 13 | return deezer.Playlist(client, json={"id": 9200461, "type": "playlist"}) 14 | 15 | def test_attributes(self, client): 16 | playlist = client.get_playlist(9200461) 17 | assert playlist.title == "Lounge Soirée" 18 | 19 | def test_get_tracks(self, playlist): 20 | tracks = playlist.get_tracks() 21 | assert isinstance(tracks, deezer.PaginatedList) 22 | first_track = tracks[0] 23 | assert isinstance(first_track, deezer.Track) 24 | assert first_track.title == "Otherwise" 25 | assert len(tracks) == 102 26 | 27 | def test_get_fans(self, playlist): 28 | fans = playlist.get_fans() 29 | assert isinstance(fans, deezer.PaginatedList) 30 | first_fan = fans[0] 31 | assert isinstance(first_fan, deezer.User) 32 | assert first_fan.name == "Fay22" 33 | assert len(fans) == 100 34 | 35 | def test_add_tracks(self, client_token): 36 | playlist = client_token.get_playlist(11015569602) 37 | # Test that we can add one track. 38 | result = playlist.add_tracks([79875054]) 39 | assert result is True 40 | # Test that we can add multiple tracks 41 | result = playlist.add_tracks([79875064, 79875044, 142986210]) 42 | assert result is True 43 | # Test that we can add tracks not using id's 44 | track = client_token.get_track(1724605597) 45 | result = playlist.add_tracks([track]) 46 | assert result is True 47 | 48 | def test_delete_tracks(self, client_token): 49 | playlist = client_token.get_playlist(11015569602) 50 | # Test that we can delete one track. 51 | result = playlist.delete_tracks([79875054]) 52 | assert result is True 53 | # Test that we can delete multiple tracks 54 | result = playlist.delete_tracks([79875064, 79875044, 142986210]) 55 | assert result is True 56 | # Test that we can add tracks not using id's 57 | track = client_token.get_track(1724605597) 58 | result = playlist.delete_tracks([track]) 59 | assert result is True 60 | 61 | def test_reorder_tracks(self, client_token): 62 | playlist = deezer.Playlist(client_token, json={"id": 11336462844}) 63 | result = playlist.reorder_tracks([79875044, 79875050, 142986210]) 64 | assert result is True 65 | 66 | def test_mark_seen(self, client_token): 67 | playlist = deezer.Playlist(client_token, json={"id": 9200461}) 68 | result = playlist.mark_seen() 69 | assert result is True 70 | -------------------------------------------------------------------------------- /tests/resources/test_podcast.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestPodcast: 11 | def test_get_episodes(self, client): 12 | podcast = client.get_podcast(699612) 13 | 14 | episodes = podcast.get_episodes() 15 | assert isinstance(episodes, deezer.PaginatedList) 16 | episode = episodes[0] 17 | assert isinstance(episode, deezer.Episode) 18 | assert episode.title == "Episode 9: Follow the money" 19 | assert len(episodes) == 12 20 | -------------------------------------------------------------------------------- /tests/resources/test_radio.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestRadio: 11 | @pytest.fixture 12 | def radio(self, client): 13 | return client.get_radio(23261) 14 | 15 | def test_attributes(self, radio): 16 | assert hasattr(radio, "title") 17 | assert isinstance(radio, deezer.Radio) 18 | assert repr(radio) == "" 19 | 20 | def test_get_tracks(self, radio): 21 | tracks = radio.get_tracks() 22 | assert isinstance(tracks, list) 23 | assert len(tracks) == 25 24 | track = tracks[0] 25 | assert isinstance(track, deezer.Track) 26 | assert repr(track) == '' 27 | -------------------------------------------------------------------------------- /tests/resources/test_resource.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestResource: 11 | def test_resource_relation(self, client): 12 | """Test passing parent object when using get_relation.""" 13 | album = client.get_album(302127) 14 | tracks = album.get_tracks() 15 | assert tracks[0].album is album 16 | 17 | def test_access_non_inferable_field_simplified_objet(self, client): 18 | """Fetch the full object when the missing field is not inferable.""" 19 | track = deezer.Track( 20 | client, 21 | json={ 22 | "id": 3135556, 23 | "type": "track", 24 | }, 25 | ) 26 | assert track.bpm == 123.4 27 | 28 | def test_access_no_infinite_fetch(self, client): 29 | track = deezer.Track( 30 | client, 31 | json={ 32 | "id": 3135556, 33 | "type": "track", 34 | }, 35 | ) 36 | # Does and API call 37 | assert track.title == "Harder, Better, Faster, Stronger" 38 | 39 | # Response cassette has been modified to simulate missing 'bpm' field 40 | with pytest.raises(AttributeError) as exc_info: 41 | track.bpm # noqa B018 42 | assert str(exc_info.value) == "'Track' object has no attribute 'bpm'" 43 | 44 | def test_field_not_found(self, client): 45 | """When field is missing an attribute error is raised without API calls.""" 46 | episode = deezer.Episode( 47 | client, 48 | json={ 49 | "id": 343457312, 50 | "type": "episode", 51 | }, 52 | ) 53 | with pytest.raises(AttributeError) as exc_info: 54 | episode.something # noqa B018 55 | assert str(exc_info.value) == "'Episode' object has no attribute 'something'" 56 | -------------------------------------------------------------------------------- /tests/resources/test_track.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import deezer 6 | 7 | pytestmark = pytest.mark.vcr 8 | 9 | 10 | class TestTrack: 11 | def test_track_attributes(self, client): 12 | """Test track resource.""" 13 | track = client.get_track(3135556) 14 | artist = track.get_artist() 15 | album = track.get_album() 16 | assert hasattr(track, "title") 17 | assert isinstance(track, deezer.Track) 18 | assert isinstance(artist, deezer.Artist) 19 | assert isinstance(album, deezer.Album) 20 | assert repr(track) == "" 21 | assert repr(artist) == "" 22 | assert repr(album) == "" 23 | 24 | def test_contributors(self, client): 25 | track = client.get_track(1425844092) 26 | contributors = track.contributors 27 | assert isinstance(contributors, list) 28 | assert len(contributors) == 2 29 | assert all(isinstance(c, deezer.Artist) for c in contributors) 30 | assert [c.id for c in contributors] == [51204222, 288166] 31 | -------------------------------------------------------------------------------- /tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import httpx 4 | import pytest 5 | 6 | from deezer.exceptions import ( 7 | DeezerForbiddenError, 8 | DeezerHTTPError, 9 | DeezerNotFoundError, 10 | DeezerRetryableHTTPError, 11 | ) 12 | 13 | 14 | @pytest.mark.parametrize( 15 | ("status_code", "expected_exception"), 16 | [ 17 | (403, DeezerForbiddenError), 18 | (404, DeezerNotFoundError), 19 | (418, DeezerHTTPError), 20 | (502, DeezerRetryableHTTPError), 21 | ], 22 | ) 23 | def test_deezer_http_error(status_code, expected_exception): 24 | response = httpx.Response(status_code=status_code) 25 | http_error = httpx.HTTPStatusError( 26 | message="", 27 | request=httpx.Request("GET", "https://example.com"), 28 | response=response, 29 | ) 30 | 31 | exc = DeezerHTTPError.from_http_error(http_error) 32 | assert isinstance(exc, expected_exception) 33 | --------------------------------------------------------------------------------