From 79d4448a926c088caf9e5195bcb475e32c29762f Mon Sep 17 00:00:00 2001 From: Il`ya Semyonov Date: Sat, 6 Jun 2020 23:48:32 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BA=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D1=81=20LicenceTextPart.=20=D0=9F=D0=BE=D0=BB=D1=8F=20ch?= =?UTF-8?q?eapest,=20title,=20family=5Fsub,=20fb=5Fimage,=20fb=5Fname,=20f?= =?UTF-8?q?amily,=20intro=5Fperiod=5Fduration,=20intro=5Fprice,=20start=5F?= =?UTF-8?q?period=5Fduration,=20start=5Fprice,=20licence=5Ftext=5Fparts=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20Product.=20=D0=9F=D0=BE=D0=BB=D1=8F=20stor?= =?UTF-8?q?age=5Fdir,=20duplicates=20=D0=B4=D0=BB=D1=8F=20Album.=20=D0=9F?= =?UTF-8?q?=D0=BE=D0=BB=D0=B5=20subscribed=20=D0=B4=D0=BB=D1=8F=20ArtistEv?= =?UTF-8?q?ent.=20=D0=9F=D0=BE=D0=BB=D0=B5=20description=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20GeneratedPlaylist.=20=D0=9F=D0=BE=D0=BB=D0=B5=20genre?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20Event.=20=D0=9F=D0=BE=D0=BB=D0=B5=20sho?= =?UTF-8?q?w=5Fin=5Fregions=20=D0=B4=D0=BB=D1=8F=20Genre.=20=D0=9F=D0=BE?= =?UTF-8?q?=D0=BB=D0=B5=20cover=5Furi=20=D0=B4=D0=BB=D1=8F=20MixLink.=20?= =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D1=8F=20og=5Fdescription,=20top=5Fartist=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20Playlist.=20=D0=9F=D0=BE=D0=BB=D1=8F=20ful?= =?UTF-8?q?l=5Fimage=5Furl,=20mts=5Ffull=5Fimage=5Furl=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20Station.=20=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B8=20=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D1=8B=20=D0=BA=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=BC=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8F.=20#339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yandex_music.track.licence_text_part.rst | 7 ++ docs/source/yandex_music.track.rst | 1 + tests/__init__.py | 1 + tests/conftest.py | 98 +++++++++++-------- tests/test_album.py | 13 ++- tests/test_artist_event.py | 7 +- tests/test_event.py | 5 +- tests/test_generated_playlist.py | 6 +- tests/test_genre.py | 9 +- tests/test_licence_text_part.py | 40 ++++++++ tests/test_mix_link.py | 8 +- tests/test_playlist.py | 12 ++- tests/test_product.py | 46 ++++++++- tests/test_station.py | 9 +- yandex_music/__init__.py | 4 +- yandex_music/account/product.py | 69 +++++++++++-- yandex_music/album/album.py | 17 +++- yandex_music/feed/artist_event.py | 5 + yandex_music/feed/event.py | 4 + yandex_music/feed/generated_playlist.py | 11 ++- yandex_music/genre/genre.py | 10 +- yandex_music/landing/mix_link.py | 5 + yandex_music/playlist/playlist.py | 13 ++- yandex_music/rotor/station.py | 18 +++- yandex_music/track/licence_text_part.py | 70 +++++++++++++ 25 files changed, 395 insertions(+), 93 deletions(-) create mode 100644 docs/source/yandex_music.track.licence_text_part.rst create mode 100644 tests/test_licence_text_part.py create mode 100644 yandex_music/track/licence_text_part.py diff --git a/docs/source/yandex_music.track.licence_text_part.rst b/docs/source/yandex_music.track.licence_text_part.rst new file mode 100644 index 0000000..dce92ed --- /dev/null +++ b/docs/source/yandex_music.track.licence_text_part.rst @@ -0,0 +1,7 @@ +yandex_music.LicenceTextPart +============================ + +.. autoclass:: yandex_music.LicenceTextPart + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/yandex_music.track.rst b/docs/source/yandex_music.track.rst index a34177d..08ef0ed 100644 --- a/docs/source/yandex_music.track.rst +++ b/docs/source/yandex_music.track.rst @@ -7,3 +7,4 @@ yandex_music.track.major yandex_music.track.track yandex_music.track.meta_data + yandex_music.track.licence_text_part diff --git a/tests/__init__.py b/tests/__init__.py index 75e0782..304faba 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -79,3 +79,4 @@ from .test_shot import TestShot from .test_renewable_remainder import TestRenewableRemainder from .test_tag import TestTag from .test_meta_data import TestMetaData +from .test_licence_text_part import TestLicenceTextPart diff --git a/tests/conftest.py b/tests/conftest.py index ee18e5c..a0e780d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,23 +1,23 @@ import pytest -from yandex_music import Counts, TrackId, CaseForms, Ratings, Icon, Album, Lyrics, Track, InvocationInfo, Playlist, \ - AutoRenewable, Station, MadeFor, Normalization, Major, TrackPosition, Best, Chart, Restrictions, Permissions, \ - Product, Cover, PlayCounter, Sequence, Price, Artist, AdParams, Description, Subscription, Id, Images, Pager, \ - Account, Client, TrackShort, Value, DiscreteScale, PlaylistId, MixLink, Link, PassportPhone, User, Promotion, \ - PersonalPlaylistsData, RotorSettings, TrackShortOld, PlayContextsData, Status, Settings, StationResult, Enum, \ - TrackWithAds, VideoSupplement, ArtistEvent, ChartItem, Event, AlbumEvent, Day, PlayContext, Plus, Title, Label, \ - GeneratedPlaylist, Video, Vinyl, SearchResult, BlockEntity, Block, PlaylistAbsence, ShotType, ShotData, Shot, \ - RenewableRemainder, ChartInfoMenuItem, ChartInfoMenu, ChartInfo, Tag, MetaData -from . import TestCounts, TestTrackId, TestCaseForms, TestRatings, TestIcon, TestAlbum, TestLyrics, \ - TestTrack, TestInvocationInfo, TestPlaylist, TestAutoRenewable, TestStation, TestNormalization, TestMajor, \ - TestTrackPosition, TestBest, TestChart, TestPermissions, TestPlus, TestProduct, TestCover, TestPlayCounter, \ - TestSequence, TestPrice, TestArtist, TestAdParams, TestDescription, TestSubscription, TestId, TestImages, \ - TestDiscreteScale, TestAccount, TestTrackShort, TestEnum, TestValue, TestPlaylistId, TestMixLink, TestLink, \ - TestUser, TestPassportPhone, TestPromotion, TestTitle, TestPersonalPlaylistsData, TestRotorSettings, \ - TestTrackShortOld, TestPager, TestStatus, TestSettings, TestStationResult, TestLabel, TestTrackWithAds, \ - TestVideoSupplement, TestEvent, TestDay, TestPlayContext, TestGeneratedPlaylist, TestVideo, TestVinyl, \ - TestSearchResult, TestBlockEntity, TestBlock, TestPlaylistAbsence, TestShot, TestShotData, TestShotType, \ - TestRenewableRemainder, TestChartInfoMenuItem, TestChartInfo, TestTag, TestMetaData +from yandex_music import Account, AdParams, Album, AlbumEvent, Artist, ArtistEvent, AutoRenewable, Best, Block, \ + BlockEntity, CaseForms, Chart, ChartInfo, ChartInfoMenu, ChartInfoMenuItem, ChartItem, Client, Counts, Cover, Day, \ + Description, DiscreteScale, Enum, Event, GeneratedPlaylist, Icon, Id, Images, InvocationInfo, Label, \ + LicenceTextPart, Link, Lyrics, MadeFor, Major, MetaData, MixLink, Normalization, Pager, PassportPhone, Permissions, \ + PersonalPlaylistsData, PlayContext, PlayContextsData, PlayCounter, Playlist, PlaylistAbsence, PlaylistId, Plus, \ + Price, Product, Promotion, Ratings, RenewableRemainder, Restrictions, RotorSettings, SearchResult, Sequence, \ + Settings, Shot, ShotData, ShotType, Station, StationResult, Status, Subscription, Tag, Title, Track, TrackId, \ + TrackPosition, TrackShort, TrackShortOld, TrackWithAds, User, Value, Video, VideoSupplement, Vinyl +from . import TestAccount, TestAdParams, TestAlbum, TestArtist, TestAutoRenewable, TestBest, TestBlock, TestBlockEntity, \ + TestCaseForms, TestChart, TestChartInfo, TestChartInfoMenuItem, TestCounts, TestCover, TestDay, TestDescription, \ + TestDiscreteScale, TestEnum, TestEvent, TestGeneratedPlaylist, TestIcon, TestId, TestImages, TestInvocationInfo, \ + TestLabel, TestLicenceTextPart, TestLink, TestLyrics, TestMajor, TestMetaData, TestMixLink, TestNormalization, \ + TestPager, TestPassportPhone, TestPermissions, TestPersonalPlaylistsData, TestPlayContext, TestPlayCounter, \ + TestPlaylist, TestPlaylistAbsence, TestPlaylistId, TestPlus, TestPrice, TestProduct, TestPromotion, TestRatings, \ + TestRenewableRemainder, TestRotorSettings, TestSearchResult, TestSequence, TestSettings, TestShot, TestShotData, \ + TestShotType, TestStation, TestStationResult, TestStatus, TestSubscription, TestTag, TestTitle, TestTrack, \ + TestTrackId, TestTrackPosition, TestTrackShort, TestTrackShortOld, TestTrackWithAds, TestUser, TestValue,\ + TestVideo, TestVideoSupplement, TestVinyl, TestArtistEvent @pytest.fixture(scope='session') @@ -27,7 +27,7 @@ def artist_factory(cover, counts, ratings, link, description): return Artist(TestArtist.id, TestArtist.error, TestArtist.reason, TestArtist.name, cover, TestArtist.various, TestArtist.composer, TestArtist.genres, TestArtist.og_image, TestArtist.op_image, TestArtist.no_pictures_from_search, counts, TestArtist.available, - ratings, [link], TestArtist.tickets_available, TestArtist.likes_count, popular_tracks, + ratings, [link], TestArtist.tickets_available, TestArtist.likes_count, popular_tracks, TestArtist.regions, TestArtist.decomposed, TestArtist.full_names, description, TestArtist.countries, TestArtist.en_wikipedia_link, TestArtist.db_aliases, TestArtist.aliases, TestArtist.init_date, TestArtist.end_date) @@ -90,21 +90,21 @@ def track_without_nested_tracks(artist, album, track_factory): @pytest.fixture(scope='session') def album_factory(label, track_position): class AlbumFactory: - def get(self, artists, volumes): + def get(self, artists, volumes, duplicates=None): return Album(TestAlbum.id, TestAlbum.error, TestAlbum.title, TestAlbum.track_count, artists, [label], TestAlbum.available, TestAlbum.available_for_premium_users, TestAlbum.version, TestAlbum.cover_uri, TestAlbum.content_warning, TestAlbum.original_release_year, - TestAlbum.genre, TestAlbum.meta_type, TestAlbum.og_image, TestAlbum.buy, TestAlbum.recent, - TestAlbum.very_important, TestAlbum.available_for_mobile, TestAlbum.available_partially, - TestAlbum.bests, TestAlbum.prerolls, volumes, TestAlbum.year, TestAlbum.release_date, - TestAlbum.type, track_position, TestAlbum.regions) + TestAlbum.genre, TestAlbum.meta_type, TestAlbum.storage_dir, TestAlbum.og_image, TestAlbum.buy, + TestAlbum.recent, TestAlbum.very_important, TestAlbum.available_for_mobile, + TestAlbum.available_partially, TestAlbum.bests, duplicates, TestAlbum.prerolls, volumes, + TestAlbum.year, TestAlbum.release_date, TestAlbum.type, track_position, TestAlbum.regions) return AlbumFactory() @pytest.fixture(scope='session') -def album(album_factory, artist_without_tracks, track_without_albums): - return album_factory.get([artist_without_tracks], [[track_without_albums]]) +def album(album_factory, artist_without_tracks, track_without_albums, album_without_nested_albums): + return album_factory.get([artist_without_tracks], [[track_without_albums]], [album_without_nested_albums]) @pytest.fixture(scope='session') @@ -113,7 +113,12 @@ def album_without_tracks(album_factory, artist_without_tracks): @pytest.fixture(scope='session') -def playlist_factory(user, cover, made_for, track_short, play_counter, playlist_absence): +def album_without_nested_albums(album_factory, artist_without_tracks, track_without_albums): + return album_factory.get([artist_without_tracks], [[track_without_albums]]) + + +@pytest.fixture(scope='session') +def playlist_factory(user, cover, made_for, track_short, play_counter, playlist_absence, artist): class PlaylistFactory: def get(self, similar_playlists, last_owner_playlists): return Playlist(user, cover, made_for, play_counter, playlist_absence, TestPlaylist.uid, TestPlaylist.kind, @@ -122,11 +127,12 @@ def playlist_factory(user, cover, made_for, track_short, play_counter, playlist_ TestPlaylist.url_part, TestPlaylist.created, TestPlaylist.modified, TestPlaylist.available, TestPlaylist.is_banner, TestPlaylist.is_premiere, TestPlaylist.duration_ms, TestPlaylist.og_image, TestPlaylist.og_title, - TestPlaylist.image, cover, TestPlaylist.background_color, TestPlaylist.text_color, - TestPlaylist.id_for_from, [track_short], TestPlaylist.prerolls, TestPlaylist.likes_count, - similar_playlists, last_owner_playlists, TestPlaylist.generated_playlist_type, - TestPlaylist.animated_cover_uri, TestPlaylist.ever_played, TestPlaylist.description, - TestPlaylist.description_formatted, TestPlaylist.is_for_from, TestPlaylist.regions) + TestPlaylist.og_description, TestPlaylist.image, cover, TestPlaylist.background_color, + TestPlaylist.text_color, TestPlaylist.id_for_from, [artist], [track_short], + TestPlaylist.prerolls, TestPlaylist.likes_count, similar_playlists, last_owner_playlists, + TestPlaylist.generated_playlist_type, TestPlaylist.animated_cover_uri, + TestPlaylist.ever_played, TestPlaylist.description, TestPlaylist.description_formatted, + TestPlaylist.is_for_from, TestPlaylist.regions) return PlaylistFactory() @@ -144,7 +150,7 @@ def playlist_without_nested_playlists(playlist_factory): @pytest.fixture(scope='session') def generated_playlist(playlist): return GeneratedPlaylist(TestGeneratedPlaylist.type, TestGeneratedPlaylist.ready, - TestGeneratedPlaylist.notify, playlist) + TestGeneratedPlaylist.notify, playlist, TestGeneratedPlaylist.description) @pytest.fixture(scope='session') @@ -223,6 +229,11 @@ def meta_data(): return MetaData(TestMetaData.album, TestMetaData.volume, TestMetaData.year) +@pytest.fixture(scope='session') +def licence_text_part(): + return LicenceTextPart(TestLicenceTextPart.text, TestLicenceTextPart.url) + + @pytest.fixture(scope='session') def link(): return Link(TestLink.title, TestLink.href, TestLink.type, TestLink.social_network) @@ -256,7 +267,7 @@ def pager(): @pytest.fixture(scope='session') def artist_event(artist, track): - return ArtistEvent(artist, [track], [artist]) + return ArtistEvent(artist, [track], [artist], TestArtistEvent.subscribed) @pytest.fixture(scope='session') @@ -321,7 +332,8 @@ def normalization(): @pytest.fixture(scope='session') def mix_link(): return MixLink(TestMixLink.title, TestMixLink.url, TestMixLink.url_scheme, TestMixLink.text_color, - TestMixLink.background_color, TestMixLink.background_image_uri, TestMixLink.cover_white) + TestMixLink.background_color, TestMixLink.background_image_uri, + TestMixLink.cover_white, TestMixLink.cover_uri) @pytest.fixture(scope='session') @@ -409,12 +421,15 @@ def rotor_settings(): @pytest.fixture(scope='session') -def product(price): +def product(price, licence_text_part): return Product(TestProduct.product_id, TestProduct.type, TestProduct.common_period_duration, TestProduct.duration, TestProduct.trial_duration, price, TestProduct.feature, TestProduct.debug, TestProduct.plus, - TestProduct.features, TestProduct.description, TestProduct.available, TestProduct.trial_available, - TestProduct.vendor_trial_available, TestProduct.button_text, TestProduct.button_additional_text, - TestProduct.payment_method_types) + TestProduct.cheapest, TestProduct.title, TestProduct.family_sub, TestProduct.fb_image, + TestProduct.fb_name, TestProduct.family, TestProduct.features, TestProduct.description, + TestProduct.available, TestProduct.trial_available, TestProduct.trial_period_duration, + TestProduct.intro_period_duration, price, TestProduct.start_period_duration, price, + [licence_text_part], TestProduct.vendor_trial_available, TestProduct.button_text, + TestProduct.button_additional_text, TestProduct.payment_method_types) @pytest.fixture(scope='session') @@ -448,7 +463,7 @@ def chart(track_id): @pytest.fixture(scope='session') def event(track, artist_event, album_event): return Event(TestEvent.id, TestEvent.type, TestEvent.type_for_from, TestEvent.title, [track], [artist_event], - [album_event], TestEvent.message, TestEvent.device, TestEvent.tracks_count) + [album_event], TestEvent.message, TestEvent.device, TestEvent.tracks_count, TestEvent.genre) @pytest.fixture(scope='session') @@ -489,7 +504,8 @@ def sequence(track): @pytest.fixture(scope='session') def station(id_, icon, restrictions): - return Station(id_, TestStation.name, icon, icon, icon, TestStation.id_for_from, restrictions, restrictions, id_) + return Station(id_, TestStation.name, icon, icon, icon, TestStation.id_for_from, restrictions, + restrictions, TestStation.full_image_url, TestStation.mts_full_image_url, id_) @pytest.fixture(scope='session') diff --git a/tests/test_album.py b/tests/test_album.py index 1337b7e..2f96dba 100644 --- a/tests/test_album.py +++ b/tests/test_album.py @@ -14,6 +14,7 @@ class TestAlbum: original_release_year = None genre = 'alternative' meta_type = 'music' + storage_dir = '4beeac1e.a.1155208' og_image = 'avatars.yandex.net/get-music-content/95061/89c14a7d.a.5239478-1/%%' buy = [] recent = False @@ -27,7 +28,8 @@ class TestAlbum: type = 'single' regions = None - def test_expected_values(self, album, artist_without_tracks, label, track_position, track_without_albums): + def test_expected_values(self, album, artist_without_tracks, label, track_position, + track_without_albums, album_without_nested_albums): assert album.id == self.id assert album.error == self.error assert album.title == self.title @@ -42,6 +44,7 @@ class TestAlbum: assert album.original_release_year == self.original_release_year assert album.genre == self.genre assert album.meta_type == self.meta_type + assert album.storage_dir == self.storage_dir assert album.og_image == self.og_image assert album.buy == self.buy assert album.recent == self.recent @@ -49,6 +52,7 @@ class TestAlbum: assert album.available_for_mobile == self.available_for_mobile assert album.available_partially == self.available_partially assert album.bests == self.bests + assert album.duplicates == [album_without_nested_albums] assert album.prerolls == self.prerolls assert album.volumes == [[track_without_albums]] assert album.year == self.year @@ -69,7 +73,7 @@ class TestAlbum: assert album.id == self.id - def test_de_json_all(self, client, artist, label, track_position, track): + def test_de_json_all(self, client, artist, label, track_position, track, album_without_nested_albums): json_dict = {'id_': self.id, 'error': self.error, 'title': self.title, 'cover_uri': self.cover_uri, 'track_count': self.track_count, 'artists': [artist.to_dict()], 'labels': [label.to_dict()], 'available': self.available, 'available_for_premium_users': self.available_for_premium_users, @@ -79,7 +83,8 @@ class TestAlbum: 'available_for_mobile': self.available_for_mobile, 'available_partially': self.available_partially, 'bests': self.bests, 'prerolls': self.prerolls, 'volumes': [[track.to_dict()]], 'year': self.year, 'release_date': self.release_date, 'type_': self.type, 'track_position': track_position.to_dict(), - 'meta_type': self.meta_type} + 'meta_type': self.meta_type, 'storage_dir': self.storage_dir, + 'duplicates': [album_without_nested_albums.to_dict()]} album = Album.de_json(json_dict, client) assert album.id == self.id @@ -96,6 +101,7 @@ class TestAlbum: assert album.original_release_year == self.original_release_year assert album.genre == self.genre assert album.meta_type == self.meta_type + assert album.storage_dir == self.storage_dir assert album.og_image == self.og_image assert album.buy == self.buy assert album.recent == self.recent @@ -103,6 +109,7 @@ class TestAlbum: assert album.available_for_mobile == self.available_for_mobile assert album.available_partially == self.available_partially assert album.bests == self.bests + assert album.duplicates == [album_without_nested_albums] assert album.prerolls == self.prerolls assert album.volumes == [[track]] assert album.year == self.year diff --git a/tests/test_artist_event.py b/tests/test_artist_event.py index fa59f90..e9ac574 100644 --- a/tests/test_artist_event.py +++ b/tests/test_artist_event.py @@ -2,10 +2,13 @@ from yandex_music import ArtistEvent class TestArtistEvent: + subscribed = True + def test_expected_values(self, artist_event, artist, track): assert artist_event.artist == artist assert artist_event.tracks == [track] assert artist_event.similar_to_artists_from_history == [artist] + assert artist_event.subscribed == self.subscribed def test_de_json_none(self, client): assert ArtistEvent.de_json({}, client) is None @@ -24,12 +27,14 @@ class TestArtistEvent: def test_de_json_all(self, client, artist, track): json_dict = {'artist': artist.to_dict(), 'tracks': [track.to_dict()], - 'similar_to_artists_from_history': [artist.to_dict()]} + 'similar_to_artists_from_history': [artist.to_dict()], + 'subscribed': self.subscribed} artist_event = ArtistEvent.de_json(json_dict, client) assert artist_event.artist == artist assert artist_event.tracks == [track] assert artist_event.similar_to_artists_from_history == [artist] + assert artist_event.subscribed == self.subscribed def test_equality(self, artist, track): a = ArtistEvent(artist, [track], [artist]) diff --git a/tests/test_event.py b/tests/test_event.py index 57bf479..c5b1ed4 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -9,6 +9,7 @@ class TestEvent: message = None device = None tracks_count = None + genre = 'electronics' def test_expected_values(self, event, track, artist_event, album_event): assert event.id == self.id @@ -21,6 +22,7 @@ class TestEvent: assert event.message == self.message assert event.device == self.device assert event.tracks_count == self.tracks_count + assert event.genre == self.genre def test_de_json_none(self, client): assert Event.de_json({}, client) is None @@ -39,7 +41,7 @@ class TestEvent: json_dict = {'id_': self.id, 'type_': self.type, 'type_for_from': self.type_for_from, 'title': self.title, 'tracks': [track.to_dict()], 'artists': [artist_event.to_dict()], 'albums': [album_event.to_dict()], 'message': self.message, 'device': self.device, - 'tracks_count': self.tracks_count} + 'tracks_count': self.tracks_count, 'genre': self.genre} event = Event.de_json(json_dict, client) assert event.id == self.id @@ -52,6 +54,7 @@ class TestEvent: assert event.message == self.message assert event.device == self.device assert event.tracks_count == self.tracks_count + assert event.genre == self.genre def test_equality(self): a = Event(self.id, self.type) diff --git a/tests/test_generated_playlist.py b/tests/test_generated_playlist.py index fc2db0e..a153677 100644 --- a/tests/test_generated_playlist.py +++ b/tests/test_generated_playlist.py @@ -5,12 +5,14 @@ class TestGeneratedPlaylist: type = 'playlistOfTheDay' ready = True notify = False + description = [] def test_expected_values(self, generated_playlist, playlist): assert generated_playlist.type == self.type assert generated_playlist.ready == self.ready assert generated_playlist.notify == self.notify assert generated_playlist.data == playlist + assert generated_playlist.description == self.description def test_de_json_none(self, client): assert GeneratedPlaylist.de_json({}, client) is None @@ -28,13 +30,15 @@ class TestGeneratedPlaylist: assert generated_playlist.data == playlist def test_de_json_all(self, client, playlist): - json_dict = {'type_': self.type, 'ready': self.ready, 'notify': self.notify, 'data': playlist.to_dict()} + json_dict = {'type_': self.type, 'ready': self.ready, 'notify': self.notify, 'data': playlist.to_dict(), + 'description': self.description} generated_playlist = GeneratedPlaylist.de_json(json_dict, client) assert generated_playlist.type == self.type assert generated_playlist.ready == self.ready assert generated_playlist.notify == self.notify assert generated_playlist.data == playlist + assert generated_playlist.description == self.description def test_equality(self, playlist): a = GeneratedPlaylist(self.type, self.ready, self.notify, playlist) diff --git a/tests/test_genre.py b/tests/test_genre.py index 84a6ec9..123f907 100644 --- a/tests/test_genre.py +++ b/tests/test_genre.py @@ -6,8 +6,8 @@ from yandex_music import Genre @pytest.fixture(scope='class') def genre(title, images, icon, genre_without_sub_genre): return Genre(TestGenre.id, TestGenre.weight, TestGenre.composer_top, TestGenre.title, {'uz': title}, images, - TestGenre.show_in_menu, TestGenre.full_title, TestGenre.url_part, TestGenre.color, icon, - [genre_without_sub_genre], TestGenre.hide_in_regions) + TestGenre.show_in_menu, TestGenre.show_in_regions, TestGenre.full_title, TestGenre.url_part, + TestGenre.color, icon, [genre_without_sub_genre], TestGenre.hide_in_regions) @pytest.fixture(scope='class') @@ -22,6 +22,7 @@ class TestGenre: composer_top = False title = None show_in_menu = True + show_in_regions = [181] full_title = 'Музыка всех жанров' url_part = None color = None @@ -35,6 +36,7 @@ class TestGenre: assert genre.titles == {"uz": title} assert genre.images == images assert genre.show_in_menu == self.show_in_menu + assert genre.show_in_regions == self.show_in_regions assert genre.full_title == self.full_title assert genre.url_part == self.url_part assert genre.color == self.color @@ -66,7 +68,7 @@ class TestGenre: 'titles': {'uz': title.to_dict()}, 'images': images.to_dict(), 'show_in_menu': self.show_in_menu, 'full_title': self.full_title, 'url_part': self.url_part, 'color': self.color, 'radio_icon': icon.to_dict(), 'sub_genres': [genre_without_sub_genre.to_dict()], - 'hide_in_regions': self.hide_in_regions} + 'hide_in_regions': self.hide_in_regions, 'show_in_regions': self.show_in_regions} genre = Genre.de_json(json_dict, client) assert genre.id == self.id @@ -76,6 +78,7 @@ class TestGenre: assert genre.titles == {'uz': title} assert genre.images == images assert genre.show_in_menu == self.show_in_menu + assert genre.show_in_regions == self.show_in_regions assert genre.full_title == self.full_title assert genre.url_part == self.url_part assert genre.color == self.color diff --git a/tests/test_licence_text_part.py b/tests/test_licence_text_part.py new file mode 100644 index 0000000..d291bf4 --- /dev/null +++ b/tests/test_licence_text_part.py @@ -0,0 +1,40 @@ +from yandex_music import LicenceTextPart + + +class TestLicenceTextPart: + text = 'Условия подписки.' + url = 'https://yandex.ru/legal/yandex_plus_conditions/' + + def test_expected_values(self, licence_text_part): + assert licence_text_part.text == self.text + assert licence_text_part.url == self.url + + def test_de_json_none(self, client): + assert LicenceTextPart.de_json({}, client) is None + + def test_de_list_none(self, client): + assert LicenceTextPart.de_list({}, client) == [] + + def test_de_json_required(self, client): + json_dict = {'text': self.text} + licence_text_part = LicenceTextPart.de_json(json_dict, client) + + assert licence_text_part.text == self.text + + def test_de_json_all(self, client): + json_dict = {'text': self.text, 'url': self.url} + licence_text_part = LicenceTextPart.de_json(json_dict, client) + + assert licence_text_part.text == self.text + assert licence_text_part.url == self.url + + def test_equality(self): + a = LicenceTextPart(self.text, self.url) + b = LicenceTextPart('', self.url) + c = LicenceTextPart(self.text, self.url) + + assert a != b + assert hash(a) != hash(b) + assert a is not b + + assert a == c diff --git a/tests/test_mix_link.py b/tests/test_mix_link.py index d4b1e3c..9bbfb38 100644 --- a/tests/test_mix_link.py +++ b/tests/test_mix_link.py @@ -7,8 +7,10 @@ class TestMixLink: url_scheme = 'yandexmusic://tag/belarus' text_color = '#6c65a9' background_color = 'transparent' - background_image_uri = 'avatars.yandex.net/get-music-misc/28592/mix.5cf0bd5e58ea3a1e70caa07b.background-image.1559281047248/%%' + background_image_uri = 'avatars.yandex.net/get-music-misc/28592/mix.5cf0bd5e58ea3a1e70caa07b.' \ + 'background-image.1559281047248/%%' cover_white = 'avatars.yandex.net/get-music-misc/28052/mix.5cf0bd5e58ea3a1e70caa07b.cover-white.1559281049219/%%' + cover_uri = 'avatars.yandex.net/get-music-misc/34161/mix.57c6d15a2d3213a86ac653d2.cover.1555818786846/%%' def test_expected_values(self, mix_link): assert mix_link.title == self.title @@ -18,6 +20,7 @@ class TestMixLink: assert mix_link.background_color == self.background_color assert mix_link.background_image_uri == self.background_image_uri assert mix_link.cover_white == self.cover_white + assert mix_link.cover_uri == self.cover_uri def test_de_json_none(self, client): assert MixLink.de_json({}, client) is None @@ -42,7 +45,7 @@ class TestMixLink: def test_de_json_all(self, client): json_dict = {'title': self.title, 'url': self.url, 'url_scheme': self.url_scheme, 'text_color': self.text_color, 'background_color': self.background_color, 'background_image_uri': self.background_image_uri, - 'cover_white': self.cover_white} + 'cover_white': self.cover_white, 'cover_uri': self.cover_uri} mix_link = MixLink.de_json(json_dict, client) assert mix_link.title == self.title @@ -52,6 +55,7 @@ class TestMixLink: assert mix_link.background_color == self.background_color assert mix_link.background_image_uri == self.background_image_uri assert mix_link.cover_white == self.cover_white + assert mix_link.cover_uri == self.cover_uri def test_equality(self): a = MixLink(self.title, self.url, self.url_scheme, self.text_color, self.background_color, diff --git a/tests/test_playlist.py b/tests/test_playlist.py index 5222cd4..32bb380 100644 --- a/tests/test_playlist.py +++ b/tests/test_playlist.py @@ -20,6 +20,7 @@ class TestPlaylist: duration_ms = 12402690 og_image = 'avatars.yandex.net/get-music-user-playlist/38125/q0ahkhfQE3neTk/%%?1572609906461' og_title = 'Плейлист дня' + og_description = 'Слушайте с пользой: подкасты, которые могут вам понравиться' image = '' background_color = '' text_color = '' @@ -35,7 +36,7 @@ class TestPlaylist: regions = None def test_expected_values(self, playlist, user, cover, made_for, track_short, play_counter, playlist_absence, - playlist_without_nested_playlists): + playlist_without_nested_playlists, artist): assert playlist.owner == user assert playlist.uid == self.uid assert playlist.kind == self.kind @@ -59,11 +60,13 @@ class TestPlaylist: assert playlist.duration_ms == self.duration_ms assert playlist.og_image == self.og_image assert playlist.og_title == self.og_title + assert playlist.og_description == self.og_description assert playlist.image == self.image assert playlist.cover_without_text == cover assert playlist.background_color == self.background_color assert playlist.text_color == self.text_color assert playlist.id_for_from == self.id_for_from + assert playlist.top_artist == [artist] assert playlist.tracks == [track_short] assert playlist.prerolls == self.prerolls assert playlist.likes_count == self.likes_count @@ -95,7 +98,7 @@ class TestPlaylist: assert playlist.playlist_absence == playlist_absence def test_de_json_all(self, client, user, cover, made_for, track_short, play_counter, playlist_absence, - playlist_without_nested_playlists): + playlist_without_nested_playlists, artist): json_dict = {'owner': user.to_dict(), 'uid': self.uid, 'kind': self.kind, 'title': self.title, 'track_count': self.track_count, 'cover': cover.to_dict(), 'made_for': made_for.to_dict(), 'play_counter': play_counter.to_dict(), 'playlist_absence': playlist_absence.to_dict(), @@ -111,7 +114,8 @@ class TestPlaylist: 'image': self.image, 'id_for_from': self.id_for_from, 'background_color': self.background_color, 'text_color': self.text_color, 'cover_without_text': cover.to_dict(), 'similar_playlists': [playlist_without_nested_playlists.to_dict()], - 'last_owner_playlists': [playlist_without_nested_playlists.to_dict()]} + 'last_owner_playlists': [playlist_without_nested_playlists.to_dict()], + 'og_description': self.og_description, 'top_artist': [artist.to_dict()]} playlist = Playlist.de_json(json_dict, client) assert playlist.owner == user @@ -137,11 +141,13 @@ class TestPlaylist: assert playlist.duration_ms == self.duration_ms assert playlist.og_image == self.og_image assert playlist.og_title == self.og_title + assert playlist.og_description == self.og_description assert playlist.image == self.image assert playlist.cover_without_text == cover assert playlist.background_color == self.background_color assert playlist.text_color == self.text_color assert playlist.id_for_from == self.id_for_from + assert playlist.top_artist == [artist] assert playlist.tracks == [track_short] assert playlist.prerolls == self.prerolls assert playlist.likes_count == self.likes_count diff --git a/tests/test_product.py b/tests/test_product.py index 584db52..679b605 100644 --- a/tests/test_product.py +++ b/tests/test_product.py @@ -11,15 +11,24 @@ class TestProduct: feature = 'basic-music' debug = False plus = False + cheapest = True + title = 'КиноПоиск HD' + family_sub = False + fb_image = '' + fb_name = 'kinopoisk-plus' + family = False features = ['basic-music'] available = None trial_available = None + trial_period_duration = 'P1M' + intro_period_duration = 'P1Y' + start_period_duration = 'P1M' vendor_trial_available = None button_text = None button_additional_text = None payment_method_types = None - def test_expected_values(self, product, price): + def test_expected_values(self, product, price, licence_text_part): assert product.product_id == self.product_id assert product.type == self.type assert product.common_period_duration == self.common_period_duration @@ -29,10 +38,22 @@ class TestProduct: assert product.feature == self.feature assert product.debug == self.debug assert product.plus == self.plus + assert product.cheapest == self.cheapest + assert product.title == self.title + assert product.family_sub == self.family_sub + assert product.fb_image == self.fb_image + assert product.fb_name == self.fb_name + assert product.family == self.family assert product.features == self.features assert product.description == self.description assert product.available == self.available assert product.trial_available == self.trial_available + assert product.trial_period_duration == self.trial_period_duration + assert product.intro_period_duration == self.intro_period_duration + assert product.intro_price == price + assert product.start_period_duration == self.start_period_duration + assert product.start_price == price + assert product.licence_text_parts == [licence_text_part] assert product.vendor_trial_available == self.vendor_trial_available assert product.button_text == self.button_text assert product.button_additional_text == self.button_additional_text @@ -61,15 +82,20 @@ class TestProduct: assert product.debug == self.debug assert product.plus == self.plus - def test_de_json_all(self, client, price): + def test_de_json_all(self, client, price, licence_text_part): json_dict = {'product_id': self.product_id, 'type_': self.type, 'common_period_duration': self.common_period_duration, 'duration': self.duration, 'trial_duration': self.trial_duration, 'price': price.to_dict(), 'feature': self.feature, 'debug': self.debug, 'plus': self.plus, 'features': self.features, 'description': self.description, 'available': self.available, 'trial_available': self.trial_available, 'vendor_trial_available': self.vendor_trial_available, 'button_text': self.button_text, - 'button_additional_text': self.button_additional_text, - 'payment_method_types': self.payment_method_types} + 'button_additional_text': self.button_additional_text, 'cheapest': self.cheapest, + 'payment_method_types': self.payment_method_types, 'title': self.title, 'family': self.family, + 'family_sub': self.family_sub, 'fb_image': self.fb_image, 'fb_name': self.fb_name, + 'trial_period_duration': self.trial_period_duration, + 'intro_period_duration': self.intro_period_duration, 'intro_price': price.to_dict(), + 'start_period_duration': self.start_period_duration, 'start_price': price.to_dict(), + 'licence_text_parts': [licence_text_part.to_dict()]} product = Product.de_json(json_dict, client) assert product.product_id == self.product_id @@ -81,10 +107,22 @@ class TestProduct: assert product.feature == self.feature assert product.debug == self.debug assert product.plus == self.plus + assert product.cheapest == self.cheapest + assert product.title == self.title + assert product.family_sub == self.family_sub + assert product.fb_image == self.fb_image + assert product.fb_name == self.fb_name + assert product.family == self.family assert product.features == self.features assert product.description == self.description assert product.available == self.available assert product.trial_available == self.trial_available + assert product.trial_period_duration == self.trial_period_duration + assert product.intro_period_duration == self.intro_period_duration + assert product.intro_price == price + assert product.start_period_duration == self.start_period_duration + assert product.start_price == price + assert product.licence_text_parts == [licence_text_part] assert product.vendor_trial_available == self.vendor_trial_available assert product.button_text == self.button_text assert product.button_additional_text == self.button_additional_text diff --git a/tests/test_station.py b/tests/test_station.py index 900164a..6f36f66 100644 --- a/tests/test_station.py +++ b/tests/test_station.py @@ -4,6 +4,8 @@ from yandex_music import Station class TestStation: name = 'На вашей волне' id_for_from = 'user-561231028' + full_image_url = 'avatars.yandex.net/get-music-misc/30221/rotor-activity-sex-full-image-3sv6j/%%' + mts_full_image_url = 'avatars.yandex.net/get-music-misc/2413828/rotor-activity-sex-mts-full-image-sYtDD/%%' def test_expected_values(self, station, id_, icon, restrictions): assert station.id == id_ @@ -14,6 +16,8 @@ class TestStation: assert station.id_for_from == self.id_for_from assert station.restrictions == restrictions assert station.restrictions2 == restrictions + assert station.full_image_url == self.full_image_url + assert station.mts_full_image_url == self.mts_full_image_url assert station.parent_id == id_ def test_de_json_none(self, client): @@ -38,7 +42,8 @@ class TestStation: json_dict = {'id_': id_.to_dict(), 'name': self.name, 'icon': icon.to_dict(), 'mts_icon': icon.to_dict(), 'geocell_icon': icon.to_dict(), 'id_for_from': self.id_for_from, 'restrictions': restrictions.to_dict(), 'restrictions2': restrictions.to_dict(), - 'parent_id': id_.to_dict()} + 'parent_id': id_.to_dict(), 'full_image_url': self.full_image_url, + 'mts_full_image_url': self.mts_full_image_url} station = Station.de_json(json_dict, client) assert station.id == id_ @@ -49,6 +54,8 @@ class TestStation: assert station.id_for_from == self.id_for_from assert station.restrictions == restrictions assert station.restrictions2 == restrictions + assert station.full_image_url == self.full_image_url + assert station.mts_full_image_url == self.mts_full_image_url assert station.parent_id == id_ def test_equality(self, id_, icon, restrictions): diff --git a/yandex_music/__init__.py b/yandex_music/__init__.py index 349eb42..1b08972 100644 --- a/yandex_music/__init__.py +++ b/yandex_music/__init__.py @@ -47,6 +47,7 @@ from .shot.shot_event import ShotEvent from .tracks_list import TracksList from .track.major import Major +from .track.licence_text_part import LicenceTextPart from .track.meta_data import MetaData from .track.normalization import Normalization from .track.track import Track @@ -128,4 +129,5 @@ __all__ = ['YandexMusicObject', 'Client', 'Account', 'PassportPhone', 'Invocatio 'Sequence', 'StationTracksResult', 'BriefInfo', 'Description', 'PlaylistId', 'Vinyl', 'Supplement', 'Lyrics', 'VideoSupplement', 'ArtistTracks', 'Pager', 'ArtistAlbums', 'PlaylistAbsence', 'Shot', 'ShotEvent', 'ShotType', 'ShotData', 'SimilarTracks', 'UserSettings', 'RenewableRemainder', 'ChartInfo', 'ChartInfoMenu', - 'ChartInfoMenuItem', 'Tag', 'TagResult', 'PlaylistRecommendations', 'LandingList', 'MetaData'] + 'ChartInfoMenuItem', 'Tag', 'TagResult', 'PlaylistRecommendations', 'LandingList', 'MetaData', + 'LicenceTextPart'] diff --git a/yandex_music/account/product.py b/yandex_music/account/product.py index ff4d9eb..2a2b000 100644 --- a/yandex_music/account/product.py +++ b/yandex_music/account/product.py @@ -19,10 +19,23 @@ class Product(YandexMusicObject): feature (:obj:`str`): Предоставляемая возможность. debug (:obj:`bool`): Отладочный продукт. plus (:obj:`bool`): Даёт ли подписку "Плюс". + cheapest (:obj:`bool`): Самый дешёвый (лучшее предложение). + title (:obj:`str`): Заголовок продукта. + family_sub (:obj:`bool`): Семейная ли подписка. + fb_image (:obj:`str`): Картинка для превью на facebook. + fb_name (:obj:`str`): Заголовок превью на facebook. + family (:obj:`bool`): Доступно ли для семьи. features (:obj:`list` из :obj:`str`): Список предоставляемых возможностей. description (:obj:`str`): Описание. available (:obj:`bool`): Доступна ли покупка. - trial_available (:obj:`bool`): Доступен ли испытательный срок. + trial_available (:obj:`bool`): Доступен ли пробный период. + trial_period_duration (:obj:`str`): Длительность пробного периода. + intro_period_duration (:obj:`str`): Длительность вступительного периода TODO. + intro_price (:obj:`yandex_music.Price`): Цена вступительного периода. + start_period_duration (:obj:`str`): Длительность первого срока (за меньшую цену). + start_price (:obj:`yandex_music.Price`): Цена за первый срок. + licence_text_parts (:obj:`list` из :obj:`yandex_music.LicenceTextPart`): + Длительность пробного периода. vendor_trial_available (:obj:`bool`): Доступен испытательный срок продавца TODO. button_text (:obj:`str`): Текст кнопки. button_additional_text (:obj:`str`): Дополнительный текст кнопки. @@ -39,10 +52,23 @@ class Product(YandexMusicObject): feature (:obj:`str`): Предоставляемая возможность. debug (:obj:`bool`): Отладочный продукт. plus (:obj:`bool`): Даёт ли подписку "Плюс". + cheapest (:obj:`bool`, optional): Самый дешёвый (лучшее предложение). + title (:obj:`str`, optional): Заголовок продукта. + family_sub (:obj:`bool`, optional): Семейная ли подписка. + fb_image (:obj:`str`, optional): Картинка для превью на facebook. + fb_name (:obj:`str`, optional): Заголовок превью на facebook. + family (:obj:`bool`, optional): Доступно ли для семьи. features (:obj:`list` из :obj:`str`, optional): Список предоставляемых возможностей. description (:obj:`str`, optional): Описание. available (:obj:`bool`, optional): Доступна ли покупка. - trial_available (:obj:`bool`, optional): Доступен ли испытательный срок. + trial_available (:obj:`bool`, optional): Доступен ли пробный период. + trial_period_duration (:obj:`str`, optional): Длительность пробного периода. + intro_period_duration (:obj:`str`, optional): Длительность вступительного периода TODO. + intro_price (:obj:`yandex_music.Price`, optional): Цена вступительного периода. + start_period_duration (:obj:`str`, optional): Длительность первого срока (за меньшую цену). + start_price (:obj:`yandex_music.Price`, optional): Цена за первый срок. + licence_text_parts (:obj:`list` из :obj:`yandex_music.LicenceTextPart`, optional): + Длительность пробного периода. vendor_trial_available (:obj:`bool`, optional): Доступен испытательный срок продавца TODO. button_text (:obj:`str`, optional): Текст кнопки. button_additional_text (:obj:`str`, optional): Дополнительный текст кнопки. @@ -61,10 +87,22 @@ class Product(YandexMusicObject): feature: str, debug: bool, plus: bool, + cheapest: Optional[bool] = None, + title: Optional[str] = None, + family_sub: Optional[bool] = None, + fb_image: Optional[str] = None, + fb_name: Optional[str] = None, + family: Optional[bool] = None, features: List[str] = None, description: Optional[str] = None, available: Optional[bool] = None, trial_available: Optional[bool] = None, + trial_period_duration: Optional[str] = None, + intro_period_duration: Optional[str] = None, + intro_price: Optional['Price'] = None, + start_period_duration: Optional[str] = None, + start_price: Optional['Price'] = None, + licence_text_parts: List['Price'] = None, vendor_trial_available: Optional[bool] = None, button_text: Optional[str] = None, button_additional_text: Optional[str] = None, @@ -81,10 +119,22 @@ class Product(YandexMusicObject): self.debug = debug self.plus = plus + self.cheapest = cheapest + self.title = title + self.family_sub = family_sub + self.fb_image = fb_image + self.fb_name = fb_name + self.family = family self.features = features self.description = description self.available = available self.trial_available = trial_available + self.trial_period_duration = trial_period_duration + self.intro_period_duration = intro_period_duration + self.intro_price = intro_price + self.start_period_duration = start_period_duration + self.start_price = start_price + self.licence_text_parts = licence_text_parts self.vendor_trial_available = vendor_trial_available self.button_text = button_text self.button_additional_text = button_additional_text @@ -105,14 +155,17 @@ class Product(YandexMusicObject): client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. Returns: - :obj:`yandex_music.Product`: Продоваемый продукт. + :obj:`yandex_music.Product`: Продаваемый продукт. """ if not data: return None data = super(Product, cls).de_json(data, client) - from yandex_music import Price + from yandex_music import Price, LicenceTextPart data['price'] = Price.de_json(data.get('price'), client) + data['intro_price'] = Price.de_json(data.get('intro_price'), client) + data['start_price'] = Price.de_json(data.get('start_price'), client) + data['licence_text_parts'] = LicenceTextPart.de_list(data.get('licence_text_parts'), client) return cls(client=client, **data) @@ -125,13 +178,9 @@ class Product(YandexMusicObject): client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. Returns: - :obj:`list` из :obj:`yandex_music.Product`: Продоваемые продукты. + :obj:`list` из :obj:`yandex_music.Product`: Продаваемые продукты. """ if not data: return [] - products = list() - for product in data: - products.append(cls.de_json(product, client)) - - return products + return [cls.de_json(product, client) for product in data] diff --git a/yandex_music/album/album.py b/yandex_music/album/album.py index b802057..ad8f8b2 100644 --- a/yandex_music/album/album.py +++ b/yandex_music/album/album.py @@ -31,12 +31,15 @@ class Album(YandexMusicObject): cover_uri (:obj:`str`): Ссылка на обложку. content_warning (:obj:`str`): Предупреждение о содержимом альбома. genre (:obj:`str`): Жанр музыки. + storage_dir (:obj:`str`): В какой папке на сервере хранится файл TODO. og_image (:obj:`str`): Ссылка на превью Open Graph. recent (:obj:`bool`): Является ли альбом новым. very_important (:obj:`bool`): Популярен ли альбом у слушателей. available_for_mobile (:obj:`bool`): Доступен ли альбом из приложения для телефона. available_partially (:obj:`bool`): Доступен ли альбом частично для пользователей без подписки. bests (:obj:`list` из :obj:`int`): ID лучших треков альбома. + duplicates (:obj:`list` из :obj:`yandex_music.Album`): Альбомы-дубликаты. + prerolls (:obj:`list`): Прероллы TODO. volumes (:obj:`list` из :obj:`list` из :obj:`Track`): Треки альбома, разделенные по дискам. year (:obj:`int`): Год релиза. release_date (:obj:`str`): Дата релиза в формате ISO 8601. @@ -59,12 +62,15 @@ class Album(YandexMusicObject): content_warning (:obj:`str`, optional): Предупреждение о содержимом альбома. genre (:obj:`str`, optional): Жанр музыки. meta_type (:obj:`str`, optional): Мета тип TODO. + storage_dir (:obj:`str`, optional): В какой папке на сервере хранится файл TODO. og_image (:obj:`str`, optional): Ссылка на превью Open Graph. recent (:obj:`bool`, optional): Является ли альбом новым. very_important (:obj:`bool`, optional): Популярен ли альбом у слушателей. available_for_mobile (:obj:`bool`, optional): Доступен ли альбом из приложения для телефона. available_partially (:obj:`bool`, optional): Доступен ли альбом частично для пользователей без подписки. bests (:obj:`list` из :obj:`int`, optional): ID лучших треков альбома. + duplicates (:obj:`list` из :obj:`yandex_music.Album`, optional): Альбомы-дубликаты. + prerolls (:obj:`list`, optional): Прероллы TODO. volumes (:obj:`list` из :obj:`list` из :obj:`Track`, optional): Треки альбома, разделенные по дискам. year (:obj:`int`, optional): Год релиза. release_date (:obj:`str`, optional): Дата релиза в формате ISO 8601. @@ -90,6 +96,7 @@ class Album(YandexMusicObject): original_release_year=None, genre: Optional[str] = None, meta_type: Optional[str] = None, + storage_dir: Optional[str] = None, og_image: Optional[str] = None, buy: Optional[list] = None, recent: Optional[bool] = None, @@ -97,6 +104,7 @@ class Album(YandexMusicObject): available_for_mobile: Optional[bool] = None, available_partially: Optional[bool] = None, bests: Optional[List[int]] = None, + duplicates: List['Album'] = None, prerolls: Optional[list] = None, volumes: Optional[List[List['Track']]] = None, year: Optional[int] = None, @@ -122,8 +130,10 @@ class Album(YandexMusicObject): self.year = year self.release_date = release_date self.bests = bests + self.duplicates = duplicates self.prerolls = prerolls self.volumes = volumes + self.storage_dir = storage_dir self.og_image = og_image self.buy = buy self.recent = recent @@ -201,6 +211,7 @@ class Album(YandexMusicObject): data['artists'] = Artist.de_list(data.get('artists'), client) data['labels'] = Label.de_list(data.get('labels'), client) data['track_position'] = TrackPosition.de_json(data.get('track_position'), client) + data['duplicates'] = Album.de_list(data.get('duplicates'), client) if data.get('volumes'): data['volumes'] = [Track.de_list(i, client) for i in data['volumes']] @@ -220,11 +231,7 @@ class Album(YandexMusicObject): if not data: return [] - albums = list() - for album in data: - albums.append(cls.de_json(album, client)) - - return albums + return [cls.de_json(album, client) for album in data] # camelCase псевдонимы diff --git a/yandex_music/feed/artist_event.py b/yandex_music/feed/artist_event.py index 632eda4..b46dcbe 100644 --- a/yandex_music/feed/artist_event.py +++ b/yandex_music/feed/artist_event.py @@ -13,12 +13,14 @@ class ArtistEvent(YandexMusicObject): artist (:obj:`yandex_music.Artist` | :obj:`None`): Артист. tracks (:obj:`list` :obj:`yandex_music.Track`): Треки. similar_to_artists_from_history (:obj:`list` :obj:`yandex_music.Artist`): Похожие артисты из истории. + subscribed (:obj:`bool`): Подписан ли на событие. client (:obj:`yandex_music.Client`): Клиент Yandex Music. Args: artist (:obj:`yandex_music.Artist` | :obj:`None`): Артист. tracks (:obj:`list` :obj:`yandex_music.Track`): Треки. similar_to_artists_from_history (:obj:`list` :obj:`yandex_music.Artist`): Похожие артисты из истории. + subscribed (:obj:`bool`): Подписан ли на событие. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. """ @@ -27,12 +29,15 @@ class ArtistEvent(YandexMusicObject): artist: Optional['Artist'], tracks: List['Track'], similar_to_artists_from_history: List['Artist'], + subscribed: Optional['bool'] = None, client: Optional['Client'] = None, **kwargs) -> None: self.artist = artist self.tracks = tracks self.similar_to_artists_from_history = similar_to_artists_from_history + self.subscribed = subscribed + self.client = client self._id_attrs = (self.artist, self.tracks, self.similar_to_artists_from_history) diff --git a/yandex_music/feed/event.py b/yandex_music/feed/event.py index 3ee3ca8..850ee82 100644 --- a/yandex_music/feed/event.py +++ b/yandex_music/feed/event.py @@ -29,6 +29,7 @@ class Event(YandexMusicObject): message (:obj:`str`): Сообщение уведомления. device (:obj:`str`): Устройство, с которого пришло уведомление. tracks_count (:obj:`int`): Количество треков (возможно, уже не используется). + genre (:obj:`str`): Жанр треков. client (:obj:`yandex_music.Client`): Клиент Yandex Music. Args: @@ -43,6 +44,7 @@ class Event(YandexMusicObject): message (:obj:`str`, optional): Сообщение уведомления. device (:obj:`str`, optional): Устройство, с которого пришло уведомление. tracks_count (:obj:`int`, optional): Количество треков (возможно, уже не используется). + genre (:obj:`str`, optional): Жанр треков. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. """ @@ -58,6 +60,7 @@ class Event(YandexMusicObject): message=None, device=None, tracks_count: Optional[int] = None, + genre: Optional[str] = None, client: Optional['Client'] = None, **kwargs) -> None: self.id = id_ @@ -71,6 +74,7 @@ class Event(YandexMusicObject): self.message = message self.device = device self.tracks_count = tracks_count + self.genre = genre self.client = client self._id_attrs = (self.id, self.type) diff --git a/yandex_music/feed/generated_playlist.py b/yandex_music/feed/generated_playlist.py index daa0985..2c43728 100644 --- a/yandex_music/feed/generated_playlist.py +++ b/yandex_music/feed/generated_playlist.py @@ -18,6 +18,7 @@ class GeneratedPlaylist(YandexMusicObject): ready (:obj:`bool`): Готовность плейлиста. notify (:obj:`bool`): Уведомлён ли пользователь об обновлении содержания. data (:obj:`yandex_music.Playlist`): Сгенерированный плейлист. + description (:obj:`list`): Описание TODO. client (:obj:`yandex_music.Client`): Клиент Yandex Music. Args: @@ -25,6 +26,7 @@ class GeneratedPlaylist(YandexMusicObject): ready (:obj:`bool`): Готовность плейлиста. notify (:obj:`bool`): Уведомлён ли пользователь об обновлении содержания. data (:obj:`yandex_music.Playlist`, optional): Сгенерированный плейлист. + description (:obj:`list`, optional): Описание TODO. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. """ @@ -34,6 +36,7 @@ class GeneratedPlaylist(YandexMusicObject): ready: bool, notify: bool, data: Optional['Playlist'], + description: Optional[list] = None, client: Optional['Client'] = None, **kwargs) -> None: self.type = type_ @@ -41,6 +44,8 @@ class GeneratedPlaylist(YandexMusicObject): self.notify = notify self.data = data + self.description = description + self.client = client self._id_attrs = (self.type, self.ready, self.notify, self.data) @@ -80,8 +85,4 @@ class GeneratedPlaylist(YandexMusicObject): if not data: return [] - generated_playlists = list() - for generated_playlist in data: - generated_playlists.append(cls.de_json(generated_playlist, client)) - - return generated_playlists + return [cls.de_json(generated_playlist, client) for generated_playlist in data] diff --git a/yandex_music/genre/genre.py b/yandex_music/genre/genre.py index 680bdfd..601aa47 100644 --- a/yandex_music/genre/genre.py +++ b/yandex_music/genre/genre.py @@ -17,6 +17,7 @@ class Genre(YandexMusicObject): titles (:obj:`dict`): Словарь заголовков на разных языках, где ключ - язык. images (:obj:`yandex_music.Images`): Изображение жанра. show_in_menu (:obj:`bool`): Показывать в меню. + show_in_regions (:obj:`list` из :obj:`int`): Список регионов в которых отображается жанр в списках. full_title (:obj:`str`): Полный заголовок. url_part (:obj:`str`): Часть ссылки на жанр для открытия в браузере. color (:obj:`str`): Цвет фона изображения. @@ -33,6 +34,7 @@ class Genre(YandexMusicObject): titles (:obj:`dict`): Словарь заголовков на разных языках, где ключ - язык. images (:obj:`yandex_music.Images`): Изображение жанра. show_in_menu (:obj:`bool`): Показывать в меню. + show_in_regions (:obj:`list` из :obj:`int`, optional): Список регионов в которых отображается жанр в списках. full_title (:obj:`str`, optional): Полный заголовок. url_part (:obj:`str`, optional): Часть ссылки на жанр для открытия в браузере. color (:obj:`str`, optional): Цвет фона изображения. @@ -51,6 +53,7 @@ class Genre(YandexMusicObject): titles: Dict[str, Optional['Title']], images: Optional['Images'], show_in_menu: bool, + show_in_regions: Optional[list] = None, full_title: Optional[str] = None, url_part: Optional[str] = None, color: Optional[str] = None, @@ -67,6 +70,7 @@ class Genre(YandexMusicObject): self.images = images self.show_in_menu = show_in_menu + self.show_in_regions = show_in_regions self.full_title = full_title self.url_part = url_part self.color = color @@ -116,8 +120,4 @@ class Genre(YandexMusicObject): if not data: return [] - genres = list() - for genre in data: - genres.append(cls.de_json(genre, client)) - - return genres + return [cls.de_json(genre, client) for genre in data] diff --git a/yandex_music/landing/mix_link.py b/yandex_music/landing/mix_link.py index 1837b45..9f42a7a 100644 --- a/yandex_music/landing/mix_link.py +++ b/yandex_music/landing/mix_link.py @@ -22,6 +22,7 @@ class MixLink(YandexMusicObject): background_color (:obj:`str`): Цвет заднего фона. background_image_uri (:obj:`str`): Ссылка на изображение заднего фона. cover_white (:obj:`str`): Ссылка на изображение с обложкой TODO. + cover_uri (:obj:`str`): Ссылка на изображение с обложкой. client (:obj:`yandex_music.Client`): Клиент Yandex Music. Args: @@ -32,6 +33,7 @@ class MixLink(YandexMusicObject): background_color (:obj:`str`): Цвет заднего фона. background_image_uri (:obj:`str`): Ссылка на изображение заднего фона. cover_white (:obj:`str`): Ссылка на изображение с обложкой TODO. + cover_uri (:obj:`str`, optional): Ссылка на изображение с обложкой. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. """ @@ -44,6 +46,7 @@ class MixLink(YandexMusicObject): background_color: str, background_image_uri: str, cover_white: str, + cover_uri: Optional[str] = None, client: Optional['Client'] = None, **kwargs) -> None: self.title = title @@ -54,6 +57,8 @@ class MixLink(YandexMusicObject): self.background_image_uri = background_image_uri self.cover_white = cover_white + self.cover_uri = cover_uri + self.client = client self._id_attrs = (self.url, self.title, self.url_scheme, self.text_color, self.background_color, self.background_image_uri, self.cover_white) diff --git a/yandex_music/playlist/playlist.py b/yandex_music/playlist/playlist.py index 15b5d75..afeb367 100644 --- a/yandex_music/playlist/playlist.py +++ b/yandex_music/playlist/playlist.py @@ -4,7 +4,7 @@ from yandex_music import YandexMusicObject if TYPE_CHECKING: from yandex_music import Client, User, Cover, MadeFor, TrackShort, PlaylistAbsence, PlayCounter,\ - PlaylistRecommendations + PlaylistRecommendations, Artist class Playlist(YandexMusicObject): @@ -41,11 +41,13 @@ class Playlist(YandexMusicObject): duration_ms (:obj:`int`): Длительность в миллисекундах. og_image (:obj:`str`): Ссылка на превью Open Graph. og_title (:obj:`str`): Заголовок Open Graph. + og_description (:obj:`str`, optional): Описание Open Graph. image (:obj:`str`): Изображение TODO. cover_without_text (:obj:`yandex_music.Cover`): Обложка без текста. background_color (:obj:`str`): Цвет заднего фона TODO. text_color (:obj:`str`): Цвет текста TODO. id_for_from (:obj:`str`): Откуда пришло событие (уникальный идентификатор объекта) TODO. + top_artist (:obj:`list` из :obj:`yandex_music.Artist`): Топ артистов TODO. tracks (:obj:`list` из :obj:`yandex_music.TrackShort`): Список треков. prerolls (:obj:`list`): Прерол, проигрываемый перед плейлистом. Присутствует только у персональных плейлистов. likes_count (:obj:`int`): Количество лайков. @@ -85,11 +87,13 @@ class Playlist(YandexMusicObject): duration_ms (:obj:`int`, optional): Длительность в миллисекундах. og_image (:obj:`str`, optional): Ссылка на превью Open Graph. og_title (:obj:`str`, optional): Заголовок Open Graph. + og_description (:obj:`str`, optional): Описание Open Graph. image (:obj:`str`, optional): Изображение TODO. cover_without_text (:obj:`yandex_music.Cover`, optional): Обложка без текста. background_color (:obj:`str`, optional): Цвет заднего фона TODO. text_color (:obj:`str`, optional): Цвет текста TODO. id_for_from (:obj:`str`, optional): Откуда пришло событие (уникальный идентификатор объекта) TODO. + top_artist (:obj:`list` из :obj:`yandex_music.Artist`, optional): Топ артистов TODO. tracks (:obj:`list` из :obj:`yandex_music.TrackShort`, optional): Список треков. prerolls (:obj:`list`, optional): Прерол, проигрываемый перед плейлистом. Присутствует только у персональных плейлистов. @@ -131,11 +135,13 @@ class Playlist(YandexMusicObject): duration_ms: Optional[int] = None, og_image: Optional[str] = None, og_title: Optional[str] = None, + og_description: Optional[str] = None, image: Optional[str] = None, cover_without_text: Optional['Cover'] = None, background_color: Optional[str] = None, text_color: Optional[str] = None, id_for_from: Optional[str] = None, + top_artist: List['Artist'] = None, tracks: List['TrackShort'] = None, prerolls: Optional[list] = None, likes_count: Optional[int] = None, @@ -173,11 +179,13 @@ class Playlist(YandexMusicObject): self.duration_ms = duration_ms self.og_image = og_image self.og_title = og_title + self.og_description = og_description self.image = image self.cover_without_text = cover_without_text self.background_color = background_color self.text_color = text_color self.id_for_from = id_for_from + self.top_artist = top_artist self.tracks = tracks self.prerolls = prerolls self.likes_count = likes_count @@ -274,13 +282,14 @@ class Playlist(YandexMusicObject): return None data = super(Playlist, cls).de_json(data, client) - from yandex_music import User, MadeFor, Cover, PlayCounter, TrackShort, PlaylistAbsence + from yandex_music import User, MadeFor, Cover, PlayCounter, TrackShort, PlaylistAbsence, Artist data['owner'] = User.de_json(data.get('owner'), client) data['cover'] = Cover.de_json(data.get('cover'), client) data['cover_without_text'] = Cover.de_json(data.get('cover_without_text'), client) data['made_for'] = MadeFor.de_json(data.get('made_for'), client) data['tracks'] = TrackShort.de_list(data.get('tracks'), client) data['play_counter'] = PlayCounter.de_json(data.get('play_counter'), client) + data['top_artist'] = Artist.de_list(data.get('top_artist'), client) data['similar_playlists'] = Playlist.de_list(data.get('similar_playlists'), client) data['last_owner_playlists'] = Playlist.de_list(data.get('last_owner_playlists'), client) diff --git a/yandex_music/rotor/station.py b/yandex_music/rotor/station.py index cad1e12..44b8a81 100644 --- a/yandex_music/rotor/station.py +++ b/yandex_music/rotor/station.py @@ -22,6 +22,8 @@ class Station(YandexMusicObject): id_for_from (:obj:`str`): Категория (тип) станции. restrictions (:obj:`yandex_music.Restrictions`): Ограничения для настроек станции старого формата. restrictions2 (:obj:`yandex_music.Restrictions`): Ограничения для настроек станции. + full_image_url (:obj:`str`): Ссылка на полное изображение. + mts_full_image_url (:obj:`str`): Ссылка на полную иконку. parent_id (:obj:`yandex_music.Id`): Уникальный идентификатор станции, являющейся предком текущей. client (:obj:`yandex_music.Client`): Клиент Yandex Music. @@ -34,6 +36,8 @@ class Station(YandexMusicObject): id_for_from (:obj:`str`): Категория (тип) станции. restrictions (:obj:`yandex_music.Restrictions`): Ограничения для настроек станции старого формата. restrictions2 (:obj:`yandex_music.Restrictions`): Ограничения для настроек станции. + full_image_url (:obj:`str`, optional): Ссылка на полное изображение. + mts_full_image_url (:obj:`str`, optional): Ссылка на полную иконку. parent_id (:obj:`yandex_music.Id`, optional): Уникальный идентификатор станции, являющейся предком текущей. client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. **kwargs: Произвольные ключевые аргументы полученные от API. @@ -42,12 +46,14 @@ class Station(YandexMusicObject): def __init__(self, id_: Optional['Id'], name: str, - icon: Optional['Icon'], - mts_icon: Optional['Icon'], - geocell_icon: Optional['Icon'], + icon: 'Icon', + mts_icon: 'Icon', + geocell_icon: 'Icon', id_for_from: str, - restrictions: Optional['Restrictions'], - restrictions2: Optional['Restrictions'], + restrictions: 'Restrictions', + restrictions2: 'Restrictions', + full_image_url: Optional[str] = None, + mts_full_image_url: Optional[str] = None, parent_id: Optional['Id'] = None, client: Optional['Client'] = None, **kwargs) -> None: @@ -60,6 +66,8 @@ class Station(YandexMusicObject): self.restrictions = restrictions self.restrictions2 = restrictions2 + self.full_image_url = full_image_url + self.mts_full_image_url = mts_full_image_url self.parent_id = parent_id self.client = client diff --git a/yandex_music/track/licence_text_part.py b/yandex_music/track/licence_text_part.py new file mode 100644 index 0000000..bdd354b --- /dev/null +++ b/yandex_music/track/licence_text_part.py @@ -0,0 +1,70 @@ +from typing import TYPE_CHECKING, Optional, List + +from yandex_music import YandexMusicObject + +if TYPE_CHECKING: + from yandex_music import Client + + +class LicenceTextPart(YandexMusicObject): + """Класс, представляющий часть текста с ссылкой на лицензионное соглашение. + + Attributes: + text (:obj:`str`): Часть текста (строка). + url (:obj:`str`): Ссылка на лицензионное соглашение. + client (:obj:`yandex_music.Client`): Клиент Yandex Music. + + Args: + text (:obj:`str`): Часть текста (строка). + url (:obj:`str`, optional): Ссылка на лицензионное соглашение. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + **kwargs: Произвольные ключевые аргументы полученные от API. + """ + + def __init__(self, + text: str, + url: Optional[str] = None, + client: Optional['Client'] = None, + **kwargs) -> None: + self.text = text + + self.url = url + + self.client = client + self._id_attrs = (self.text,) + + super().handle_unknown_kwargs(self, **kwargs) + + @classmethod + def de_json(cls, data: dict, client: 'Client') -> Optional['PassportPhone']: + """Десериализация объекта. + + Args: + data (:obj:`dict`): Поля и значения десериализуемого объекта. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + + Returns: + :obj:`yandex_music.LicenceTextPart`: Строка лицензионного соглашения. + """ + if not data: + return None + + data = super(LicenceTextPart, cls).de_json(data, client) + + return cls(client=client, **data) + + @classmethod + def de_list(cls, data: dict, client: 'Client') -> List['LicenceTextPart']: + """Десериализация списка объектов. + + Args: + data (:obj:`list`): Список словарей с полями и значениями десериализуемого объекта. + client (:obj:`yandex_music.Client`, optional): Клиент Yandex Music. + + Returns: + :obj:`list` из :obj:`yandex_music.LicenceTextPart`: Строки текста с ссылкой на лицензионное соглашение. + """ + if not data: + return [] + + return [cls.de_json(row, client) for row in data]