From 11d0b0d72f1b3eb2aa06ece9e0a3668edc49a197 Mon Sep 17 00:00:00 2001 From: FrigaT Date: Fri, 10 Apr 2026 12:12:33 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8C=D1=82?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YandexMusic.API/API/YAlbumAPI.cs | 38 ++ YandexMusic.API/API/YArtistAPI.cs | 71 ++++ YandexMusic.API/API/YCommonAPI.cs | 14 + YandexMusic.API/API/YLabelAPIAsync.cs | 40 +++ YandexMusic.API/API/YLandingAPIAsync.cs | 63 ++++ YandexMusic.API/API/YLibraryAPIAsync.cs | 258 ++++++++++++++ YandexMusic.API/API/YPinsAPIAsync.cs | 33 ++ YandexMusic.API/API/YPlaylistAPIAsync.cs | 335 ++++++++++++++++++ YandexMusic.API/API/YQueueAPIAsync.cs | 73 ++++ YandexMusic.API/API/YRadioAPIAsync.cs | 116 ++++++ YandexMusic.API/API/YSearchAPIAsync.cs | 141 ++++++++ YandexMusic.API/API/YTrackAPIAsync.cs | 308 ++++++++++++++++ YandexMusic.API/API/YUgcAPIAsync.cs | 72 ++++ YandexMusic.API/API/YUserAPIAsync.cs | 303 ++++++++++++++++ YandexMusic.API/Common/AuthStorage.cs | 103 ++++++ YandexMusic.API/Common/DataDownloader.cs | 44 +++ YandexMusic.API/Common/Encryptor.cs | 75 ++++ .../Common/Providers/CommonRequestProvider.cs | 60 ++++ .../Providers/DefaultRequestProvider.cs | 69 ++++ .../Common/Providers/IRequestProvider.cs | 25 ++ .../Common/Providers/MockRequestProvider.cs | 27 ++ .../Ynison/UpperSnakeCaseNamingStrategy.cs | 7 + YandexMusic.API/Common/Ynison/YnisonPlayer.cs | 315 ++++++++++++++++ .../Common/Ynison/YnisonWebSocket.cs | 179 ++++++++++ .../Extensions/API/YAlbumExtensions.cs | 25 ++ .../Extensions/API/YAlbumExtensionsAsync.cs | 30 ++ .../Extensions/API/YArtistExtensions.cs | 36 ++ .../Extensions/API/YArtistExtensionsAsync.cs | 41 +++ .../Extensions/API/YPlaylistExtensions.cs | 56 +++ .../API/YPlaylistExtensionsAsync.cs | 72 ++++ .../API/YStationResultExtensions.cs | 26 ++ .../API/YStationResultExtensionsAsync.cs | 28 ++ .../Extensions/API/YTrackExtensions.cs | 55 +++ .../Extensions/API/YTrackExtensionsAsync.cs | 61 ++++ .../Extensions/HttpRequestHeaderExtensions.cs | 12 + .../Extensions/StringExtensions.cs | 50 +++ .../Models/Account/YAccessToken.cs | 15 + YandexMusic.API/Models/Account/YAccount.cs | 25 ++ .../Models/Account/YAccountResult.cs | 26 ++ YandexMusic.API/Models/Account/YAuthBase.cs | 12 + .../Models/Account/YAuthCaptcha.cs | 28 ++ .../Models/Account/YAuthCaptchaError.cs | 8 + .../Models/Account/YAuthCaptchaErrorCode.cs | 9 + .../Models/Account/YAuthCaptchaOptions.cs | 7 + .../Models/Account/YAuthCaptchaVoice.cs | 10 + YandexMusic.API/Models/Account/YAuthError.cs | 22 ++ YandexMusic.API/Models/Account/YAuthLetter.cs | 9 + .../Models/Account/YAuthLetterStatus.cs | 11 + YandexMusic.API/Models/Account/YAuthMethod.cs | 22 ++ YandexMusic.API/Models/Account/YAuthQr.cs | 11 + .../Models/Account/YAuthQrStatus.cs | 19 + YandexMusic.API/Models/Account/YAuthStatus.cs | 8 + YandexMusic.API/Models/Account/YAuthToken.cs | 11 + YandexMusic.API/Models/Account/YAuthTypes.cs | 47 +++ YandexMusic.API/Models/Account/YBar.cs | 14 + YandexMusic.API/Models/Account/YLoginInfo.cs | 31 ++ .../Models/Account/YPhoneNumber.cs | 20 ++ .../Models/Account/YShortAccountInfo.cs | 81 +++++ YandexMusic.API/Models/Album/YAlbum.cs | 98 +++++ YandexMusic.API/Models/Artist/YArtist.cs | 41 +++ .../Models/Artist/YArtistBriefInfo.cs | 36 ++ .../Models/Artist/YArtistCounts.cs | 10 + .../Models/Artist/YArtistRatings.cs | 9 + .../Models/Artist/YBandlinkScannerLink.cs | 10 + YandexMusic.API/Models/Artist/YConcert.cs | 29 ++ YandexMusic.API/Models/Artist/YDeprecation.cs | 7 + .../Models/Artist/YMetroStation.cs | 9 + YandexMusic.API/Models/Artist/YTracksPage.cs | 12 + YandexMusic.API/Models/Common/Cover/YCover.cs | 71 ++++ .../Models/Common/Cover/YCoverColor.cs | 8 + .../Models/Common/Cover/YCoverError.cs | 7 + .../Models/Common/Cover/YCoverImage.cs | 8 + .../Models/Common/Cover/YCoverMosaic.cs | 8 + .../Models/Common/Cover/YCoverPic.cs | 11 + .../Models/Common/Cover/YCoverType.cs | 17 + YandexMusic.API/Models/Common/YBaseModel.cs | 8 + YandexMusic.API/Models/Common/YButton.cs | 12 + YandexMusic.API/Models/Common/YCashback.cs | 7 + YandexMusic.API/Models/Common/YChart.cs | 12 + YandexMusic.API/Models/Common/YClip.cs | 19 + .../Models/Common/YCloseButtonStyles.cs | 8 + .../Models/Common/YCountsTracks.cs | 9 + YandexMusic.API/Models/Common/YCustomWave.cs | 11 + .../Models/Common/YDerivedColors.cs | 10 + YandexMusic.API/Models/Common/YDescription.cs | 8 + YandexMusic.API/Models/Common/YError.cs | 8 + .../Models/Common/YErrorResponse.cs | 8 + .../Models/Common/YExecutionContext.cs | 57 +++ YandexMusic.API/Models/Common/YExtraAction.cs | 11 + YandexMusic.API/Models/Common/YId.cs | 7 + .../Models/Common/YInvocationInfo.cs | 13 + YandexMusic.API/Models/Common/YLabel.cs | 8 + YandexMusic.API/Models/Common/YLikedCounts.cs | 8 + YandexMusic.API/Models/Common/YLink.cs | 13 + YandexMusic.API/Models/Common/YLinkType.cs | 25 ++ YandexMusic.API/Models/Common/YLyrics.cs | 12 + YandexMusic.API/Models/Common/YLyricsInfo.cs | 8 + YandexMusic.API/Models/Common/YMajor.cs | 8 + YandexMusic.API/Models/Common/YMasterHub.cs | 8 + YandexMusic.API/Models/Common/YMetaType.cs | 8 + YandexMusic.API/Models/Common/YOwner.cs | 11 + YandexMusic.API/Models/Common/YPager.cs | 9 + YandexMusic.API/Models/Common/YPeriod.cs | 8 + YandexMusic.API/Models/Common/YPermissions.cs | 9 + YandexMusic.API/Models/Common/YPhone.cs | 7 + YandexMusic.API/Models/Common/YPlus.cs | 8 + .../Models/Common/YPodcastEpisodeType.cs | 9 + YandexMusic.API/Models/Common/YPrerolls.cs | 8 + YandexMusic.API/Models/Common/YPrice.cs | 10 + YandexMusic.API/Models/Common/YProduct.cs | 22 ++ YandexMusic.API/Models/Common/YProductType.cs | 7 + YandexMusic.API/Models/Common/YProfile.cs | 8 + YandexMusic.API/Models/Common/YReminder.cs | 7 + YandexMusic.API/Models/Common/YResponse.cs | 12 + YandexMusic.API/Models/Common/YRevision.cs | 7 + YandexMusic.API/Models/Common/YSearchType.cs | 56 +++ YandexMusic.API/Models/Common/YSortOrder.cs | 8 + YandexMusic.API/Models/Common/YStats.cs | 8 + .../Models/Common/YStorageDownloadFile.cs | 10 + YandexMusic.API/Models/Common/YStyle.cs | 9 + .../Models/Common/YSubscription.cs | 12 + .../Models/Common/YSubscriptionService.cs | 13 + YandexMusic.API/Models/Common/YTag.cs | 8 + .../Models/Common/YTrackDownloadInfo.cs | 12 + YandexMusic.API/Models/Common/YTrackId.cs | 10 + .../Models/Common/YTrackSharingFlag.cs | 12 + YandexMusic.API/Models/Common/YTrackSource.cs | 18 + YandexMusic.API/Models/Common/YTrailer.cs | 7 + YandexMusic.API/Models/Common/YUser.cs | 17 + YandexMusic.API/Models/Common/YVideo.cs | 11 + YandexMusic.API/Models/Common/YVinyl.cs | 14 + .../Models/Feed/Event/YArtistsFromHistory.cs | 10 + .../Models/Feed/Event/YFeedEvent.cs | 104 ++++++ .../Models/Feed/Event/YFeedEventAlbums.cs | 9 + .../Models/Feed/Event/YFeedEventArtist.cs | 9 + .../Feed/Event/YFeedEventArtistWithArtists.cs | 7 + .../Models/Feed/Event/YFeedEventArtists.cs | 9 + .../Feed/Event/YFeedEventGenreAlbums.cs | 7 + .../Feed/Event/YFeedEventGenreTracks.cs | 7 + .../Feed/Event/YFeedEventGenreTracksTop.cs | 7 + .../Models/Feed/Event/YFeedEventLikeTrack.cs | 9 + .../Feed/Event/YFeedEventNotification.cs | 7 + .../Models/Feed/Event/YFeedEventPromotion.cs | 7 + .../Feed/Event/YFeedEventPromotionType.cs | 8 + .../Feed/Event/YFeedEventSimilarArtists.cs | 10 + .../Feed/Event/YFeedEventSimilarGenre.cs | 8 + .../Models/Feed/Event/YFeedEventTitle.cs | 10 + .../Models/Feed/Event/YFeedEventTitleType.cs | 10 + .../Models/Feed/Event/YFeedEventTitled.cs | 8 + .../Models/Feed/Event/YFeedEventTracks.cs | 9 + .../Models/Feed/Event/YFeedEventType.cs | 59 +++ .../Models/Feed/Event/YFeedPromotion.cs | 24 ++ YandexMusic.API/Models/Feed/YFeed.cs | 16 + YandexMusic.API/Models/Feed/YFeedDay.cs | 14 + .../Models/Feed/YFeedDayTrackWithAds.cs | 10 + .../Models/Feed/YFeedDayTrackWithAdsType.cs | 7 + YandexMusic.API/Models/Feed/YHeadline.cs | 9 + YandexMusic.API/Models/Feed/YHeadlineType.cs | 7 + YandexMusic.API/Models/Label/YLabelAlbums.cs | 11 + YandexMusic.API/Models/Label/YLabelArtists.cs | 11 + .../Entity/Entities/Context/YPlayContext.cs | 58 +++ .../Entities/Context/YPlayContextAlbum.cs | 9 + .../Entities/Context/YPlayContextArtist.cs | 9 + .../Entities/Context/YPlayContextPlaylist.cs | 9 + .../Entities/Context/YPlayContextType.cs | 9 + .../Landing/Entity/Entities/YAlbumMenuItem.cs | 7 + .../Landing/Entity/Entities/YBlockEntity.cs | 7 + .../Landing/Entity/Entities/YCategory.cs | 16 + .../Landing/Entity/Entities/YChartItem.cs | 11 + .../Landing/Entity/Entities/YChartProgress.cs | 10 + .../Landing/Entity/Entities/YClientWidget.cs | 11 + .../Entity/Entities/YLandingEntityAlbum.cs | 9 + .../Entities/YLandingEntityAlbumMenuItem.cs | 7 + .../Entity/Entities/YLandingEntityCategory.cs | 7 + .../Entity/Entities/YLandingEntityChart.cs | 7 + .../Entities/YLandingEntityClientWidget.cs | 7 + .../YLandingEntityPersonalPlaylist.cs | 7 + .../Entities/YLandingEntityPlayContext.cs | 10 + .../Entity/Entities/YLandingEntityPlaylist.cs | 9 + .../Entity/Entities/YLandingEntityPodcast.cs | 10 + .../Entities/YLandingEntityPromotion.cs | 7 + .../Entity/Entities/YLandingEntityStation.cs | 7 + .../Entity/Entities/YLandingStation.cs | 10 + .../Entity/Entities/YPersonalPlaylist.cs | 14 + .../Landing/Entity/Entities/YPodcast.cs | 12 + .../Landing/Entity/Entities/YPromotion.cs | 15 + .../Models/Landing/Entity/YLandingEntity.cs | 90 +++++ .../Landing/Entity/YLandingEntityType.cs | 26 ++ .../Models/Landing/YChildrenLanding.cs | 9 + YandexMusic.API/Models/Landing/YLanding.cs | 10 + .../Models/Landing/YLandingBlock.cs | 21 ++ .../Models/Landing/YLandingBlockData.cs | 7 + .../Models/Landing/YLandingBlockType.cs | 29 ++ .../Models/Landing/YLandingHeaderButton.cs | 8 + .../Landing/YLandingHeaderSpecialBlock.cs | 13 + .../Models/Landing/YLandingPlayContext.cs | 9 + YandexMusic.API/Models/Library/YLibrary.cs | 10 + .../Models/Library/YLibraryAlbum.cs | 8 + .../Models/Library/YLibraryPlaylists.cs | 10 + .../Models/Library/YLibrarySection.cs | 28 ++ .../Models/Library/YLibrarySectionType.cs | 18 + .../Models/Library/YLibraryTrack.cs | 9 + .../Models/Library/YLibraryTracks.cs | 7 + .../Models/Library/YListenedTrack.cs | 10 + .../Models/Library/YRecentlyListened.cs | 12 + .../Library/YRecentlyListenedContext.cs | 10 + YandexMusic.API/Models/Pins/Items/YPin.cs | 68 ++++ .../Models/Pins/Items/YPinAlbumData.cs | 7 + .../Models/Pins/Items/YPinArtistData.cs | 7 + .../Models/Pins/Items/YPinPlaylistData.cs | 7 + .../Models/Pins/Items/YPinWaveData.cs | 7 + YandexMusic.API/Models/Pins/YPinType.cs | 19 + YandexMusic.API/Models/Pins/YPins.cs | 10 + .../Models/Playlist/YArtistPlaylistType.cs | 12 + .../Models/Playlist/YGeneratedPlaylistType.cs | 42 +++ YandexMusic.API/Models/Playlist/YPlaylist.cs | 82 +++++ .../Models/Playlist/YPlaylistChange.cs | 14 + .../Models/Playlist/YPlaylistChangeType.cs | 8 + .../Models/Playlist/YPlaylistMadeFor.cs | 28 ++ .../Models/Playlist/YPlaylistPlayCounter.cs | 10 + .../Models/Playlist/YPlaylistUidPair.cs | 13 + YandexMusic.API/Models/Queue/YContext.cs | 10 + YandexMusic.API/Models/Queue/YNewQueue.cs | 9 + YandexMusic.API/Models/Queue/YQueue.cs | 15 + YandexMusic.API/Models/Queue/YQueueItem.cs | 10 + .../Models/Queue/YQueueItemsContainer.cs | 7 + YandexMusic.API/Models/Queue/YUpdatedQueue.cs | 8 + .../Models/Radio/Restriction/YRestriction.cs | 54 +++ .../Restriction/YRestrictionDiscreteScale.cs | 8 + .../Radio/Restriction/YRestrictionEnum.cs | 7 + .../Radio/Restriction/YRestrictionType.cs | 12 + .../Radio/Restriction/YRestrictionValue.cs | 8 + YandexMusic.API/Models/Radio/YAdParams.cs | 14 + YandexMusic.API/Models/Radio/YBrand.cs | 12 + YandexMusic.API/Models/Radio/YSequenceItem.cs | 12 + YandexMusic.API/Models/Radio/YStation.cs | 18 + YandexMusic.API/Models/Radio/YStationData.cs | 13 + .../Models/Radio/YStationDescription.cs | 17 + .../Models/Radio/YStationFeedback.cs | 11 + .../Models/Radio/YStationFeedbackType.cs | 10 + YandexMusic.API/Models/Radio/YStationIcon.cs | 8 + YandexMusic.API/Models/Radio/YStationId.cs | 8 + .../Models/Radio/YStationRestrictions.cs | 16 + .../Models/Radio/YStationRestrictions2.cs | 14 + .../Models/Radio/YStationSequence.cs | 11 + .../Models/Radio/YStationSettings.cs | 10 + .../Models/Radio/YStationSettings2.cs | 9 + .../Models/Radio/YStationsDashboard.cs | 9 + .../Models/Radio/YTrackParameters.cs | 9 + .../Models/Search/Album/YSearchAlbumModel.cs | 11 + .../Models/Search/Artist/YSearchArtist.cs | 15 + .../Search/Artist/YSearchArtistCounter.cs | 10 + .../Search/Artist/YSearchArtistModel.cs | 11 + .../Search/Playlist/YSearchPlaylistModel.cs | 10 + .../Models/Search/Track/YSearchTrackModel.cs | 13 + .../Models/Search/Track/YSearchTrackType.cs | 7 + .../Models/Search/User/YSearchUserModel.cs | 6 + .../Models/Search/Video/YSearchVideoModel.cs | 13 + YandexMusic.API/Models/Search/YSearch.cs | 35 ++ YandexMusic.API/Models/Search/YSearchBest.cs | 70 ++++ .../Models/Search/YSearchResult.cs | 10 + .../Models/Search/YSearchSuggest.cs | 8 + YandexMusic.API/Models/Track/YTrack.cs | 84 +++++ .../Models/Track/YTrackAlbumPair.cs | 40 +++ .../Models/Track/YTrackContainer.cs | 38 ++ YandexMusic.API/Models/Track/YTrackFade.cs | 10 + .../Models/Track/YTrackNormalization.cs | 8 + .../Models/Track/YTrackNormalizationR128.cs | 8 + .../Models/Track/YTrackPosition.cs | 8 + YandexMusic.API/Models/Track/YTrackSimilar.cs | 8 + .../Models/Track/YTrackSupplement.cs | 12 + YandexMusic.API/Models/Ugc/YUgcUpload.cs | 12 + .../Ynison/Messages/YYnisonErrorMessage.cs | 7 + .../Models/Ynison/Messages/YYnisonMessage.cs | 7 + .../Ynison/Messages/YYnisonMessageType.cs | 12 + .../Messages/YYnisonUpdateFullStateMessage.cs | 7 + .../Ynison/Messages/YYnisonUpdateMessage.cs | 10 + .../YYnisonUpdatePlayerStateMessage.cs | 7 + .../Ynison/Wave/YYnisonEntityOptions.cs | 8 + .../Ynison/Wave/YYnisonPhonotekaSource.cs | 9 + .../Models/Ynison/Wave/YYnisonTrackSource.cs | 9 + .../Ynison/Wave/YYnisonWaveEntityOptional.cs | 7 + .../Models/Ynison/Wave/YYnisonWaveQueue.cs | 9 + .../Models/Ynison/Wave/YYnisonWaveSource.cs | 7 + .../Ynison/Wave/YYnisonWaveSourceType.cs | 10 + .../Models/Ynison/YYnisonDevice.cs | 10 + .../Ynison/YYnisonDeviceCapabilities.cs | 9 + .../Models/Ynison/YYnisonDeviceFull.cs | 10 + .../Models/Ynison/YYnisonDeviceInfo.cs | 11 + .../Models/Ynison/YYnisonDeviceVolumeInfo.cs | 8 + .../Models/Ynison/YYnisonEntityContext.cs | 7 + .../Models/Ynison/YYnisonEntityType.cs | 16 + YandexMusic.API/Models/Ynison/YYnisonError.cs | 11 + .../Models/Ynison/YYnisonErrorDetails.cs | 8 + .../Models/Ynison/YYnisonFullState.cs | 9 + YandexMusic.API/Models/Ynison/YYnisonId.cs | 7 + .../Models/Ynison/YYnisonKeepAliveParams.cs | 8 + .../Models/Ynison/YYnisonPlayableItem.cs | 20 ++ .../Models/Ynison/YYnisonPlayableItemType.cs | 7 + .../Models/Ynison/YYnisonPlayerQueue.cs | 22 ++ .../Models/Ynison/YYnisonPlayerState.cs | 9 + .../Models/Ynison/YYnisonPlayerStateStatus.cs | 11 + YandexMusic.API/Models/Ynison/YYnisonQueue.cs | 9 + .../Models/Ynison/YYnisonQueueOptions.cs | 8 + .../Models/Ynison/YYnisonRedirect.cs | 10 + .../Models/Ynison/YYnisonSession.cs | 7 + YandexMusic.API/Models/Ynison/YYnisonState.cs | 11 + .../Models/Ynison/YYnisonTrackInfo.cs | 7 + .../Models/Ynison/YYnisonVersion.cs | 9 + YandexMusic.API/README.md | 230 ++++++++++++ .../Account/YGetAuthAppPasswordBuilder.cs | 27 ++ .../Account/YGetAuthCaptchaBuilder.cs | 30 ++ .../Account/YGetAuthCookiesBuilder.cs | 37 ++ .../Requests/Account/YGetAuthInfoBuilder.cs | 18 + .../Requests/Account/YGetAuthLetterBuilder.cs | 30 ++ .../Account/YGetAuthLoginCaptchaBuilder.cs | 32 ++ .../Account/YGetAuthLoginLetterBuilder.cs | 25 ++ .../Account/YGetAuthLoginQRBuilder.cs | 25 ++ .../Account/YGetAuthLoginUserBuilder.cs | 25 ++ .../Account/YGetAuthMethodsBuilder.cs | 24 ++ .../Requests/Account/YGetAuthQRBuilder.cs | 26 ++ .../Requests/Account/YGetLoginInfoBuilder.cs | 17 + .../Requests/Account/YGetMusicTokenBuilder.cs | 36 ++ .../Account/YGetShortAccountInifoBuilder.cs | 31 ++ .../Requests/Album/YGetAlbumBuilder.cs | 25 ++ .../Requests/Album/YGetAlbumsBuilder.cs | 25 ++ .../Requests/Artist/YGetArtistBuilder.cs | 25 ++ .../Requests/Artist/YGetArtistTrackBuilder.cs | 32 ++ .../Requests/Artist/YGetArtistsBuilder.cs | 25 ++ .../Common/Attributes/YApiRequestAttribute.cs | 10 + .../Attributes/YBasePathRequestAttribute.cs | 32 ++ .../Attributes/YLoginRequestAttribute.cs | 10 + .../YMobileProxyRequestAttribute.cs | 10 + .../Attributes/YOAuthMobileAttribute.cs | 10 + .../Attributes/YOAuthRequestAttribute.cs | 10 + .../Attributes/YPassportRequestAttribute.cs | 10 + .../Common/Attributes/YRequestAttribute.cs | 27 ++ .../Attributes/YWebApiRequestAttribute.cs | 10 + .../Requests/Common/HttpContext.cs | 26 ++ YandexMusic.API/Requests/Common/YConstants.cs | 11 + YandexMusic.API/Requests/Common/YRequest.cs | 48 +++ .../Requests/Common/YRequestBuilder.cs | 158 +++++++++ .../Requests/Feed/YGetFeedBuilder.cs | 18 + .../Requests/Label/YGetLabelAlbumsBuilder.cs | 33 ++ .../Requests/Label/YGetLabelArtistsBuilder.cs | 33 ++ .../Landing/YGetChildrenLandingBuilder.cs | 18 + .../Requests/Landing/YGetLandingBuilder.cs | 29 ++ .../YGetLibraryRecentlyListenedBuilder.cs | 37 ++ .../Library/YGetLibrarySectionBuilder.cs | 27 ++ .../Requests/Library/YLibraryAddBuilder.cs | 34 ++ .../Requests/Library/YLibraryRemoveBuilder.cs | 34 ++ .../Requests/Pins/YGetPinsBuilder.cs | 18 + .../Requests/Playlist/YGetPlaylistBuilder.cs | 26 ++ .../Playlist/YGetPlaylistByUuidBuilder.cs | 25 ++ .../Playlist/YGetPlaylistFavoritesBuilder.cs | 25 ++ .../Requests/Playlist/YGetPlaylistsBuilder.cs | 25 ++ .../Playlist/YPlaylistChangeBuilder.cs | 35 ++ .../Playlist/YPlaylistCreateBuilder.cs | 33 ++ .../Playlist/YPlaylistRemoveBuilder.cs | 33 ++ .../Playlist/YPlaylistRenameBuilder.cs | 24 ++ .../Requests/Queue/YGetQueueBuilder.cs | 25 ++ .../Requests/Queue/YQueueCreateBuilder.cs | 44 +++ .../Queue/YQueueUpdatePositionBuilder.cs | 42 +++ .../Requests/Queue/YQueuesListBuilder.cs | 26 ++ .../Requests/Radio/YGetStationBuilder.cs | 26 ++ .../Radio/YGetStationTracksBuilder.cs | 39 ++ .../Requests/Radio/YGetStationsBuilder.cs | 18 + .../Radio/YGetStationsDashboardBuilder.cs | 18 + .../Requests/Radio/YSetSettings2Builder.cs | 46 +++ .../Radio/YSetStationFeedbackBuilder.cs | 72 ++++ .../Requests/Search/YSearchBuilder.cs | 29 ++ .../Requests/Search/YSearchSuggestBuilder.cs | 26 ++ .../Requests/Track/YGetTrackSimilarBuilder.cs | 25 ++ .../Track/YGetTrackSupplementBuilder.cs | 25 ++ .../Requests/Track/YGetTracksBuilder.cs | 26 ++ .../Requests/Track/YSendTrackInfoBuilder.cs | 38 ++ .../Track/YStorageDownloadFileBuilder.cs | 43 +++ .../Track/YTrackDownloadInfoBuilder.cs | 32 ++ .../Requests/Ugc/YUgcGetUploadLinkBuilder.cs | 33 ++ .../Requests/Ugc/YUgcUploadBuilder.cs | 32 ++ YandexMusic.API/YandexMusic.API.csproj | 13 + YandexMusic.API/YandexMusicApi.cs | 41 +++ YandexMusic.slnx | 3 + 383 files changed, 9661 insertions(+) create mode 100644 YandexMusic.API/API/YAlbumAPI.cs create mode 100644 YandexMusic.API/API/YArtistAPI.cs create mode 100644 YandexMusic.API/API/YCommonAPI.cs create mode 100644 YandexMusic.API/API/YLabelAPIAsync.cs create mode 100644 YandexMusic.API/API/YLandingAPIAsync.cs create mode 100644 YandexMusic.API/API/YLibraryAPIAsync.cs create mode 100644 YandexMusic.API/API/YPinsAPIAsync.cs create mode 100644 YandexMusic.API/API/YPlaylistAPIAsync.cs create mode 100644 YandexMusic.API/API/YQueueAPIAsync.cs create mode 100644 YandexMusic.API/API/YRadioAPIAsync.cs create mode 100644 YandexMusic.API/API/YSearchAPIAsync.cs create mode 100644 YandexMusic.API/API/YTrackAPIAsync.cs create mode 100644 YandexMusic.API/API/YUgcAPIAsync.cs create mode 100644 YandexMusic.API/API/YUserAPIAsync.cs create mode 100644 YandexMusic.API/Common/AuthStorage.cs create mode 100644 YandexMusic.API/Common/DataDownloader.cs create mode 100644 YandexMusic.API/Common/Encryptor.cs create mode 100644 YandexMusic.API/Common/Providers/CommonRequestProvider.cs create mode 100644 YandexMusic.API/Common/Providers/DefaultRequestProvider.cs create mode 100644 YandexMusic.API/Common/Providers/IRequestProvider.cs create mode 100644 YandexMusic.API/Common/Providers/MockRequestProvider.cs create mode 100644 YandexMusic.API/Common/Ynison/UpperSnakeCaseNamingStrategy.cs create mode 100644 YandexMusic.API/Common/Ynison/YnisonPlayer.cs create mode 100644 YandexMusic.API/Common/Ynison/YnisonWebSocket.cs create mode 100644 YandexMusic.API/Extensions/API/YAlbumExtensions.cs create mode 100644 YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs create mode 100644 YandexMusic.API/Extensions/API/YArtistExtensions.cs create mode 100644 YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs create mode 100644 YandexMusic.API/Extensions/API/YPlaylistExtensions.cs create mode 100644 YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs create mode 100644 YandexMusic.API/Extensions/API/YStationResultExtensions.cs create mode 100644 YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs create mode 100644 YandexMusic.API/Extensions/API/YTrackExtensions.cs create mode 100644 YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs create mode 100644 YandexMusic.API/Extensions/HttpRequestHeaderExtensions.cs create mode 100644 YandexMusic.API/Extensions/StringExtensions.cs create mode 100644 YandexMusic.API/Models/Account/YAccessToken.cs create mode 100644 YandexMusic.API/Models/Account/YAccount.cs create mode 100644 YandexMusic.API/Models/Account/YAccountResult.cs create mode 100644 YandexMusic.API/Models/Account/YAuthBase.cs create mode 100644 YandexMusic.API/Models/Account/YAuthCaptcha.cs create mode 100644 YandexMusic.API/Models/Account/YAuthCaptchaError.cs create mode 100644 YandexMusic.API/Models/Account/YAuthCaptchaErrorCode.cs create mode 100644 YandexMusic.API/Models/Account/YAuthCaptchaOptions.cs create mode 100644 YandexMusic.API/Models/Account/YAuthCaptchaVoice.cs create mode 100644 YandexMusic.API/Models/Account/YAuthError.cs create mode 100644 YandexMusic.API/Models/Account/YAuthLetter.cs create mode 100644 YandexMusic.API/Models/Account/YAuthLetterStatus.cs create mode 100644 YandexMusic.API/Models/Account/YAuthMethod.cs create mode 100644 YandexMusic.API/Models/Account/YAuthQr.cs create mode 100644 YandexMusic.API/Models/Account/YAuthQrStatus.cs create mode 100644 YandexMusic.API/Models/Account/YAuthStatus.cs create mode 100644 YandexMusic.API/Models/Account/YAuthToken.cs create mode 100644 YandexMusic.API/Models/Account/YAuthTypes.cs create mode 100644 YandexMusic.API/Models/Account/YBar.cs create mode 100644 YandexMusic.API/Models/Account/YLoginInfo.cs create mode 100644 YandexMusic.API/Models/Account/YPhoneNumber.cs create mode 100644 YandexMusic.API/Models/Account/YShortAccountInfo.cs create mode 100644 YandexMusic.API/Models/Album/YAlbum.cs create mode 100644 YandexMusic.API/Models/Artist/YArtist.cs create mode 100644 YandexMusic.API/Models/Artist/YArtistBriefInfo.cs create mode 100644 YandexMusic.API/Models/Artist/YArtistCounts.cs create mode 100644 YandexMusic.API/Models/Artist/YArtistRatings.cs create mode 100644 YandexMusic.API/Models/Artist/YBandlinkScannerLink.cs create mode 100644 YandexMusic.API/Models/Artist/YConcert.cs create mode 100644 YandexMusic.API/Models/Artist/YDeprecation.cs create mode 100644 YandexMusic.API/Models/Artist/YMetroStation.cs create mode 100644 YandexMusic.API/Models/Artist/YTracksPage.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCover.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCoverColor.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCoverError.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCoverImage.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCoverMosaic.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCoverPic.cs create mode 100644 YandexMusic.API/Models/Common/Cover/YCoverType.cs create mode 100644 YandexMusic.API/Models/Common/YBaseModel.cs create mode 100644 YandexMusic.API/Models/Common/YButton.cs create mode 100644 YandexMusic.API/Models/Common/YCashback.cs create mode 100644 YandexMusic.API/Models/Common/YChart.cs create mode 100644 YandexMusic.API/Models/Common/YClip.cs create mode 100644 YandexMusic.API/Models/Common/YCloseButtonStyles.cs create mode 100644 YandexMusic.API/Models/Common/YCountsTracks.cs create mode 100644 YandexMusic.API/Models/Common/YCustomWave.cs create mode 100644 YandexMusic.API/Models/Common/YDerivedColors.cs create mode 100644 YandexMusic.API/Models/Common/YDescription.cs create mode 100644 YandexMusic.API/Models/Common/YError.cs create mode 100644 YandexMusic.API/Models/Common/YErrorResponse.cs create mode 100644 YandexMusic.API/Models/Common/YExecutionContext.cs create mode 100644 YandexMusic.API/Models/Common/YExtraAction.cs create mode 100644 YandexMusic.API/Models/Common/YId.cs create mode 100644 YandexMusic.API/Models/Common/YInvocationInfo.cs create mode 100644 YandexMusic.API/Models/Common/YLabel.cs create mode 100644 YandexMusic.API/Models/Common/YLikedCounts.cs create mode 100644 YandexMusic.API/Models/Common/YLink.cs create mode 100644 YandexMusic.API/Models/Common/YLinkType.cs create mode 100644 YandexMusic.API/Models/Common/YLyrics.cs create mode 100644 YandexMusic.API/Models/Common/YLyricsInfo.cs create mode 100644 YandexMusic.API/Models/Common/YMajor.cs create mode 100644 YandexMusic.API/Models/Common/YMasterHub.cs create mode 100644 YandexMusic.API/Models/Common/YMetaType.cs create mode 100644 YandexMusic.API/Models/Common/YOwner.cs create mode 100644 YandexMusic.API/Models/Common/YPager.cs create mode 100644 YandexMusic.API/Models/Common/YPeriod.cs create mode 100644 YandexMusic.API/Models/Common/YPermissions.cs create mode 100644 YandexMusic.API/Models/Common/YPhone.cs create mode 100644 YandexMusic.API/Models/Common/YPlus.cs create mode 100644 YandexMusic.API/Models/Common/YPodcastEpisodeType.cs create mode 100644 YandexMusic.API/Models/Common/YPrerolls.cs create mode 100644 YandexMusic.API/Models/Common/YPrice.cs create mode 100644 YandexMusic.API/Models/Common/YProduct.cs create mode 100644 YandexMusic.API/Models/Common/YProductType.cs create mode 100644 YandexMusic.API/Models/Common/YProfile.cs create mode 100644 YandexMusic.API/Models/Common/YReminder.cs create mode 100644 YandexMusic.API/Models/Common/YResponse.cs create mode 100644 YandexMusic.API/Models/Common/YRevision.cs create mode 100644 YandexMusic.API/Models/Common/YSearchType.cs create mode 100644 YandexMusic.API/Models/Common/YSortOrder.cs create mode 100644 YandexMusic.API/Models/Common/YStats.cs create mode 100644 YandexMusic.API/Models/Common/YStorageDownloadFile.cs create mode 100644 YandexMusic.API/Models/Common/YStyle.cs create mode 100644 YandexMusic.API/Models/Common/YSubscription.cs create mode 100644 YandexMusic.API/Models/Common/YSubscriptionService.cs create mode 100644 YandexMusic.API/Models/Common/YTag.cs create mode 100644 YandexMusic.API/Models/Common/YTrackDownloadInfo.cs create mode 100644 YandexMusic.API/Models/Common/YTrackId.cs create mode 100644 YandexMusic.API/Models/Common/YTrackSharingFlag.cs create mode 100644 YandexMusic.API/Models/Common/YTrackSource.cs create mode 100644 YandexMusic.API/Models/Common/YTrailer.cs create mode 100644 YandexMusic.API/Models/Common/YUser.cs create mode 100644 YandexMusic.API/Models/Common/YVideo.cs create mode 100644 YandexMusic.API/Models/Common/YVinyl.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YArtistsFromHistory.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEvent.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventAlbums.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventArtist.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventArtistWithArtists.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventArtists.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventGenreAlbums.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracks.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracksTop.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventLikeTrack.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventNotification.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventPromotion.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventPromotionType.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventSimilarArtists.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventSimilarGenre.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventTitle.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventTitleType.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventTitled.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventTracks.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedEventType.cs create mode 100644 YandexMusic.API/Models/Feed/Event/YFeedPromotion.cs create mode 100644 YandexMusic.API/Models/Feed/YFeed.cs create mode 100644 YandexMusic.API/Models/Feed/YFeedDay.cs create mode 100644 YandexMusic.API/Models/Feed/YFeedDayTrackWithAds.cs create mode 100644 YandexMusic.API/Models/Feed/YFeedDayTrackWithAdsType.cs create mode 100644 YandexMusic.API/Models/Feed/YHeadline.cs create mode 100644 YandexMusic.API/Models/Feed/YHeadlineType.cs create mode 100644 YandexMusic.API/Models/Label/YLabelAlbums.cs create mode 100644 YandexMusic.API/Models/Label/YLabelArtists.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContext.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextAlbum.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextArtist.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextPlaylist.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextType.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YAlbumMenuItem.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YBlockEntity.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YCategory.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YChartItem.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YChartProgress.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YClientWidget.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbum.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbumMenuItem.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityCategory.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityChart.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityClientWidget.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPersonalPlaylist.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlayContext.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlaylist.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPodcast.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPromotion.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityStation.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YLandingStation.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YPersonalPlaylist.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YPodcast.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/Entities/YPromotion.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/YLandingEntity.cs create mode 100644 YandexMusic.API/Models/Landing/Entity/YLandingEntityType.cs create mode 100644 YandexMusic.API/Models/Landing/YChildrenLanding.cs create mode 100644 YandexMusic.API/Models/Landing/YLanding.cs create mode 100644 YandexMusic.API/Models/Landing/YLandingBlock.cs create mode 100644 YandexMusic.API/Models/Landing/YLandingBlockData.cs create mode 100644 YandexMusic.API/Models/Landing/YLandingBlockType.cs create mode 100644 YandexMusic.API/Models/Landing/YLandingHeaderButton.cs create mode 100644 YandexMusic.API/Models/Landing/YLandingHeaderSpecialBlock.cs create mode 100644 YandexMusic.API/Models/Landing/YLandingPlayContext.cs create mode 100644 YandexMusic.API/Models/Library/YLibrary.cs create mode 100644 YandexMusic.API/Models/Library/YLibraryAlbum.cs create mode 100644 YandexMusic.API/Models/Library/YLibraryPlaylists.cs create mode 100644 YandexMusic.API/Models/Library/YLibrarySection.cs create mode 100644 YandexMusic.API/Models/Library/YLibrarySectionType.cs create mode 100644 YandexMusic.API/Models/Library/YLibraryTrack.cs create mode 100644 YandexMusic.API/Models/Library/YLibraryTracks.cs create mode 100644 YandexMusic.API/Models/Library/YListenedTrack.cs create mode 100644 YandexMusic.API/Models/Library/YRecentlyListened.cs create mode 100644 YandexMusic.API/Models/Library/YRecentlyListenedContext.cs create mode 100644 YandexMusic.API/Models/Pins/Items/YPin.cs create mode 100644 YandexMusic.API/Models/Pins/Items/YPinAlbumData.cs create mode 100644 YandexMusic.API/Models/Pins/Items/YPinArtistData.cs create mode 100644 YandexMusic.API/Models/Pins/Items/YPinPlaylistData.cs create mode 100644 YandexMusic.API/Models/Pins/Items/YPinWaveData.cs create mode 100644 YandexMusic.API/Models/Pins/YPinType.cs create mode 100644 YandexMusic.API/Models/Pins/YPins.cs create mode 100644 YandexMusic.API/Models/Playlist/YArtistPlaylistType.cs create mode 100644 YandexMusic.API/Models/Playlist/YGeneratedPlaylistType.cs create mode 100644 YandexMusic.API/Models/Playlist/YPlaylist.cs create mode 100644 YandexMusic.API/Models/Playlist/YPlaylistChange.cs create mode 100644 YandexMusic.API/Models/Playlist/YPlaylistChangeType.cs create mode 100644 YandexMusic.API/Models/Playlist/YPlaylistMadeFor.cs create mode 100644 YandexMusic.API/Models/Playlist/YPlaylistPlayCounter.cs create mode 100644 YandexMusic.API/Models/Playlist/YPlaylistUidPair.cs create mode 100644 YandexMusic.API/Models/Queue/YContext.cs create mode 100644 YandexMusic.API/Models/Queue/YNewQueue.cs create mode 100644 YandexMusic.API/Models/Queue/YQueue.cs create mode 100644 YandexMusic.API/Models/Queue/YQueueItem.cs create mode 100644 YandexMusic.API/Models/Queue/YQueueItemsContainer.cs create mode 100644 YandexMusic.API/Models/Queue/YUpdatedQueue.cs create mode 100644 YandexMusic.API/Models/Radio/Restriction/YRestriction.cs create mode 100644 YandexMusic.API/Models/Radio/Restriction/YRestrictionDiscreteScale.cs create mode 100644 YandexMusic.API/Models/Radio/Restriction/YRestrictionEnum.cs create mode 100644 YandexMusic.API/Models/Radio/Restriction/YRestrictionType.cs create mode 100644 YandexMusic.API/Models/Radio/Restriction/YRestrictionValue.cs create mode 100644 YandexMusic.API/Models/Radio/YAdParams.cs create mode 100644 YandexMusic.API/Models/Radio/YBrand.cs create mode 100644 YandexMusic.API/Models/Radio/YSequenceItem.cs create mode 100644 YandexMusic.API/Models/Radio/YStation.cs create mode 100644 YandexMusic.API/Models/Radio/YStationData.cs create mode 100644 YandexMusic.API/Models/Radio/YStationDescription.cs create mode 100644 YandexMusic.API/Models/Radio/YStationFeedback.cs create mode 100644 YandexMusic.API/Models/Radio/YStationFeedbackType.cs create mode 100644 YandexMusic.API/Models/Radio/YStationIcon.cs create mode 100644 YandexMusic.API/Models/Radio/YStationId.cs create mode 100644 YandexMusic.API/Models/Radio/YStationRestrictions.cs create mode 100644 YandexMusic.API/Models/Radio/YStationRestrictions2.cs create mode 100644 YandexMusic.API/Models/Radio/YStationSequence.cs create mode 100644 YandexMusic.API/Models/Radio/YStationSettings.cs create mode 100644 YandexMusic.API/Models/Radio/YStationSettings2.cs create mode 100644 YandexMusic.API/Models/Radio/YStationsDashboard.cs create mode 100644 YandexMusic.API/Models/Radio/YTrackParameters.cs create mode 100644 YandexMusic.API/Models/Search/Album/YSearchAlbumModel.cs create mode 100644 YandexMusic.API/Models/Search/Artist/YSearchArtist.cs create mode 100644 YandexMusic.API/Models/Search/Artist/YSearchArtistCounter.cs create mode 100644 YandexMusic.API/Models/Search/Artist/YSearchArtistModel.cs create mode 100644 YandexMusic.API/Models/Search/Playlist/YSearchPlaylistModel.cs create mode 100644 YandexMusic.API/Models/Search/Track/YSearchTrackModel.cs create mode 100644 YandexMusic.API/Models/Search/Track/YSearchTrackType.cs create mode 100644 YandexMusic.API/Models/Search/User/YSearchUserModel.cs create mode 100644 YandexMusic.API/Models/Search/Video/YSearchVideoModel.cs create mode 100644 YandexMusic.API/Models/Search/YSearch.cs create mode 100644 YandexMusic.API/Models/Search/YSearchBest.cs create mode 100644 YandexMusic.API/Models/Search/YSearchResult.cs create mode 100644 YandexMusic.API/Models/Search/YSearchSuggest.cs create mode 100644 YandexMusic.API/Models/Track/YTrack.cs create mode 100644 YandexMusic.API/Models/Track/YTrackAlbumPair.cs create mode 100644 YandexMusic.API/Models/Track/YTrackContainer.cs create mode 100644 YandexMusic.API/Models/Track/YTrackFade.cs create mode 100644 YandexMusic.API/Models/Track/YTrackNormalization.cs create mode 100644 YandexMusic.API/Models/Track/YTrackNormalizationR128.cs create mode 100644 YandexMusic.API/Models/Track/YTrackPosition.cs create mode 100644 YandexMusic.API/Models/Track/YTrackSimilar.cs create mode 100644 YandexMusic.API/Models/Track/YTrackSupplement.cs create mode 100644 YandexMusic.API/Models/Ugc/YUgcUpload.cs create mode 100644 YandexMusic.API/Models/Ynison/Messages/YYnisonErrorMessage.cs create mode 100644 YandexMusic.API/Models/Ynison/Messages/YYnisonMessage.cs create mode 100644 YandexMusic.API/Models/Ynison/Messages/YYnisonMessageType.cs create mode 100644 YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateFullStateMessage.cs create mode 100644 YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateMessage.cs create mode 100644 YandexMusic.API/Models/Ynison/Messages/YYnisonUpdatePlayerStateMessage.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonEntityOptions.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonPhonotekaSource.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonTrackSource.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonWaveEntityOptional.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonWaveQueue.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSource.cs create mode 100644 YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSourceType.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonDevice.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonDeviceCapabilities.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonDeviceFull.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonDeviceInfo.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonDeviceVolumeInfo.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonEntityContext.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonEntityType.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonError.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonErrorDetails.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonFullState.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonId.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonKeepAliveParams.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonPlayableItem.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonPlayableItemType.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonPlayerQueue.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonPlayerState.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonPlayerStateStatus.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonQueue.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonQueueOptions.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonRedirect.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonSession.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonState.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonTrackInfo.cs create mode 100644 YandexMusic.API/Models/Ynison/YYnisonVersion.cs create mode 100644 YandexMusic.API/README.md create mode 100644 YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthInfoBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthMethodsBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetMusicTokenBuilder.cs create mode 100644 YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs create mode 100644 YandexMusic.API/Requests/Album/YGetAlbumBuilder.cs create mode 100644 YandexMusic.API/Requests/Album/YGetAlbumsBuilder.cs create mode 100644 YandexMusic.API/Requests/Artist/YGetArtistBuilder.cs create mode 100644 YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs create mode 100644 YandexMusic.API/Requests/Artist/YGetArtistsBuilder.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YApiRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YBasePathRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YLoginRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YMobileProxyRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YOAuthMobileAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YOAuthRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YPassportRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/Attributes/YWebApiRequestAttribute.cs create mode 100644 YandexMusic.API/Requests/Common/HttpContext.cs create mode 100644 YandexMusic.API/Requests/Common/YConstants.cs create mode 100644 YandexMusic.API/Requests/Common/YRequest.cs create mode 100644 YandexMusic.API/Requests/Common/YRequestBuilder.cs create mode 100644 YandexMusic.API/Requests/Feed/YGetFeedBuilder.cs create mode 100644 YandexMusic.API/Requests/Label/YGetLabelAlbumsBuilder.cs create mode 100644 YandexMusic.API/Requests/Label/YGetLabelArtistsBuilder.cs create mode 100644 YandexMusic.API/Requests/Landing/YGetChildrenLandingBuilder.cs create mode 100644 YandexMusic.API/Requests/Landing/YGetLandingBuilder.cs create mode 100644 YandexMusic.API/Requests/Library/YGetLibraryRecentlyListenedBuilder.cs create mode 100644 YandexMusic.API/Requests/Library/YGetLibrarySectionBuilder.cs create mode 100644 YandexMusic.API/Requests/Library/YLibraryAddBuilder.cs create mode 100644 YandexMusic.API/Requests/Library/YLibraryRemoveBuilder.cs create mode 100644 YandexMusic.API/Requests/Pins/YGetPinsBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YGetPlaylistBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YGetPlaylistByUuidBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YGetPlaylistFavoritesBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YGetPlaylistsBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YPlaylistChangeBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YPlaylistCreateBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YPlaylistRemoveBuilder.cs create mode 100644 YandexMusic.API/Requests/Playlist/YPlaylistRenameBuilder.cs create mode 100644 YandexMusic.API/Requests/Queue/YGetQueueBuilder.cs create mode 100644 YandexMusic.API/Requests/Queue/YQueueCreateBuilder.cs create mode 100644 YandexMusic.API/Requests/Queue/YQueueUpdatePositionBuilder.cs create mode 100644 YandexMusic.API/Requests/Queue/YQueuesListBuilder.cs create mode 100644 YandexMusic.API/Requests/Radio/YGetStationBuilder.cs create mode 100644 YandexMusic.API/Requests/Radio/YGetStationTracksBuilder.cs create mode 100644 YandexMusic.API/Requests/Radio/YGetStationsBuilder.cs create mode 100644 YandexMusic.API/Requests/Radio/YGetStationsDashboardBuilder.cs create mode 100644 YandexMusic.API/Requests/Radio/YSetSettings2Builder.cs create mode 100644 YandexMusic.API/Requests/Radio/YSetStationFeedbackBuilder.cs create mode 100644 YandexMusic.API/Requests/Search/YSearchBuilder.cs create mode 100644 YandexMusic.API/Requests/Search/YSearchSuggestBuilder.cs create mode 100644 YandexMusic.API/Requests/Track/YGetTrackSimilarBuilder.cs create mode 100644 YandexMusic.API/Requests/Track/YGetTrackSupplementBuilder.cs create mode 100644 YandexMusic.API/Requests/Track/YGetTracksBuilder.cs create mode 100644 YandexMusic.API/Requests/Track/YSendTrackInfoBuilder.cs create mode 100644 YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs create mode 100644 YandexMusic.API/Requests/Track/YTrackDownloadInfoBuilder.cs create mode 100644 YandexMusic.API/Requests/Ugc/YUgcGetUploadLinkBuilder.cs create mode 100644 YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs create mode 100644 YandexMusic.API/YandexMusic.API.csproj create mode 100644 YandexMusic.API/YandexMusicApi.cs create mode 100644 YandexMusic.slnx diff --git a/YandexMusic.API/API/YAlbumAPI.cs b/YandexMusic.API/API/YAlbumAPI.cs new file mode 100644 index 0000000..5d2d11f --- /dev/null +++ b/YandexMusic.API/API/YAlbumAPI.cs @@ -0,0 +1,38 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Album; + +namespace YandexMusic.API; + +/// +/// API для взаимодействия с альбомами +/// +public class YAlbumAPI : YCommonAPI +{ + public YAlbumAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// Получает альбом по идентификатору. + /// Хранилище авторизации. + /// Идентификатор альбома. + /// + public Task> GetAsync(AuthStorage storage, string albumId) + { + return new YGetAlbumBuilder(api, storage) + .Build(albumId) + .GetResponseAsync(); + } + + /// Получение альбомов по списку идентификаторов. + /// Хранилище авторизации. + /// Идентификаторы альбомов. + /// + public Task>> GetAsync(AuthStorage storage, IEnumerable albumIds) + { + return new YGetAlbumsBuilder(api, storage) + .Build(albumIds) + .GetResponseAsync(); + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YArtistAPI.cs b/YandexMusic.API/API/YArtistAPI.cs new file mode 100644 index 0000000..f132417 --- /dev/null +++ b/YandexMusic.API/API/YArtistAPI.cs @@ -0,0 +1,71 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Artist; + +namespace YandexMusic.API; + +/// +/// API для взаимодействия с исполнителями +/// +public class YArtistAPI : YCommonAPI +{ + public YArtistAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение исполнителя + /// + /// Хранилище + /// Идентификатор + public Task> GetAsync(AuthStorage storage, string artistId) + { + return new YGetArtistBuilder(api, storage) + .Build(artistId) + .GetResponseAsync(); + } + + /// + /// Получение исполнителей + /// + /// Хранилище + /// Идентификаторы + public Task>> GetAsync(AuthStorage storage, IEnumerable artistIds) + { + return new YGetArtistsBuilder(api, storage) + .Build(artistIds) + .GetResponseAsync(); + } + + /// + /// Получение треков исполнителя с пагинацией + /// + /// Треки поставляются по штук на страницу, + /// для получения всех треков необходимо использовать метод + /// + /// + /// Хранилище + /// Идентификатор исполнителя + /// Страница ответов + /// Количество треков на странице ответов + public Task> GetTracksAsync(AuthStorage storage, string artistId, int page = 0, int pageSize = 20) + { + return new YGetArtistTrackBuilder(api, storage) + .Build((artistId, page, pageSize)) + .GetResponseAsync(); + } + + /// + /// Получение всех треков исполнителя + /// + /// Хранилище + /// Идентификатор исполнителя + public async Task> GetAllTracksAsync(AuthStorage storage, string artistId) + { + YResponse response = await GetAsync(storage, artistId); + return await GetTracksAsync(storage, artistId, pageSize: response.Result.Artist.Counts.Tracks); + } + + +} \ No newline at end of file diff --git a/YandexMusic.API/API/YCommonAPI.cs b/YandexMusic.API/API/YCommonAPI.cs new file mode 100644 index 0000000..d066141 --- /dev/null +++ b/YandexMusic.API/API/YCommonAPI.cs @@ -0,0 +1,14 @@ +namespace YandexMusic.API; + +/// +/// Родительский класс для ветки API +/// +public class YCommonAPI +{ + protected YandexMusicApi api; + + public YCommonAPI(YandexMusicApi yandex) + { + api = yandex; + } +} diff --git a/YandexMusic.API/API/YLabelAPIAsync.cs b/YandexMusic.API/API/YLabelAPIAsync.cs new file mode 100644 index 0000000..eeac03b --- /dev/null +++ b/YandexMusic.API/API/YLabelAPIAsync.cs @@ -0,0 +1,40 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Label; +using YandexMusic.API.Requests.Label; + +namespace YandexMusic.API +{ + public partial class YLabelAPI : YCommonAPI + { + public YLabelAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Постраничное получение альбомов лейбла + /// + /// Хранилище + /// Лейбл + /// Страница + public Task> GetAlbumsByLabelAsync(AuthStorage storage, YLabel label, int page) + { + return new YGetLabelAlbumsBuilder(api, storage) + .Build((label, page)) + .GetResponseAsync(); + } + + /// + /// Постраничное получение артистов лейбла + /// + /// Хранилище + /// Лейбл + /// Страница + public Task> GetArtistsByLabelAsync(AuthStorage storage, YLabel label, int page) + { + return new YGetLabelArtistsBuilder(api, storage) + .Build((label, page)) + .GetResponseAsync(); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YLandingAPIAsync.cs b/YandexMusic.API/API/YLandingAPIAsync.cs new file mode 100644 index 0000000..8ff4fe9 --- /dev/null +++ b/YandexMusic.API/API/YLandingAPIAsync.cs @@ -0,0 +1,63 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Feed; +using YandexMusic.API.Models.Landing; +using YandexMusic.API.Requests.Feed; +using YandexMusic.API.Requests.Landing; + +namespace YandexMusic.API +{ + /// + /// API для взаимодействия с главной страницей + /// + public partial class YLandingAPI : YCommonAPI + { + + + public YLandingAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение персональных списков + /// + /// Хранилище + /// Типы запрашиваемых блоков + /// + public Task> GetAsync(AuthStorage storage, params YLandingBlockType[] blocks) + { + if (blocks == null) + return null; + + return new YGetLandingBuilder(api, storage) + .Build(blocks) + .GetResponseAsync(); + } + + /// + /// Получение ленты + /// + /// Хранилище + /// + public Task> GetFeedAsync(AuthStorage storage) + { + return new YGetFeedBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Получение лендинга детского раздела + /// + /// Хранилище + /// + public Task> GetChildrenLandingAsync(AuthStorage storage) + { + return new YGetChildrenLandingBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YLibraryAPIAsync.cs b/YandexMusic.API/API/YLibraryAPIAsync.cs new file mode 100644 index 0000000..46b5209 --- /dev/null +++ b/YandexMusic.API/API/YLibraryAPIAsync.cs @@ -0,0 +1,258 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Landing.Entity.Entities.Context; +using YandexMusic.API.Models.Library; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Library; + +namespace YandexMusic.API +{ + /// + /// API для взаимодействия с библиотекой + /// + public partial class YLibraryAPI : YCommonAPI + { + #region Вспомогательные функции + + /// + /// Получение секции библиотеки + /// + /// Тип объекта библиотеки + /// Хранилище + /// Секция + /// Тип + /// Список объектов из секции + private Task> GetLibrarySection(AuthStorage storage, YLibrarySection section, YLibrarySectionType type = YLibrarySectionType.Likes) + { + return new YGetLibrarySectionBuilder(api, storage) + .Build((section, type)) + .GetResponseAsync(); + } + + #endregion Вспомогательные функции + + + + public YLibraryAPI(YandexMusicApi yandex) : base(yandex) + { + } + + #region Лайки + + /// + /// Получение лайкнутых треков + /// + /// Хранилище + /// + public Task> GetLikedTracksAsync(AuthStorage storage) + { + return GetLibrarySection(storage, YLibrarySection.Tracks); + } + + /// + /// Получение лайкнутых альбомов + /// + /// Хранилище + /// + public Task>> GetLikedAlbumsAsync(AuthStorage storage) + { + return GetLibrarySection>(storage, YLibrarySection.Albums); + } + + /// + /// Получение лайкнутых исполнителей + /// + /// Хранилище + /// + public Task>> GetLikedArtistsAsync(AuthStorage storage) + { + return GetLibrarySection>(storage, YLibrarySection.Artists); + } + + /// + /// Получение лайкнутых плейлистов + /// + /// Хранилище + /// + public Task>> GetLikedPlaylistsAsync(AuthStorage storage) + { + return GetLibrarySection>(storage, YLibrarySection.Playlists); + } + + #endregion Лайки + + #region Дизлайки + + /// + /// Получение дизлайкнутых треков + /// + /// Хранилище + /// + public Task> GetDislikedTracksAsync(AuthStorage storage) + { + return GetLibrarySection(storage, YLibrarySection.Tracks, YLibrarySectionType.Dislikes); + } + + /// + /// Получение дизлайкнутых исполнителей + /// + /// Хранилище + /// + public Task>> GetDislikedArtistsAsync(AuthStorage storage) + { + return GetLibrarySection>(storage, YLibrarySection.Artists, YLibrarySectionType.Dislikes); + } + + #endregion Дизлайки + + #region Добавление в списки лайков/дизлайков + + /// + /// Добавить трек в список лайкнутых + /// + /// Хранилище + /// Трек + /// + public Task> AddTrackLikeAsync(AuthStorage storage, YTrack track) + { + return new YLibraryAddBuilder(api, storage) + .Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Удалить трек из списка лайкнутых + /// + /// Хранилище + /// Трек + /// + public Task> RemoveTrackLikeAsync(AuthStorage storage, YTrack track) + { + return new YLibraryRemoveBuilder(api, storage) + .Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Добавить трек в список дизлайкнутых + /// + /// Хранилище + /// Трек + /// + public Task> AddTrackDislikeAsync(AuthStorage storage, YTrack track) + { + return new YLibraryAddBuilder(api, storage) + .Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Dislikes)) + .GetResponseAsync(); + } + + /// + /// Удалить трек из списка дизлайкнутых + /// + /// Хранилище + /// Трек + /// + public Task> RemoveTrackDislikeAsync(AuthStorage storage, YTrack track) + { + return new YLibraryRemoveBuilder(api, storage) + .Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Dislikes)) + .GetResponseAsync(); + } + + /// + /// Добавить альбом в список лайкнутых + /// + /// Хранилище + /// Альбом + /// + public Task> AddAlbumLikeAsync(AuthStorage storage, YAlbum album) + { + return new YLibraryAddBuilder(api, storage) + .Build((album.Id, YLibrarySection.Albums, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Удалить альбом из списка лайкнутых + /// + /// Хранилище + /// Альбом + /// + public Task> RemoveAlbumLikeAsync(AuthStorage storage, YAlbum album) + { + return new YLibraryRemoveBuilder(api, storage) + .Build((album.Id, YLibrarySection.Albums, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Добавить исполнителя в список лайкнутых + /// + /// Хранилище + /// Исполнитель + /// + public Task> AddArtistLikeAsync(AuthStorage storage, YArtist artist) + { + return new YLibraryAddBuilder(api, storage) + .Build((artist.Id, YLibrarySection.Artists, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Удалить исполнителя из списка лайкнутых + /// + /// Хранилище + /// Исполнитель + /// + public Task> RemoveArtistLikeAsync(AuthStorage storage, YArtist artist) + { + return new YLibraryRemoveBuilder(api, storage) + .Build((artist.Id, YLibrarySection.Artists, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Добавить плейлист в список лайкнутых + /// + /// Хранилище + /// Плейлист + /// + public Task> AddPlaylistLikeAsync(AuthStorage storage, YPlaylist playlist) + { + return new YLibraryAddBuilder(api, storage) + .Build((playlist.GetKey().ToString(), YLibrarySection.Playlists, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + /// + /// Удалить плейлист из списка лайкнутых + /// + /// Хранилище + /// Плейлист + /// + public Task> RemovePlaylistLikeAsync(AuthStorage storage, YPlaylist playlist) + { + return new YLibraryRemoveBuilder(api, storage) + .Build((playlist.GetKey().ToString(), YLibrarySection.Playlists, YLibrarySectionType.Likes)) + .GetResponseAsync(); + } + + #endregion Добавление/удаление в списки лайков/дизлайков + + #region Получение списка "Вы недавно слушали" + + public Task> GetRecentlyListenedAsync(AuthStorage storage, IEnumerable contextTypes, int trackCount, int contextCount) + { + return new YGetLibraryRecentlyListenedBuilder(api, storage) + .Build((contextTypes, trackCount, contextCount)) + .GetResponseAsync(); + } + + #endregion Получение списка "Вы недавно слушали" + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YPinsAPIAsync.cs b/YandexMusic.API/API/YPinsAPIAsync.cs new file mode 100644 index 0000000..279e323 --- /dev/null +++ b/YandexMusic.API/API/YPinsAPIAsync.cs @@ -0,0 +1,33 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Pins; +using YandexMusic.API.Requests.Pins; + +namespace YandexMusic.API +{ + /// + /// API для взаимодействия с прикреплёнными объектами + /// + public partial class YPinsAPI : YCommonAPI + { + + + public YPinsAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение списка прикреплённых объектов + /// + /// Хранилище + /// + public Task> GetAsync(AuthStorage storage) + { + return new YGetPinsBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YPlaylistAPIAsync.cs b/YandexMusic.API/API/YPlaylistAPIAsync.cs new file mode 100644 index 0000000..cf94f73 --- /dev/null +++ b/YandexMusic.API/API/YPlaylistAPIAsync.cs @@ -0,0 +1,335 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Landing; +using YandexMusic.API.Models.Landing.Entity.Entities; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Playlist; + +namespace YandexMusic.API +{ + /// + /// API для взамодействия с плейлистами + /// + public partial class YPlaylistAPI : YCommonAPI + { + #region Вспомогательные функции + + /// + /// Получение персональных плейлистов + /// + /// Хранилище + /// Тип + /// Плейлист + private async Task> GetPersonalPlaylist(AuthStorage storage, YGeneratedPlaylistType type) + { + List> list = await GetPersonalPlaylistsAsync(storage); + return list.FirstOrDefault(e => string.Equals(e.Result.GeneratedPlaylistType, type.ToString(), StringComparison.CurrentCultureIgnoreCase)); + } + + /// + /// Изменение плейлиста + /// + /// Хранилище + /// Плейлист + /// Список изменений + /// Плейлист после изменений + private Task> ChangePlaylist(AuthStorage storage, YPlaylist playlist, IEnumerable changes) + { + return new YPlaylistChangeBuilder(api, storage) + .Build((playlist, changes)) + .GetResponseAsync(); + } + + private IEnumerable RemoveIdentical(IEnumerable tracks) + { + return tracks.Distinct(); + } + + #endregion Вспомогательные функции + + + + public YPlaylistAPI(YandexMusicApi yandex) : base(yandex) + { + } + + #region Список с главной + + /// + /// Получение списка персональных плейлистов + /// + /// Хранилище + /// + public async Task>> GetPersonalPlaylistsAsync(AuthStorage storage) + { + YResponse landing = await api.Landing.GetAsync(storage, YLandingBlockType.PersonalPlaylists); + + IEnumerable>> tasks = landing + .Result + .Blocks + .FirstOrDefault(b => b.Type == YLandingBlockType.PersonalPlaylists) + ?.Entities + .Select(e => api.Playlist.GetAsync(storage, ((YLandingEntityPersonalPlaylist)e).Data?.Data)); + + return tasks == null + ? new List>() + : new List>(await Task.WhenAll(tasks)); + } + + #endregion Список с главной + + #region Стандартные плейлисты + + /// + /// Избранное + /// + /// Хранилище + /// + public Task>> FavoritesAsync(AuthStorage storage) + { + return new YGetPlaylistFavoritesBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Плейлист дня + /// + /// Хранилище + /// + public Task> OfTheDayAsync(AuthStorage storage) + { + return GetPersonalPlaylist(storage, YGeneratedPlaylistType.PlaylistOfTheDay); + } + + /// + /// Дежавю + /// + /// Хранилище + /// + public Task> DejaVuAsync(AuthStorage storage) + { + return GetPersonalPlaylist(storage, YGeneratedPlaylistType.NeverHeard); + } + + /// + /// Премьера + /// + /// Хранилище + /// + public Task> PremiereAsync(AuthStorage storage) + { + return GetPersonalPlaylist(storage, YGeneratedPlaylistType.RecentTracks); + } + + /// + /// Тайник + /// + /// Хранилище + /// + public Task> MissedAsync(AuthStorage storage) + { + return GetPersonalPlaylist(storage, YGeneratedPlaylistType.MissedLikes); + } + + /// + /// Кинопоиск + /// + /// Хранилище + /// + public Task> KinopoiskAsync(AuthStorage storage) + { + return GetPersonalPlaylist(storage, YGeneratedPlaylistType.Kinopoisk); + } + + #endregion Стандартные плейлисты + + #region Получение плейлиста + + /// + /// Получение плейлиста + /// + /// Хранилище + /// Uid пользователя-владельца плейлиста + /// Тип + /// + public Task> GetAsync(AuthStorage storage, string user, string kind) + { + return new YGetPlaylistBuilder(api, storage) + .Build((user, kind)) + .GetResponseAsync(); + } + + /// + /// Получение плейлиста по uuid + /// + /// Хранилище + /// uuid + /// + public Task> GetAsync(AuthStorage storage, string uuid) + { + return new YGetPlaylistByUuidBuilder(api, storage) + .Build(uuid) + .GetResponseAsync(); + } + + /// + /// Получение плейлистов + /// + /// Хранилище + /// Список пар пользователь:тип + /// + public Task>> GetAsync(AuthStorage storage, IEnumerable<(string user, string kind)> ids) + { + return new YGetPlaylistsBuilder(api, storage) + .Build(ids) + .GetResponseAsync(); + } + + /// + /// Получение плейлиста + /// + /// Хранилище + /// Описание плейлиста, для которого будут запрошены треки + /// + public Task> GetAsync(AuthStorage storage, YPlaylist playlist) + { + return new YGetPlaylistBuilder(api, storage) + .Build((playlist.Owner.Uid, playlist.Kind)) + .GetResponseAsync(); + } + + #endregion Получение плейлиста + + #region Операции над плейлистами + + /// + /// Создание + /// + /// Хранилище + /// Заголовок + /// + public Task> CreateAsync(AuthStorage storage, string name) + { + return new YPlaylistCreateBuilder(api, storage) + .Build(name) + .GetResponseAsync(); + } + + /// + /// Переименование + /// + /// Хранилище + /// Идентификатор плейлиста + /// Заголовок + /// + public Task> RenameAsync(AuthStorage storage, string kinds, string name) + { + return new YPlaylistRenameBuilder(api, storage) + .Build((kinds, name)) + .GetResponseAsync(); + } + + /// + /// Переименование + /// + /// Хранилище + /// Плейлист + /// Заголовок + /// + public Task> RenameAsync(AuthStorage storage, YPlaylist playlist, string name) + { + return RenameAsync(storage, playlist.Kind, name); + } + + /// + /// Удаление + /// + /// Хранилище + /// Тип + /// + public async Task DeleteAsync(AuthStorage storage, string kinds) + { + try + { + await new YPlaylistRemoveBuilder(api, storage) + .Build(kinds) + .GetResponseAsync(); + + return true; + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return false; + } + + /// + /// Удаление + /// + /// Хранилище + /// Плейлист + /// + public Task DeleteAsync(AuthStorage storage, YPlaylist playlist) + { + return DeleteAsync(storage, playlist.Kind); + } + + /// + /// Добавление трека + /// + /// Хранилище + /// Плейлист + /// Треки для добавления + /// + public async Task> InsertTracksAsync(AuthStorage storage, YPlaylist playlist, IEnumerable tracks) + { + YResponse change = await ChangePlaylist(storage, playlist, new List { + new() { + Operation = YPlaylistChangeType.Insert, + At = 0, + Tracks = tracks.Select(t => t.GetKey()) + } + }); + + return await GetAsync(storage, change.Result); + } + + /// + /// Удаление треков + /// + /// Хранилище + /// Плейлист + /// Треки для удаления + /// + public Task> DeleteTracksAsync(AuthStorage storage, YPlaylist playlist, IEnumerable tracks) + { + List changes = RemoveIdentical(tracks) + .Select(t => playlist.Tracks.Select(c => c.Track).ToList().IndexOf(t)) + .Where(i => i != -1) + .Select(i => + { + YTrackContainer t = playlist.Tracks[i]; + return new YPlaylistChange + { + Operation = YPlaylistChangeType.Delete, + From = i, + To = i + 1, + Tracks = new List { + t.Track.GetKey() + } + }; + }) + .ToList(); + + return ChangePlaylist(storage, playlist, changes); + } + + #endregion Операции над плейлистами + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YQueueAPIAsync.cs b/YandexMusic.API/API/YQueueAPIAsync.cs new file mode 100644 index 0000000..aa380fd --- /dev/null +++ b/YandexMusic.API/API/YQueueAPIAsync.cs @@ -0,0 +1,73 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Queue; +using YandexMusic.API.Requests.Queue; + +namespace YandexMusic.API +{ + /// + /// API для взаимодействия с очередями + /// + public partial class YQueueAPI : YCommonAPI + { + public YQueueAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение всех очередей треков с разных устройств для синхронизации между ними + /// + /// Хранилище + /// Устройство + /// + public Task> ListAsync(AuthStorage storage, string device = null) + { + return new YQueuesListBuilder(api, storage) + .Build(device) + .GetResponseAsync(); + } + + /// + /// Получение очереди + /// + /// Хранилище + /// Идентификатор очереди + /// + public Task> GetAsync(AuthStorage storage, string queueId) + { + return new YGetQueueBuilder(api, storage) + .Build(queueId) + .GetResponseAsync(); + } + + /// + /// Создание новой очереди треков + /// + /// Хранилище + /// Очередь треков + /// Устройство + /// + public Task> CreateAsync(AuthStorage storage, YQueue queue, string device = null) + { + return new YQueueCreateBuilder(api, storage, device) + .Build(queue) + .GetResponseAsync(); + } + + /// + /// Установка текущего индекса проигрываемого трека в очереди треков + /// + /// Хранилище + /// Идентификатор очереди + /// Текущий индекс + /// Флаг интерактивности + /// Устройство + /// + public Task> UpdatePositionAsync(AuthStorage storage, string queueId, int currentIndex, bool isInteractive, string device = null) + { + return new YQueueUpdatePositionBuilder(api, storage, device) + .Build((queueId, currentIndex, isInteractive)) + .GetResponseAsync(); + } + } +} diff --git a/YandexMusic.API/API/YRadioAPIAsync.cs b/YandexMusic.API/API/YRadioAPIAsync.cs new file mode 100644 index 0000000..169976d --- /dev/null +++ b/YandexMusic.API/API/YRadioAPIAsync.cs @@ -0,0 +1,116 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Radio; + +namespace YandexMusic.API +{ + /// + /// API для взаимодействия с радио + /// + public partial class YRadioAPI : YCommonAPI + { + + + public YRadioAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение списка рекомендованных радиостанций + /// + /// Хранилище + /// + public Task> GetStationsDashboardAsync(AuthStorage storage) + { + return new YGetStationsDashboardBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Получение списка радиостанций + /// + /// Хранилище + /// + public Task>> GetStationsAsync(AuthStorage storage) + { + return new YGetStationsBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Получение информации о радиостанции + /// + /// Хранилище + /// Тип + /// Тэг + /// + public Task>> GetStationAsync(AuthStorage storage, string type, string tag) + { + return new YGetStationBuilder(api, storage) + .Build((type, tag)) + .GetResponseAsync(); + } + + /// + /// Получение информации о радиостанции + /// + /// Хранилище + /// Идентификатор станции + /// + public Task>> GetStationAsync(AuthStorage storage, YStationId id) + { + return GetStationAsync(storage, id.Type, id.Tag); + } + + /// + /// Получение последовательности треков радиостанции + /// + /// Хранилище + /// Радиостанция + /// Идентификатор предыдущего трека + /// + public Task> GetStationTracksAsync(AuthStorage storage, YStation station, string prevTrackId = "") + { + return new YGetStationTracksBuilder(api, storage) + .Build((station.Station, prevTrackId)) + .GetResponseAsync(); + } + + /// + /// Установка настроек подбора треков + /// + /// Хранилище + /// Радиостанция + /// Настройки + /// + public Task> SetStationSettings2Async(AuthStorage storage, YStation station, YStationSettings2 settings) + { + return new YSetSettings2Builder(api, storage) + .Build((station.Station, settings)) + .GetResponseAsync(); + } + + /// + /// Отправка обратной связи на действия при прослушивании радио + /// + /// Хранилище + /// Радиостанция + /// Тип обратной связи + /// Трек + /// Уникальный идентификатор партии треков. Возвращается при получении треков + /// Сколько было проиграно секунд трека перед действием + /// + public Task SendStationFeedBackAsync(AuthStorage storage, YStation station, YStationFeedbackType type, YTrack track = null, string batchId = "", double totalPlayedSeconds = 0) + { + return new YSetStationFeedbackBuilder(api, storage) + .Build((type, station, track, batchId, totalPlayedSeconds)) + .GetResponseAsync(); + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YSearchAPIAsync.cs b/YandexMusic.API/API/YSearchAPIAsync.cs new file mode 100644 index 0000000..515a27c --- /dev/null +++ b/YandexMusic.API/API/YSearchAPIAsync.cs @@ -0,0 +1,141 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Search; +using YandexMusic.API.Requests.Search; + +namespace YandexMusic.API +{ + /// + /// API для поиска + /// + public partial class YSearchAPI : YCommonAPI + { + + + public YSearchAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Поиск по трекам + /// + /// Хранилище + /// Имя трека + /// Номер страницы + /// Размер страницы + /// + public Task> TrackAsync(AuthStorage storage, string trackName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, trackName, YSearchType.Track, pageNumber, pageSize); + } + + /// + /// Поиск по альбомам + /// + /// Хранилище + /// Имя альбома + /// Номер страницы + /// Размер страницы + /// + public Task> AlbumsAsync(AuthStorage storage, string albumName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, albumName, YSearchType.Album, pageNumber, pageSize); + } + + /// + /// Поиск по артисту + /// + /// Хранилище + /// Имя артиста + /// Номер страницы + /// Размер страницы + /// + public Task> ArtistAsync(AuthStorage storage, string artistName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, artistName, YSearchType.Artist, pageNumber, pageSize); + } + + /// + /// Поиск по плейлистам + /// + /// Хранилище + /// Имя плейлиста + /// Номер страницы + /// Размер страницы + /// + public Task> PlaylistAsync(AuthStorage storage, string playlistName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, playlistName, YSearchType.Playlist, pageNumber, pageSize); + } + + /// + /// Поиск по плейлистам + /// + /// Хранилище + /// Имя подкаста + /// Номер страницы + /// Размер страницы + /// + public Task> PodcastEpisodeAsync(AuthStorage storage, string podcastName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, podcastName, YSearchType.PodcastEpisode, pageNumber, pageSize); + } + + /// + /// Поиск по видео + /// + /// Хранилище + /// Имя видео + /// Номер страницы + /// Размер страницы + /// + public Task> VideosAsync(AuthStorage storage, string videoName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, videoName, YSearchType.Video, pageNumber, pageSize); + } + + /// + /// Поиск по пользователям + /// + /// Хранилище + /// Имя пользователя + /// Номер страницы + /// Размер страницы + /// + public Task> UsersAsync(AuthStorage storage, string userName, int pageNumber = 0, int pageSize = 20) + { + return SearchAsync(storage, userName, YSearchType.User, pageNumber, pageSize); + } + + /// + /// Поиск + /// + /// Хранилище + /// Поисковый запрос + /// Тип поиска + /// Страница + /// Размер страницы + /// + public Task> SearchAsync(AuthStorage storage, string searchText, YSearchType searchType, int page = 0, int pageSize = 20) + { + return new YSearchBuilder(api, storage) + .Build((searchText, searchType, page, pageSize)) + .GetResponseAsync(); + } + + /// + /// Подсказка + /// + /// Хранилище + /// Поисковый запрос + /// + public Task> SuggestAsync(AuthStorage storage, string searchText) + { + return new YSearchSuggestBuilder(api, storage) + .Build(searchText) + .GetResponseAsync(); + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YTrackAPIAsync.cs b/YandexMusic.API/API/YTrackAPIAsync.cs new file mode 100644 index 0000000..555465c --- /dev/null +++ b/YandexMusic.API/API/YTrackAPIAsync.cs @@ -0,0 +1,308 @@ +using System.Security.Cryptography; +using System.Text; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Track; + +namespace YandexMusic.API +{ + /// + /// API для взаимодействия с треками + /// + public partial class YTrackAPI : YCommonAPI + { + #region Вспомогательные функции + + private string BuildLinkForDownload(YTrackDownloadInfo mainDownloadResponse, YStorageDownloadFile storageDownload) + { + string path = storageDownload.Path; + string host = storageDownload.Host; + string ts = storageDownload.Ts; + string s = storageDownload.S; + string codec = mainDownloadResponse.Codec; + + string secret = $"XGRlBW9FXlekgbPrRHuSiA{path.Substring(1, path.Length - 1)}{s}"; + MD5 md5 = MD5.Create(); + byte[] md5Hash = md5.ComputeHash(Encoding.UTF8.GetBytes(secret)); + HMACSHA1 hmacsha1 = new(); + byte[] hmasha1Hash = hmacsha1.ComputeHash(md5Hash); + string sign = BitConverter.ToString(hmasha1Hash).Replace("-", "").ToLower(); + + string link = $"https://{host}/get-{codec}/{sign}/{ts}{path}"; + + return link; + } + + #endregion Вспомогательные функции + + + + public YTrackAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение треков + /// + /// Хранилище + /// Идентификатор трека + /// + public Task>> GetAsync(AuthStorage storage, string trackId) + { + return new YGetTracksBuilder(api, storage) + .Build(new[] { trackId }) + .GetResponseAsync(); + } + + /// + /// Получение треков + /// + /// Хранилище + /// Идентификаторы треков + /// + public Task>> GetAsync(AuthStorage storage, IEnumerable trackIds) + { + return new YGetTracksBuilder(api, storage) + .Build(trackIds) + .GetResponseAsync(); + } + + /// + /// Получение метаданных для загрузки + /// + /// Хранилище + /// Ключ трека в формате {идентифактор трека:идентификатор альбома} + /// Должен ли ответ содержать прямую ссылку на загрузку + /// + public Task>> GetMetadataForDownloadAsync(AuthStorage storage, string trackKey, bool direct = false) + { + return new YTrackDownloadInfoBuilder(api, storage) + .Build((trackKey, direct)) + .GetResponseAsync(); + } + + /// + /// Получение метаданных для загрузки + /// + /// Хранилище + /// Трек + /// Должен ли ответ содержать прямую ссылку на загрузку + /// + public Task>> GetMetadataForDownloadAsync(AuthStorage storage, YTrack track, bool direct = false) + { + return GetMetadataForDownloadAsync(storage, track.GetKey().ToString(), direct); + } + + /// + /// Получение информации для формирования ссылки для загрузки + /// + /// Хранилище + /// Метаданные для загрузки + /// + public Task GetDownloadFileInfoAsync(AuthStorage storage, YTrackDownloadInfo metadataInfo) + { + return new YStorageDownloadFileBuilder(api, storage) + .Build(metadataInfo.DownloadInfoUrl) + .GetResponseAsync(); + } + + /// + /// Получение ссылки для загрузки + /// + /// Хранилище + /// Ключ трека в формате {идентификатор трека:идентификатор альбома} + /// + public async Task GetFileLinkAsync(AuthStorage storage, string trackKey) + { + YResponse> meta = await GetMetadataForDownloadAsync(storage, trackKey); + YTrackDownloadInfo info = meta.Result + .OrderByDescending(i => i.BitrateInKbps) + .First(m => m.Codec == "mp3"); + YStorageDownloadFile storageDownload = await GetDownloadFileInfoAsync(storage, info); + return BuildLinkForDownload(info, storageDownload); + } + + /// + /// Получение ссылки для загрузки + /// + /// Хранилище + /// Трек + /// + public Task GetFileLinkAsync(AuthStorage storage, YTrack track) + { + return GetFileLinkAsync(storage, track.GetKey().ToString()); + } + + /// + /// Отправка текущего состояния прослушиваемого трека + /// Хранилище + /// Трек + /// Наименования клиента, с которого происходит прослушивание + /// Проигрывается ли трек с кеша + /// Уникальный идентификатор проигрывания + /// Уникальный идентификатор плейлиста, если таковой прослушивается + /// Сколько было всего воспроизведено трека в секундах + /// Окончательное значение воспроизведенных секунд + /// + /// + public Task SendPlayTrackInfoAsync(AuthStorage storage, YTrack track, string from, bool fromCache = false, string playId = "", string playlistId = "", double totalPlayedSeconds = 0, double endPositionSeconds = 0) + { + return new YSendTrackInfoBuilder(api, storage) + .Build((track, from, fromCache, playId, playlistId, totalPlayedSeconds, endPositionSeconds)) + .GetResponseAsync(); + } + + #region GetSupplement + + /// + /// Получение дополнительной информации для трека + /// + /// Хранилище + /// Идентификатор трека + /// + public Task> GetSupplementAsync(AuthStorage storage, string trackId) + { + return new YGetTrackSupplementBuilder(api, storage) + .Build(trackId) + .GetResponseAsync(); + } + + /// + /// Получение дополнительной информации для трека + /// + /// Хранилище + /// Трек + /// + public Task> GetSupplementAsync(AuthStorage storage, YTrack track) + { + return new YGetTrackSupplementBuilder(api, storage) + .Build(track.GetKey().ToString()) + .GetResponseAsync(); + } + + #endregion GetSupplement + + #region GetSimilar + + /// + /// Получение похожих треков + /// + /// Хранилище + /// Идентификатор трека + /// + public Task> GetSimilarAsync(AuthStorage storage, string trackId) + { + return new YGetTrackSimilarBuilder(api, storage) + .Build(trackId) + .GetResponseAsync(); + } + + /// + /// Получение похожих треков + /// + /// Хранилище + /// Трек + /// + public Task> GetSimilarAsync(AuthStorage storage, YTrack track) + { + return new YGetTrackSimilarBuilder(api, storage) + .Build(track.GetKey().ToString()) + .GetResponseAsync(); + } + + #endregion GetSimilar + + #region Получение данных трека + + #region В файл + + /// + /// Выгрузка в файл + /// + /// Хранилище + /// Ключ трека в формате {идентификатор трека:идентификатор альбома} + /// Путь для файла + public async Task ExtractToFileAsync(AuthStorage storage, string trackKey, string filePath) + { + string url = await GetFileLinkAsync(storage, trackKey); + await new DataDownloader(storage).ToFile(url, filePath); + } + + /// + /// Выгрузка в файл + /// + /// Хранилище + /// Трек + /// Путь для файла + public Task ExtractToFileAsync(AuthStorage storage, YTrack track, string filePath) + { + return ExtractToFileAsync(storage, track.GetKey().ToString(), filePath); + } + + #endregion В файл + + #region В массив байт + + /// + /// Получение двоичного массива данных + /// + /// Хранилище + /// Ключ трека в формате {идентификатор трека:идентификатор альбома} + /// + public async Task ExtractDataAsync(AuthStorage storage, string trackKey) + { + string url = await GetFileLinkAsync(storage, trackKey); + return await new DataDownloader(storage).AsBytes(url); + } + + /// + /// Получение двоичного массива данных + /// + /// Хранилище + /// Трек + /// + public Task ExtractDataAsync(AuthStorage storage, YTrack track) + { + return ExtractDataAsync(storage, track.GetKey().ToString()); + } + + #endregion В массив байт + + #region В поток + + /// + /// Получение потока данных + /// + /// Хранилище + /// Ключ трека в формате {идентификатор трека:идентификатор альбома} + /// Параметры передачи управления при http запросе + /// + public async Task ExtractStreamAsync(AuthStorage storage, string trackKey, + HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) + { + string url = await GetFileLinkAsync(storage, trackKey); + return await new DataDownloader(storage).AsStream(url, httpCompletionOption); + } + + /// + /// Получение потока данных + /// + /// Хранилище + /// Трек + /// Параметры передачи управления при http запросе + /// + public Task ExtractStreamAsync(AuthStorage storage, YTrack track, + HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) + { + return ExtractStreamAsync(storage, track.GetKey().ToString(), httpCompletionOption); + } + + #endregion В поток + + #endregion Получение данных трека + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YUgcAPIAsync.cs b/YandexMusic.API/API/YUgcAPIAsync.cs new file mode 100644 index 0000000..9316b28 --- /dev/null +++ b/YandexMusic.API/API/YUgcAPIAsync.cs @@ -0,0 +1,72 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Ugc; +using YandexMusic.API.Requests.Ugc; + +namespace YandexMusic.API +{ + public partial class YUgcAPI : YCommonAPI + { + public YUgcAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Получение ссылки на загрузчик трека + /// + /// Хранилище + /// Плейлист, куда будет загружен трек + /// Название файла для загрузки + public Task GetUgcUploadLinkAsync(AuthStorage storage, YPlaylist playlist, string fileName) + { + return new YUgcGetUploadLinkBuilder(api, storage) + .Build((playlist, fileName)) + .GetResponseAsync(); + } + + /// + /// Загрузка трека из файла + /// + /// Хранилище + /// Ссылка на балансировщик для загрузки, можно получить из GetUgcUploadLinkAsync + /// Загружаемый файл + public Task> UploadUgcTrackAsync(AuthStorage storage, string uploadLink, string filePath) + { + if (!File.Exists(filePath)) + throw new FileNotFoundException("Файл для загрузки не существует.", filePath); + + return UploadUgcTrackAsync(storage, uploadLink, File.Open(filePath, FileMode.Open)); + } + + /// + /// Загрузка трека из потока + /// + /// Хранилище + /// Ссылка на балансировщик для загрузки, можно получить из GetUgcUploadLinkAsync + /// Поток с данными для загрузки + public Task> UploadUgcTrackAsync(AuthStorage storage, string uploadLink, Stream stream) + { + if (stream == null) + throw new NullReferenceException("Пустая ссылка на поток загрузки."); + + using MemoryStream ms = new(); + stream.CopyTo(ms); + + return UploadUgcTrackAsync(storage, uploadLink, ms.ToArray()); + } + + /// + /// Загрузка трека из массива + /// + /// Хранилище + /// Ссылка на балансировщик для загрузки, можно получить из GetUgcUploadLinkAsync + /// Загружаемый трек в виде массив байтов + public Task> UploadUgcTrackAsync(AuthStorage storage, string uploadLink, byte[] file) + { + return new YUgcUploadBuilder(api, storage) + .Build((uploadLink, file)) + .GetResponseAsync(); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YUserAPIAsync.cs b/YandexMusic.API/API/YUserAPIAsync.cs new file mode 100644 index 0000000..0c84ffa --- /dev/null +++ b/YandexMusic.API/API/YUserAPIAsync.cs @@ -0,0 +1,303 @@ +using System.Security.Authentication; +using System.Text.RegularExpressions; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Account; + +namespace YandexMusic.API +{ + /// + /// API для пользователя + /// + public partial class YUserAPI : YCommonAPI + { + #region Вспомогательные функции + + private async Task GetCsrfTokenAsync(AuthStorage storage) + { + using HttpResponseMessage authMethodsResponse = await new YGetAuthMethodsBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + + if (!authMethodsResponse.IsSuccessStatusCode) + throw new HttpRequestException("Невозможно получить CFRF-токен."); + + string responseString = await authMethodsResponse.Content + .ReadAsStringAsync(); + Match match = Regex.Match(responseString, "\"csrf_token\" value=\"([^\"]+)\""); + + if (!match.Success || match.Groups.Count < 2) + return false; + + storage.AuthToken = new YAuthToken + { + CsfrToken = match.Groups[1].Value + }; + + return true; + } + + private async Task LoginByCookiesAsync(AuthStorage storage) + { + if (storage.AuthToken == null) + throw new AuthenticationException("Невозможно инициализировать сессию входа."); + + YAccessToken accessToken = await new YGetAuthCookiesBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + + storage.IsAuthorized = !string.IsNullOrEmpty(accessToken.AccessToken); + + storage.AccessToken = accessToken; + storage.Token = accessToken.AccessToken; + + YShortAccountInfo validateTokenResponse = await new YGetShortAccountInifoBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + + if (validateTokenResponse.Status != YAuthStatus.Ok) + throw new Exception("Вход в аккаунт не выполнен."); + + storage.IsAuthorized = !string.IsNullOrWhiteSpace(validateTokenResponse.Uid); + + return storage.IsAuthorized; + } + + #endregion Вспомогательные функции + + + + public YUserAPI(YandexMusicApi yandex) : base(yandex) + { + } + + /// + /// Авторизация + /// + /// Хранилище + /// Токен авторизации + /// + public async Task AuthorizeAsync(AuthStorage storage, string token) + { + if (string.IsNullOrEmpty(token)) + throw new Exception("Задан пустой токен авторизации."); + + storage.Token = token; + + // Пытаемся получить информацию о пользователе + YResponse authInfo = await GetUserAuthAsync(storage); + + // Если не авторизован, то авторизуем + if (string.IsNullOrEmpty(authInfo.Result.Account.Uid)) + throw new Exception("Пользователь незалогинен."); + + // Флаг авторизации + storage.IsAuthorized = true; + storage.User = authInfo.Result.Account; + } + + /// + /// Получение информации об авторизации + /// + /// Хранилище + /// + public Task> GetUserAuthAsync(AuthStorage storage) + { + return new YGetAuthInfoBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Создание сеанса и получение доступных методов авторизации + /// + /// Хранилище + /// Имя пользователя + /// + public async Task CreateAuthSessionAsync(AuthStorage storage, string userName) + { + if (!await GetCsrfTokenAsync(storage)) + throw new Exception("Невозможно инициализировать сессию входа."); + + YAuthTypes types = await new YGetAuthLoginUserBuilder(api, storage) + .Build((storage.AuthToken.CsfrToken, userName)) + .GetResponseAsync(); + + storage.AuthToken.TrackId = types.TrackId; + + return types; + } + + /// + /// Получение ссылки на QR-код + /// + /// Хранилище + /// + public async Task GetAuthQRLinkAsync(AuthStorage storage) + { + if (!await GetCsrfTokenAsync(storage)) + throw new Exception("Невозможно инициализировать сессию входа."); + + YAuthQR result = await new YGetAuthQRBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + + if (result.Status != YAuthStatus.Ok) + return string.Empty; + + storage.AuthToken = new YAuthToken + { + TrackId = result.TrackId, + CsfrToken = result.CsrfToken + }; + + return $"https://passport.yandex.ru/auth/magic/code/?track_id={result.TrackId}"; + } + + /// + /// Авторизация по QR-коду + /// + /// Хранилище + /// + public async Task AuthorizeByQRAsync(AuthStorage storage) + { + if (storage.AuthToken == null) + throw new Exception("Не выполнен запрос на авторизацию по QR."); + + try + { + YAuthQRStatus qrStatus = await new YGetAuthLoginQRBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + if (qrStatus.Status != YAuthStatus.Ok) + return qrStatus; + + bool ok = await LoginByCookiesAsync(storage); + if (!ok) + throw new AuthenticationException("Ошибка авторизации по QR."); + + return qrStatus; + } + catch (Exception ex) + { + throw new AuthenticationException("Ошибка авторизации по QR.", ex); + } + } + + /// + /// Получение + /// + /// Хранилище + /// + public Task GetCaptchaAsync(AuthStorage storage) + { + if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken)) + throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием."); + + return new YGetAuthCaptchaBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Авторизация по captcha + /// + /// Хранилище + /// Значение captcha + /// + public Task AuthorizeByCaptchaAsync(AuthStorage storage, string captchaValue) + { + if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken)) + throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием."); + + return new YGetAuthLoginCaptchaBuilder(api, storage) + .Build(captchaValue) + .GetResponseAsync(); + } + + /// + /// Получение письма авторизации на почту пользователя + /// + /// Хранилище + /// + public Task GetAuthLetterAsync(AuthStorage storage) + { + return new YGetAuthLetterBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + /// + /// Авторизация после подтверждения входа через письмо + /// + /// Хранилище + /// + public async Task AuthorizeByLetterAsync(AuthStorage storage) + { + YAuthLetterStatus status = await new YGetAuthLoginLetterBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + + if (status.Status == YAuthStatus.Ok && !status.MagicLinkConfirmed) + throw new Exception("Не подтвержден вход посредством e-mail."); + + return await LoginByCookiesAsync(storage); + } + + /// + /// Авторизация с помощью пароля из приложения Яндекс + /// + /// Хранилище + /// Пароль + /// + public async Task AuthorizeByAppPasswordAsync(AuthStorage storage, string password) + { + if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken)) + throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием."); + + YAuthBase response = await new YGetAuthAppPasswordBuilder(api, storage) + .Build(password) + .GetResponseAsync(); + + if (response.Status == YAuthStatus.Ok) + { + bool ok = await LoginByCookiesAsync(storage); + if (!ok) + throw new AuthenticationException("Ошибка авторизации."); + } + + return response; + } + + /// + /// Получение после авторизации с помощью QR, e-mail, пароля из приложения + /// + public async Task GetAccessTokenAsync(AuthStorage storage) + { + if (storage.AuthToken == null) + throw new Exception("Не найдена сессия входа."); + + YAccessToken accessToken = await new YGetMusicTokenBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + + storage.Token = accessToken.AccessToken; + + return accessToken; + } + + /// + /// Получение информации о пользователе через логин Яндекса + /// + public Task GetLoginInfoAsync(AuthStorage storage) + { + return new YGetLoginInfoBuilder(api, storage) + .Build(null) + .GetResponseAsync(); + } + + + } +} diff --git a/YandexMusic.API/Common/AuthStorage.cs b/YandexMusic.API/Common/AuthStorage.cs new file mode 100644 index 0000000..81ba0ee --- /dev/null +++ b/YandexMusic.API/Common/AuthStorage.cs @@ -0,0 +1,103 @@ +using System.Net; + +using YandexMusic.API.Common.Debug; +using YandexMusic.API.Common.Providers; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; + +namespace YandexMusic.API.Common +{ + /// + /// Хранилище данных пользователя + /// + public class AuthStorage + { + #region Свойства + + /// + /// Http-контекст + /// + public HttpContext Context { get; } + + public DebugSettings Debug { get; set; } + + /// + /// Флаг авторизации + /// + public bool IsAuthorized { get; internal set; } + + /// + /// Идентификатор устройства + /// + public string DeviceId { get; set; } = "csharp"; + + /// + /// Токен авторизации + /// + public string Token { get; internal set; } + + /// + /// Аккаунт + /// + public YAccount User { get; set; } + + /// + /// Провайдер запросов + /// + public IRequestProvider Provider { get; } + + /// + /// Токен доступа + /// + public YAccessToken AccessToken { get; set; } + + internal YAuthToken AuthToken { get; set; } + + #endregion Свойства + + + + /// + /// Конструктор + /// + public AuthStorage(DebugSettings settings = null) + { + User = new YAccount(); + Context = new HttpContext(); + Debug = settings; + Provider = new DefaultRequestProvider(this); + + if (Debug is { ClearDirectory: true }) + { + Debug.Clear(); + } + } + + /// + /// Конструктор + /// + public AuthStorage(IRequestProvider provider, DebugSettings settings = null) + { + User = new YAccount(); + Context = new HttpContext(); + Debug = settings; + Provider = provider; + + if (Debug is { ClearDirectory: true }) + { + Debug.Clear(); + } + } + + /// + /// Установка прокси для пользователия + /// + /// Прокси + public void SetProxy(IWebProxy proxy) + { + Context.WebProxy = proxy; + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/DataDownloader.cs b/YandexMusic.API/Common/DataDownloader.cs new file mode 100644 index 0000000..fbec075 --- /dev/null +++ b/YandexMusic.API/Common/DataDownloader.cs @@ -0,0 +1,44 @@ +using System.Net; + +namespace YandexMusic.API.Common +{ + /// + /// Загрузчик файлов по ссылке + /// + public class DataDownloader + { + private AuthStorage authStorage; + + private async Task GetResponseContent(string url, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) + { + HttpRequestMessage message = new(new HttpMethod(WebRequestMethods.Http.Get), url); + + HttpResponseMessage response = await authStorage.Provider.GetWebResponseAsync(message, httpCompletionOption); + return response.Content; + } + + public async Task AsStream(string url, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) + { + HttpContent content = await GetResponseContent(url, httpCompletionOption); + return await content.ReadAsStreamAsync(); + } + + public async Task AsBytes(string url) + { + HttpContent content = await GetResponseContent(url); + return await content.ReadAsByteArrayAsync(); + } + + public async Task ToFile(string url, string fileName) + { + using Stream stream = await AsStream(url); + using FileStream fs = File.Create(fileName); + await stream.CopyToAsync(fs); + } + + public DataDownloader(AuthStorage storage) + { + authStorage = storage; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Encryptor.cs b/YandexMusic.API/Common/Encryptor.cs new file mode 100644 index 0000000..541a57e --- /dev/null +++ b/YandexMusic.API/Common/Encryptor.cs @@ -0,0 +1,75 @@ +using System.Security.Cryptography; +using System.Text; + +namespace YandexMusic.API.Common +{ + /// + /// Класс для шифровки + /// + public class Encryptor + { + #region Поля + + private readonly string IV = "encryption"; + private readonly byte[] IVHash; + + private readonly byte[] keyHash; + + private readonly MD5 md5; + private readonly Aes aesAlg; + + + #endregion Поля + + #region Вспомогательные функции + + private byte[] GetHash(string value) + { + return md5.ComputeHash(Encoding.UTF8.GetBytes(value)); + } + + #endregion Вспомогательные функции + + + + public Encryptor(string key) + { + md5 = MD5.Create(); + + aesAlg = Aes.Create(); + aesAlg.BlockSize = 128; + aesAlg.Padding = PaddingMode.PKCS7; + + keyHash = GetHash(key); + IVHash = GetHash(IV); + } + + public byte[] Encrypt(byte[] data) + { + using MemoryStream ms = new(); + using CryptoStream csEncrypt = new(ms, aesAlg.CreateEncryptor(keyHash, IVHash), CryptoStreamMode.Write); + + csEncrypt.Write(data, 0, data.Length); + + if (!csEncrypt.HasFlushedFinalBlock) + csEncrypt.FlushFinalBlock(); + + return ms.ToArray(); + } + + public byte[] Decrypt(byte[] data) + { + using MemoryStream ms = new(); + using CryptoStream csDecrypt = new(ms, aesAlg.CreateDecryptor(keyHash, IVHash), CryptoStreamMode.Write); + + csDecrypt.Write(data, 0, data.Length); + + if (!csDecrypt.HasFlushedFinalBlock) + csDecrypt.FlushFinalBlock(); + + return ms.ToArray(); + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Providers/CommonRequestProvider.cs b/YandexMusic.API/Common/Providers/CommonRequestProvider.cs new file mode 100644 index 0000000..de4ac1d --- /dev/null +++ b/YandexMusic.API/Common/Providers/CommonRequestProvider.cs @@ -0,0 +1,60 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Common.Providers +{ + public class CommonRequestProvider : IRequestProvider + { + #region Поля + + protected AuthStorage storage; + + #endregion Поля + + + + public CommonRequestProvider(AuthStorage authStorage) + { + storage = authStorage; + } + + + + #region IRequestProvider + + public virtual Task GetWebResponseAsync(HttpRequestMessage message, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) + { + throw new NotImplementedException(); + } + + public virtual async Task GetDataFromResponseAsync(YandexMusicApi api, HttpResponseMessage response) + { + string result = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode) + { + YErrorResponse exception = JsonConvert.DeserializeObject(result); + throw exception ?? new Exception("Ошибка десериализации ответа с ошибкой."); + } + + try + { + JsonSerializerSettings settings = new() + { + Converters = new List { + new YExecutionContextConverter(api, storage) + } + }; + + return storage.Debug != null + ? storage.Debug.Deserialize(response.RequestMessage?.RequestUri?.AbsolutePath, result, settings) + : JsonConvert.DeserializeObject(result, settings); + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации {ex}"); + } + } + + #endregion IRequestProvider + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Providers/DefaultRequestProvider.cs b/YandexMusic.API/Common/Providers/DefaultRequestProvider.cs new file mode 100644 index 0000000..c46820f --- /dev/null +++ b/YandexMusic.API/Common/Providers/DefaultRequestProvider.cs @@ -0,0 +1,69 @@ +using System.Net; + +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Common.Providers +{ + /// + /// Стандартный провайдер запросов + /// + public class DefaultRequestProvider : CommonRequestProvider + { + #region Вспомогательные функции + + private Exception ProcessException(Exception ex) + { + if (ex is not WebException webException) + return ex; + + if (webException.Response is null) + return ex; + + Stream s = webException.Response.GetResponseStream(); + if (s is null) + return ex; + + using StreamReader sr = new(s); + string result = sr.ReadToEnd(); + + YErrorResponse exception = JsonConvert.DeserializeObject(result); + + return exception ?? ex; + } + + #endregion Вспомогательные функции + + + + public DefaultRequestProvider(AuthStorage authStorage) : base(authStorage) + { + } + + + + #region IRequestProvider + + public override Task GetWebResponseAsync(HttpRequestMessage message, + HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) + { + try + { + HttpClient client = new(new SocketsHttpHandler + { + Proxy = storage.Context.WebProxy, + AutomaticDecompression = DecompressionMethods.GZip, + UseCookies = true, + CookieContainer = storage.Context.Cookies, + }); + + return client.SendAsync(message, completionOption); + } + catch (Exception ex) + { + throw ProcessException(ex); + } + } + + #endregion IRequestProvider + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Providers/IRequestProvider.cs b/YandexMusic.API/Common/Providers/IRequestProvider.cs new file mode 100644 index 0000000..d48b18e --- /dev/null +++ b/YandexMusic.API/Common/Providers/IRequestProvider.cs @@ -0,0 +1,25 @@ +namespace YandexMusic.API.Common.Providers +{ + /// + /// Интерфейс для провайдеров обработки запросов + /// + public interface IRequestProvider + { + /// + /// Функция получения ответа + /// + /// Запрос + /// Опция завершения запроса + /// + Task GetWebResponseAsync(HttpRequestMessage message, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead); + + /// + /// Функция формирования ответа + /// + /// Тип объекта с ответом + /// API + /// Ответ + /// + Task GetDataFromResponseAsync(YandexMusicApi api, HttpResponseMessage response); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Providers/MockRequestProvider.cs b/YandexMusic.API/Common/Providers/MockRequestProvider.cs new file mode 100644 index 0000000..df3937d --- /dev/null +++ b/YandexMusic.API/Common/Providers/MockRequestProvider.cs @@ -0,0 +1,27 @@ +namespace YandexMusic.API.Common.Providers +{ + /// + /// Провайдер запросов данными из файла + /// + public class MockRequestProvider : CommonRequestProvider + { + + + public MockRequestProvider(AuthStorage authStorage) : base(authStorage) + { + storage = authStorage; + } + + + + #region IRequestProvider + + public override Task GetWebResponseAsync(HttpRequestMessage message, + HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) + { + throw new NotImplementedException(); + } + + #endregion IRequestProvider + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Ynison/UpperSnakeCaseNamingStrategy.cs b/YandexMusic.API/Common/Ynison/UpperSnakeCaseNamingStrategy.cs new file mode 100644 index 0000000..752f9ba --- /dev/null +++ b/YandexMusic.API/Common/Ynison/UpperSnakeCaseNamingStrategy.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Common.Ynison +{ + public class UpperSnakeCaseNamingStrategy : SnakeCaseNamingStrategy + { + protected override string ResolvePropertyName(string name) => base.ResolvePropertyName(name).ToUpper(); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Ynison/YnisonPlayer.cs b/YandexMusic.API/Common/Ynison/YnisonPlayer.cs new file mode 100644 index 0000000..713f208 --- /dev/null +++ b/YandexMusic.API/Common/Ynison/YnisonPlayer.cs @@ -0,0 +1,315 @@ +using System.Net.WebSockets; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Models.Ynison; +using YandexMusic.API.Models.Ynison.Messages; + +namespace YandexMusic.API.Common.Ynison +{ + public class YnisonPlayer : IDisposable + { + #region Поля + + private readonly JsonSerializerSettings jsonSettings = new() + { + Converters = new List { + new StringEnumConverter(new UpperSnakeCaseNamingStrategy()) + }, + + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new DefaultContractResolver + { + // Важно! Унисон отдаёт данные в SnakeCase + NamingStrategy = new SnakeCaseNamingStrategy() + } + }; + + private AuthStorage storage; + private YnisonWebSocket redirector; + private YnisonWebSocket state; + + #endregion Поля + + #region Свойства + + /// + /// API + /// + public YandexMusicApi API { get; internal set; } + + /// + /// Состояние + /// + public YYnisonState State { get; internal set; } + + /// + /// Текущий проигрываемый трек + /// + public YTrack Current => GetCurrent(); + + #endregion Свойства + + #region События + + public class ReceiveEventArgs + { + public YYnisonState State { get; internal set; } + } + + public delegate void OnReceiveEventHandler(YnisonPlayer player, ReceiveEventArgs args); + + /// + /// Получение данных + /// + public event OnReceiveEventHandler OnReceive; + + + public class CloseEventArgs + { + public WebSocketCloseStatus? Status { get; set; } + public string Description { get; set; } + } + + public delegate void OnCloseEventHandler(YnisonPlayer player, CloseEventArgs args); + + /// + /// Получение данных + /// + public event OnCloseEventHandler OnClose; + + #endregion События + + #region Вспомогательные функции + + private string SerializeJson(object data) + { + return JsonConvert.SerializeObject(data, jsonSettings); + } + + private T Deserialize(YYnisonMessageType messageType, string data) + { + return storage.Debug != null + ? storage.Debug.Deserialize($"Ynison{messageType}", data, jsonSettings) + : JsonConvert.DeserializeObject(data, jsonSettings); + } + + private T DeserializeMessage(YYnisonMessageType messageType, string data) + { + JObject o = JObject.Parse(data); + // Сообщение с ошибкой + if (o.ContainsKey("error")) + { + YYnisonErrorMessage exception = Deserialize(YYnisonMessageType.Error, data); + throw exception ?? new Exception("Ошибка десериализации ответа с ошибкой."); + } + + return Deserialize(messageType, data); + } + + private string DefaultState() + { + YYnisonVersion version = new() + { + DeviceId = storage.DeviceId, + Version = "0" + }; + + YYnisonUpdateFullStateMessage fullState = new() + { + UpdateFullState = new() + { + Device = new() + { + Capabilities = new() + { + CanBePlayer = true + }, + Info = new() + { + DeviceId = storage.DeviceId, + AppName = "Yandex Music API", + AppVersion = "0.0.1", + Type = "WEB", + Title = "YandexMusicAPI" + }, + IsShadow = true + }, + PlayerState = new() + { + PlayerQueue = new() + { + Version = version + }, + Status = new() + { + Version = version + } + } + } + }; + + return SerializeJson(fullState); + } + + private YTrack GetCurrent() + { + if (State == null) + return null; + + int index = State.PlayerState.PlayerQueue.CurrentPlayableIndex; + if (index < 0 || index > State.PlayerState.PlayerQueue.PlayableList.Count) + return null; + + YYnisonPlayableItem item = State.PlayerState.PlayerQueue.PlayableList[index]; + + return API.Track.Get(storage, item.PlayableId) + .Result + .FirstOrDefault(); + } + + private void UpdateState() + { + YYnisonUpdatePlayerStateMessage update = new() + { + UpdatePlayerState = State.PlayerState + }; + + update.UpdatePlayerState.Status.Version = new() + { + DeviceId = storage.DeviceId + }; + + update.UpdatePlayerState.PlayerQueue.Version = new() + { + DeviceId = storage.DeviceId + }; + + try + { + state.Send(SerializeJson(update)); + } + catch (Exception ex) + { + Console.WriteLine(ex); + throw; + } + } + + #endregion Вспомогательные функции + + + + #region Подключение + + public void Connect() + { + redirector.Connect(storage, "wss://ynison.music.yandex.ru/redirector.YnisonRedirectService/GetRedirectToYnison"); + redirector.OnReceive += (socket, data) => + { + YYnisonRedirect redirectInfo = Deserialize(YYnisonMessageType.Redirect, data.Data); + + if (state.IsConnected) + return; + + state.Connect(storage, $"wss://{redirectInfo.Host}/ynison_state.YnisonStateService/PutYnisonState", redirectInfo.RedirectTicket); + state.OnReceive += (s, d) => + { + YYnisonState message = DeserializeMessage(YYnisonMessageType.State, d.Data); + + State = message; + + OnReceive?.Invoke(this, new ReceiveEventArgs + { + State = State + }); + }; + + state.OnClose += (s, args) => + { + OnClose?.Invoke(this, new CloseEventArgs + { + Status = args.Status, + Description = args.Description + }); + }; + + state.BeginReceive(); + // Отправка изначального состояния + state.Send(DefaultState()); + }; + + redirector.BeginReceive(); + } + + public void Disconnect() + { + state?.StopReceive(); + redirector?.StopReceive(); + } + + #endregion Подключение + + #region Плеер + + /* + public void Play() + { + + } + + public void Stop() + { + + } + + public void Next() + { + List list = State.PlayerState.PlayerQueue.PlayableList; + + if (State.PlayerState.PlayerQueue.EntityType == YYnisonEntityType.Radio) + { + YYnisonPlayableItem next = State.PlayerState.PlayerQueue.Queue.WaveQueue.RecommendedPlayableList + .FirstOrDefault(); + + list.RemoveAt(0); + list.Add(next); + + UpdateState(); + } + + if (State.PlayerState.PlayerQueue.CurrentPlayableIndex < list.Count - 1) + { + State.PlayerState.PlayerQueue.CurrentPlayableIndex++; + UpdateState(); + } + } + + public void Previous() + { + + } + */ + + #endregion Плеер + + internal YnisonPlayer(YandexMusicApi api, AuthStorage authStorage) + { + API = api; + storage = authStorage; + + redirector = new(); + state = new(); + } + + + + #region IDisposable + + public void Dispose() + { + redirector?.StopReceive(); + redirector?.Dispose(); + } + + #endregion IDisposable + } +} \ No newline at end of file diff --git a/YandexMusic.API/Common/Ynison/YnisonWebSocket.cs b/YandexMusic.API/Common/Ynison/YnisonWebSocket.cs new file mode 100644 index 0000000..01363fe --- /dev/null +++ b/YandexMusic.API/Common/Ynison/YnisonWebSocket.cs @@ -0,0 +1,179 @@ +using System.Net.WebSockets; +using System.Text; + +namespace YandexMusic.API.Common.Ynison +{ + public class YnisonWebSocket : IDisposable + { + #region Поля + + private readonly JsonSerializerSettings jsonSettings = new() + { + Converters = new List { + new StringEnumConverter { + NamingStrategy = new CamelCaseNamingStrategy() + } + }, + NullValueHandling = NullValueHandling.Ignore + }; + + private readonly ClientWebSocket socketClient = new(); + + private CancellationTokenSource cancellationTokenSource = new(); + private CancellationToken cancellation; + + private readonly StringBuilder data = new(); + private readonly int size = 4096; + + #endregion Поля + + #region Свойства + + public bool IsConnected => socketClient.State == WebSocketState.Open; + + #endregion Свойства + + #region События + + public class ReceiveEventArgs + { + public string Data { get; internal set; } + } + + public delegate void OnReceiveEventHandler(YnisonWebSocket socket, ReceiveEventArgs args); + /// + /// Получение данных + /// + public event OnReceiveEventHandler OnReceive; + + public class CloseEventArgs + { + public WebSocketCloseStatus? Status { get; set; } + public string Description { get; set; } + } + + public delegate void OnCloseEventHandler(YnisonWebSocket socket, CloseEventArgs args); + /// + /// Закрытие соединения + /// + public event OnCloseEventHandler OnClose; + + #endregion События + + #region Вспомогательные функции + + private string SerializeJson(object obj) + { + return JsonConvert.SerializeObject(obj, jsonSettings); + } + + private string GetProtocolData(string deviceId, string redirectTicket) + { + Dictionary deviceInfo = new() { + { "app_name", "Chrome" }, + { "type", 1 } + }; + + Dictionary protocol = new() { + { "Ynison-Device-Id", deviceId }, + { "Ynison-Device-Info", SerializeJson(deviceInfo) } + }; + + if (!string.IsNullOrEmpty(redirectTicket)) + protocol.Add("Ynison-Redirect-Ticket", redirectTicket); + + return SerializeJson(protocol); + } + + private async Task ReadSocketContent() + { + byte[] buffer = new byte[size]; + WebSocketReceiveResult result; + + do + { + result = await socketClient.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + data.Append(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } while (!result.EndOfMessage); + + return data.ToString(); + } + + #endregion Вспомогательные функции + + + + public bool Connect(AuthStorage storage, string url, string redirectTicket = null) + { + socketClient.Options.AddSubProtocol("Bearer"); + + socketClient.Options.SetRequestHeader("Sec-WebSocket-Protocol", $"Bearer, v2, {GetProtocolData(storage.DeviceId, redirectTicket)}"); + socketClient.Options.SetRequestHeader("Origin", "https://music.yandex.ru"); + socketClient.Options.SetRequestHeader("Authorization", $"OAuth {storage.Token}"); + + socketClient.Options.Proxy = storage.Context.WebProxy; + + socketClient.ConnectAsync(new Uri(url), CancellationToken.None) + .GetAwaiter() + .GetResult(); + + cancellation = cancellationTokenSource.Token; + + return socketClient.State == WebSocketState.Open; + } + + public async Task BeginReceive() + { + if (socketClient.State != WebSocketState.Open) + return; + + do + { + string content = await ReadSocketContent(); + OnReceive?.Invoke(this, new ReceiveEventArgs + { + Data = content + }); + + data.Clear(); + } while (!cancellation.IsCancellationRequested && socketClient.State == WebSocketState.Open); + + OnClose?.Invoke(this, new CloseEventArgs + { + Status = socketClient.CloseStatus, + Description = socketClient.CloseStatusDescription + }); + + await socketClient.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + } + + + public ValueTask Send(string json) + { + ReadOnlyMemory message = new(Encoding.UTF8.GetBytes(json)); + return socketClient.SendAsync(message, WebSocketMessageType.Text, false, CancellationToken.None); + } + + public Task StopReceive() + { + if (socketClient.State != WebSocketState.Open) + return Task.CompletedTask; + + cancellationTokenSource.Cancel(false); + + return Task.CompletedTask; + } + + + + #region IDisposable + + public void Dispose() + { + socketClient?.Dispose(); + cancellationTokenSource?.Dispose(); + } + + #endregion IDisposable + } +} \ No newline at end of file diff --git a/YandexMusic.API/Extensions/API/YAlbumExtensions.cs b/YandexMusic.API/Extensions/API/YAlbumExtensions.cs new file mode 100644 index 0000000..5585628 --- /dev/null +++ b/YandexMusic.API/Extensions/API/YAlbumExtensions.cs @@ -0,0 +1,25 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для альбома + /// + public static partial class YAlbumExtensions + { + public static YAlbum WithTracks(this YAlbum album) + { + return WithTracksAsync(album).GetAwaiter().GetResult(); + } + + public static string AddLike(this YAlbum album) + { + return AddLikeAsync(album).GetAwaiter().GetResult(); + } + + public static string RemoveLike(this YAlbum album) + { + return RemoveLikeAsync(album).GetAwaiter().GetResult(); + } + } +} diff --git a/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs new file mode 100644 index 0000000..ca5243c --- /dev/null +++ b/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs @@ -0,0 +1,30 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для альбома + /// + public static partial class YAlbumExtensions + { + public static async Task WithTracksAsync(this YAlbum album) + { + return album.Volumes != null + ? album + : (await album.Context.API.Album.GetAsync(album.Context.Storage, album.Id)) + .Result; + } + + public static async Task AddLikeAsync(this YAlbum album) + { + return (await album.Context.API.Library.AddAlbumLikeAsync(album.Context.Storage, album)) + .Result; + } + + public static async Task RemoveLikeAsync(this YAlbum album) + { + return (await album.Context.API.Library.RemoveAlbumLikeAsync(album.Context.Storage, album)) + .Result; + } + } +} diff --git a/YandexMusic.API/Extensions/API/YArtistExtensions.cs b/YandexMusic.API/Extensions/API/YArtistExtensions.cs new file mode 100644 index 0000000..e9c4f2e --- /dev/null +++ b/YandexMusic.API/Extensions/API/YArtistExtensions.cs @@ -0,0 +1,36 @@ +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для исполнителя + /// + public static partial class YArtistExtensions + { + public static YArtistBriefInfo BriefInfo(this YArtist artist) + { + return BriefInfoAsync(artist).GetAwaiter().GetResult(); + } + + public static YTracksPage GetTracks(this YArtist artist, int page = 0, int pageSize = 20) + { + return GetTracksAsync(artist, page, pageSize).GetAwaiter().GetResult(); + } + + public static List GetAllTracks(this YArtist artist) + { + return GetAllTracksAsync(artist).GetAwaiter().GetResult(); + } + + public static string AddLike(this YArtist artist) + { + return AddLikeAsync(artist).GetAwaiter().GetResult(); + } + + public static string RemoveLike(this YArtist artist) + { + return RemoveLikeAsync(artist).GetAwaiter().GetResult(); + } + } +} diff --git a/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs new file mode 100644 index 0000000..6812591 --- /dev/null +++ b/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs @@ -0,0 +1,41 @@ +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для исполнителя + /// + public static partial class YArtistExtensions + { + public static async Task BriefInfoAsync(this YArtist artist) + { + return (await artist.Context.API.Artist.GetAsync(artist.Context.Storage, artist.Id)) + .Result; + } + + public static async Task GetTracksAsync(this YArtist artist, int page = 0, int pageSize = 20) + { + return (await artist.Context.API.Artist.GetTracksAsync(artist.Context.Storage, artist.Id, page, pageSize)) + .Result; + } + + public static async Task> GetAllTracksAsync(this YArtist artist) + { + return (await artist.Context.API.Artist.GetAllTracksAsync(artist.Context.Storage, artist.Id)) + .Result.Tracks; + } + + public static async Task AddLikeAsync(this YArtist artist) + { + return (await artist.Context.API.Library.AddArtistLikeAsync(artist.Context.Storage, artist)) + .Result; + } + + public static async Task RemoveLikeAsync(this YArtist artist) + { + return (await artist.Context.API.Library.RemoveArtistLikeAsync(artist.Context.Storage, artist)) + .Result; + } + } +} diff --git a/YandexMusic.API/Extensions/API/YPlaylistExtensions.cs b/YandexMusic.API/Extensions/API/YPlaylistExtensions.cs new file mode 100644 index 0000000..ef3b46f --- /dev/null +++ b/YandexMusic.API/Extensions/API/YPlaylistExtensions.cs @@ -0,0 +1,56 @@ +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для плейлиста + /// + public static partial class YPlaylistExtensions + { + private static bool CheckUser(YPlaylist playlist) + { + return playlist.Owner.Uid == playlist.Context.Storage.User.Uid; + } + + public static YPlaylist WithTracks(this YPlaylist playlist) + { + return WithTracksAsync(playlist).GetAwaiter().GetResult(); + } + + public static string AddLike(this YPlaylist playlist) + { + return AddLikeAsync(playlist).GetAwaiter().GetResult(); + } + + public static string RemoveLike(this YPlaylist playlist) + { + return RemoveLikeAsync(playlist).GetAwaiter().GetResult(); + } + + public static YPlaylist Rename(this YPlaylist playlist, string newName) + { + return RenameAsync(playlist, newName).GetAwaiter().GetResult(); + } + + public static bool Delete(this YPlaylist playlist) + { + return DeleteAsync(playlist).GetAwaiter().GetResult(); + } + + public static YPlaylist InsertTracks(this YPlaylist playlist, params YTrack[] tracks) + { + return InsertTracksAsync(playlist, tracks).GetAwaiter().GetResult(); + } + + public static YPlaylist RemoveTracks(this YPlaylist playlist, params YTrack[] tracks) + { + return RemoveTracksAsync(playlist, tracks).GetAwaiter().GetResult(); + } + + public static bool UploadTracks(this YPlaylist playlist, string filePath, string fileName) + { + return UploadTracksAsync(playlist, filePath, fileName).GetAwaiter().GetResult(); + } + } +} diff --git a/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs new file mode 100644 index 0000000..e1338d3 --- /dev/null +++ b/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs @@ -0,0 +1,72 @@ +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для плейлиста + /// + public static partial class YPlaylistExtensions + { + public static async Task WithTracksAsync(this YPlaylist playlist) + { + return playlist.Tracks != null + ? playlist + : (await playlist.Context.API.Playlist.GetAsync(playlist.Context.Storage, playlist)) + .Result; + } + + public static async Task AddLikeAsync(this YPlaylist playlist) + { + return (await playlist.Context.API.Library.AddPlaylistLikeAsync(playlist.Context.Storage, playlist)) + .Result; + } + + public static async Task RemoveLikeAsync(this YPlaylist playlist) + { + return (await playlist.Context.API.Library.RemovePlaylistLikeAsync(playlist.Context.Storage, playlist)) + .Result; + } + + public static async Task RenameAsync(this YPlaylist playlist, string newName) + { + return CheckUser(playlist) + ? (await playlist.Context.API.Playlist.RenameAsync(playlist.Context.Storage, playlist, newName)) + .Result + : playlist; + } + + public static async Task DeleteAsync(this YPlaylist playlist) + { + return CheckUser(playlist) && await playlist.Context.API.Playlist.DeleteAsync(playlist.Context.Storage, playlist); + } + + public static async Task InsertTracksAsync(this YPlaylist playlist, params YTrack[] tracks) + { + return CheckUser(playlist) + ? (await playlist.Context.API.Playlist.InsertTracksAsync(playlist.Context.Storage, playlist, tracks)) + .Result + : playlist; + } + + public static async Task RemoveTracksAsync(this YPlaylist playlist, params YTrack[] tracks) + { + return CheckUser(playlist) + ? (await playlist.Context.API.Playlist.DeleteTracksAsync(playlist.Context.Storage, playlist, tracks)) + .Result + : playlist; + } + + public static async Task UploadTracksAsync(this YPlaylist playlist, string filePath, string fileName) + { + if (!CheckUser(playlist)) + return false; + + string target = (await playlist.Context.API.UserGeneratedContent.GetUgcUploadLinkAsync(playlist.Context.Storage, playlist, fileName)) + .PostTarget; + + return (await playlist.Context.API.UserGeneratedContent.UploadUgcTrackAsync(playlist.Context.Storage, target, filePath)) + .Result == "CREATED"; + } + } +} diff --git a/YandexMusic.API/Extensions/API/YStationResultExtensions.cs b/YandexMusic.API/Extensions/API/YStationResultExtensions.cs new file mode 100644 index 0000000..39e11a0 --- /dev/null +++ b/YandexMusic.API/Extensions/API/YStationResultExtensions.cs @@ -0,0 +1,26 @@ +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для радиостанции + /// + public static partial class YStationResultExtensions + { + public static List GetTracks(this YStation station, string prevTrackId = "") + { + return GetTracksAsync(station, prevTrackId).GetAwaiter().GetResult(); + } + + public static string SetSettings2(this YStation station, YStationSettings2 settings) + { + return SetSettings2Async(station, settings).GetAwaiter().GetResult(); + } + + public static string SendFeedBack(this YStation station, YStationFeedbackType type, YTrack track = null, string batchId = "", double totalPlayedSeconds = 0) + { + return SendFeedBackAsync(station, type, track, batchId, totalPlayedSeconds).GetAwaiter().GetResult(); + } + } +} diff --git a/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs new file mode 100644 index 0000000..764e007 --- /dev/null +++ b/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs @@ -0,0 +1,28 @@ +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для радиостанции + /// + public static partial class YStationResultExtensions + { + public static async Task> GetTracksAsync(this YStation station, string prevTrackId = "") + { + return (await station.Context.API.Radio.GetStationTracksAsync(station.Context.Storage, station, prevTrackId)) + .Result.Sequence; + } + + public static async Task SetSettings2Async(this YStation station, YStationSettings2 settings) + { + return (await station.Context.API.Radio.SetStationSettings2Async(station.Context.Storage, station, settings)) + .Result; + } + + public static Task SendFeedBackAsync(this YStation station, YStationFeedbackType type, YTrack track = null, string batchId = "", double totalPlayedSeconds = 0) + { + return station.Context.API.Radio.SendStationFeedBackAsync(station.Context.Storage, station, type, track, batchId, totalPlayedSeconds); + } + } +} diff --git a/YandexMusic.API/Extensions/API/YTrackExtensions.cs b/YandexMusic.API/Extensions/API/YTrackExtensions.cs new file mode 100644 index 0000000..052e621 --- /dev/null +++ b/YandexMusic.API/Extensions/API/YTrackExtensions.cs @@ -0,0 +1,55 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для трека + /// + public static partial class YTrackExtensions + { + public static string GetLink(this YTrack track) + { + return GetLinkAsync(track).GetAwaiter().GetResult(); + } + + public static void Save(this YTrack track, string filePath) + { + SaveAsync(track, filePath).GetAwaiter().GetResult(); + } + + public static int AddLike(this YTrack track) + { + return AddLikeAsync(track).GetAwaiter().GetResult(); + } + + public static int RemoveLike(this YTrack track) + { + return RemoveLikeAsync(track).GetAwaiter().GetResult(); + } + + public static int AddDislike(this YTrack track) + { + return AddDislikeAsync(track).GetAwaiter().GetResult(); + } + + public static int RemoveDislike(this YTrack track) + { + return RemoveDislikeAsync(track).GetAwaiter().GetResult(); + } + + public static string SendPlayTrackInfo(this YTrack track, string from, bool fromCache = false, string playId = "", string playlistId = "", double totalPlayedSeconds = 0, double endPositionSeconds = 0) + { + return SendPlayTrackInfoAsync(track, from, fromCache, playId, playlistId, totalPlayedSeconds).GetAwaiter().GetResult(); + } + + public static YTrackSupplement Supplement(this YTrack track) + { + return SupplementAsync(track).GetAwaiter().GetResult(); + } + + public static YTrackSimilar Similar(this YTrack track) + { + return SimilarAsync(track).GetAwaiter().GetResult(); + } + } +} diff --git a/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs new file mode 100644 index 0000000..cbfba1f --- /dev/null +++ b/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs @@ -0,0 +1,61 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Extensions.API +{ + /// + /// Методы-расширения для трека + /// + public static partial class YTrackExtensions + { + public static Task GetLinkAsync(this YTrack track) + { + return track.Context.API.Track.GetFileLinkAsync(track.Context.Storage, track); + } + + public static Task SaveAsync(this YTrack track, string filePath) + { + return track.Context.API.Track.ExtractToFileAsync(track.Context.Storage, track, filePath); + } + + public static async Task AddLikeAsync(this YTrack track) + { + return (await track.Context.API.Library.AddTrackLikeAsync(track.Context.Storage, track)) + .Result.Revision; + } + + public static async Task RemoveLikeAsync(this YTrack track) + { + return (await track.Context.API.Library.RemoveTrackLikeAsync(track.Context.Storage, track)) + .Result.Revision; + } + + public static async Task AddDislikeAsync(this YTrack track) + { + return (await track.Context.API.Library.AddTrackDislikeAsync(track.Context.Storage, track)) + .Result.Revision; + } + + public static async Task RemoveDislikeAsync(this YTrack track) + { + return (await track.Context.API.Library.RemoveTrackDislikeAsync(track.Context.Storage, track)) + ?.Result.Revision ?? -1; + } + + public static Task SendPlayTrackInfoAsync(this YTrack track, string from, bool fromCache = false, string playId = "", string playlistId = "", double totalPlayedSeconds = 0, double endPositionSeconds = 0) + { + return track.Context.API.Track.SendPlayTrackInfoAsync(track.Context.Storage, track, from, fromCache, playId, playlistId, totalPlayedSeconds); + } + + public static async Task SupplementAsync(this YTrack track) + { + return (await track.Context.API.Track.GetSupplementAsync(track.Context.Storage, track)) + .Result; + } + + public static async Task SimilarAsync(this YTrack track) + { + return (await track.Context.API.Track.GetSimilarAsync(track.Context.Storage, track)) + .Result; + } + } +} diff --git a/YandexMusic.API/Extensions/HttpRequestHeaderExtensions.cs b/YandexMusic.API/Extensions/HttpRequestHeaderExtensions.cs new file mode 100644 index 0000000..8d6c8e3 --- /dev/null +++ b/YandexMusic.API/Extensions/HttpRequestHeaderExtensions.cs @@ -0,0 +1,12 @@ +using System.Net; + +namespace YandexMusic.API.Extensions +{ + public static class HttpRequestHeaderExtensions + { + public static string GetName(this HttpRequestHeader header) + { + return header.ToString().SplitByCapitalLetter("-"); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Extensions/StringExtensions.cs b/YandexMusic.API/Extensions/StringExtensions.cs new file mode 100644 index 0000000..e4911f2 --- /dev/null +++ b/YandexMusic.API/Extensions/StringExtensions.cs @@ -0,0 +1,50 @@ +using System.Text.RegularExpressions; + +namespace YandexMusic.API.Extensions +{ + public static class StringExtensions + { + public static string ReplaceRegex(this string str, string regExpr, string replStr, RegexOptions options = RegexOptions.IgnoreCase) + { + return str == null + ? string.Empty + : Regex.Replace(str, regExpr, replStr); + } + + public static string SplitByCapitalLetter(this string str, string delimiter) + { + return string.Join(delimiter, Regex.Matches(str, @"([A-Z]+)(?=([A-Z][a-z]|$)) | [A-Z][a-z].+?(?=([A-Z]|$))", RegexOptions.IgnorePatternWhitespace) + .Cast() + .Select(m => m.ToString())); + } + + /// + /// Проверяет соответствие регулярному выражению + /// + public static bool IsMatch(this string str, string pattern, RegexOptions options) + { + return Regex.IsMatch(str, pattern, options); + } + + /// + /// Проверяет соответствие регулярному выражению + /// + public static bool IsMatch(this string str, string pattern) + { + return IsMatch(str, pattern, RegexOptions.IgnoreCase); + } + + /// + /// Возвращает совпадения для регулярного выражения + /// + public static string[] GetMatches(this string str, string pattern, RegexOptions options = RegexOptions.IgnoreCase) + { + return str.IsMatch(pattern, options) + ? Regex.Matches(str, pattern, options) + .Cast() + .Select(m => m.Value) + .ToArray() + : new string[] { }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAccessToken.cs b/YandexMusic.API/Models/Account/YAccessToken.cs new file mode 100644 index 0000000..75dbbb3 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAccessToken.cs @@ -0,0 +1,15 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAccessToken + { + [JsonProperty("status")] + public string Status { get; set; } + [JsonProperty("access_token")] + public string AccessToken { get; set; } + [JsonProperty("expires_in")] + public string Expires { get; set; } + [JsonProperty("token_type")] + public string TokenType { get; set; } + public string Uid { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YAccount.cs b/YandexMusic.API/Models/Account/YAccount.cs new file mode 100644 index 0000000..c533034 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAccount.cs @@ -0,0 +1,25 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Account +{ + public class YAccount + { + public bool Child { get; set; } + public string Birthday { get; set; } + public string DisplayName { get; set; } + public string FirstName { get; set; } + public string FullName { get; set; } + public bool HostedUser { get; set; } + public string Login { get; set; } + public bool NonOwnerFamilyMember { get; set; } + public DateTime Now { get; set; } + [JsonProperty("passport-phones")] + public List PassportPhones { get; set; } + public int Region { get; set; } + public string RegionCode { get; set; } + public DateTime RegisteredAt { get; set; } + public string SecondName { get; set; } + public bool ServiceAvailable { get; set; } + public string Uid { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAccountResult.cs b/YandexMusic.API/Models/Account/YAccountResult.cs new file mode 100644 index 0000000..dbeb5ba --- /dev/null +++ b/YandexMusic.API/Models/Account/YAccountResult.cs @@ -0,0 +1,26 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Account +{ + public class YAccountResult + { + public YAccount Account { get; set; } + public string DefaultEmail { get; set; } + public List HasOptions { get; set; } + public YMasterHub MasterHub { get; set; } + public YPermissions Permissions { get; set; } + public YPlus Plus { get; set; } + public bool PretrialActive { get; set; } + public bool SubEditor { get; set; } + public int SubEditorLevel { get; set; } + public YSubscription Subscription { get; set; } + public YBar BarBelow { get; set; } + // Повторяющееся свойство с другим названием + [JsonProperty("bar-below")] + private YBar BarBelow2 + { + set => BarBelow = value; + } + public string Userhash { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAuthBase.cs b/YandexMusic.API/Models/Account/YAuthBase.cs new file mode 100644 index 0000000..8c02c0b --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthBase.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthBase + { + public YAuthStatus Status { get; set; } + + [JsonProperty("redirect_url")] + public string RedirectUrl { get; set; } + + public List Errors { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YAuthCaptcha.cs b/YandexMusic.API/Models/Account/YAuthCaptcha.cs new file mode 100644 index 0000000..01becec --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthCaptcha.cs @@ -0,0 +1,28 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthCaptcha : YAuthBase + { + public string Id { get; set; } + + public string Name { get; set; } + + public string Label { get; set; } + + public string Mode { get; set; } + + public List Error { get; set; } + + public bool CountryFromAudioWhiteList { get; set; } + + public YAuthCaptchaOptions Options { get; set; } + + public YAuthCaptchaVoice Voice { get; set; } + + [JsonProperty("image_url")] + public string ImageUrl { get; set; } + + public string Key { get; set; } + + public string Static { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YAuthCaptchaError.cs b/YandexMusic.API/Models/Account/YAuthCaptchaError.cs new file mode 100644 index 0000000..17385f6 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthCaptchaError.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthCaptchaError + { + public string Message { get; set; } + public YAuthCaptchaErrorCode Code { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YAuthCaptchaErrorCode.cs b/YandexMusic.API/Models/Account/YAuthCaptchaErrorCode.cs new file mode 100644 index 0000000..5faf756 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthCaptchaErrorCode.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Account +{ + public enum YAuthCaptchaErrorCode + { + MissingValue, + CaptchaLocate, + Incorrect + } +} diff --git a/YandexMusic.API/Models/Account/YAuthCaptchaOptions.cs b/YandexMusic.API/Models/Account/YAuthCaptchaOptions.cs new file mode 100644 index 0000000..812e7d5 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthCaptchaOptions.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthCaptchaOptions + { + public bool AsyncCheck { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YAuthCaptchaVoice.cs b/YandexMusic.API/Models/Account/YAuthCaptchaVoice.cs new file mode 100644 index 0000000..cdfb7b8 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthCaptchaVoice.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthCaptchaVoice + { + public string Url { get; set; } + + [JsonProperty("intro_url")] + public string IntroUrl { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAuthError.cs b/YandexMusic.API/Models/Account/YAuthError.cs new file mode 100644 index 0000000..1b12e4e --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthError.cs @@ -0,0 +1,22 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Account +{ + public enum YAuthError + { + [EnumMember(Value = "authorization.invalid")] + AuthorizationInvalid, + [EnumMember(Value = "sessionid.invalid")] + SessionIdInvalid, + [EnumMember(Value = "password.not_matched")] + PasswordNotMatched, + [EnumMember(Value = "password.empty")] + PasswordEmpty, + [EnumMember(Value = "captcha.required")] + CaptchaRequired, + [EnumMember(Value = "captcha.not_matched")] + CaptchaNotMatched, + [EnumMember(Value = "oauth_token.invalid")] + OAuthTokenInvalid + } +} diff --git a/YandexMusic.API/Models/Account/YAuthLetter.cs b/YandexMusic.API/Models/Account/YAuthLetter.cs new file mode 100644 index 0000000..7e13d3a --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthLetter.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthLetter : YAuthBase + { + public List Code { get; set; } + + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAuthLetterStatus.cs b/YandexMusic.API/Models/Account/YAuthLetterStatus.cs new file mode 100644 index 0000000..e367ed0 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthLetterStatus.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthLetterStatus : YAuthBase + { + [JsonProperty("magic_link_confirmed")] + public bool MagicLinkConfirmed { get; set; } + + [JsonProperty("track_id")] + public string TrackId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAuthMethod.cs b/YandexMusic.API/Models/Account/YAuthMethod.cs new file mode 100644 index 0000000..fbc77a2 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthMethod.cs @@ -0,0 +1,22 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Account +{ + public enum YAuthMethod + { + Password, + [EnumMember(Value = "magic_x_token")] + MagicToken, + [EnumMember(Value = "magic_x_token_with_pictures")] + MagicTokenWithPictures, + [EnumMember(Value = "magic_link")] + MagicLink, + Magic, + Otp, + [EnumMember(Value = "social_gg")] + Social, + WebAuthN, + [EnumMember(Value = "sms_code")] + SmsCode + } +} diff --git a/YandexMusic.API/Models/Account/YAuthQr.cs b/YandexMusic.API/Models/Account/YAuthQr.cs new file mode 100644 index 0000000..48c4ac1 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthQr.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthQR : YAuthBase + { + [JsonProperty("track_id")] + public string TrackId { get; set; } + + [JsonProperty("csrf_token")] + public string CsrfToken { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAuthQrStatus.cs b/YandexMusic.API/Models/Account/YAuthQrStatus.cs new file mode 100644 index 0000000..707986b --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthQrStatus.cs @@ -0,0 +1,19 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthQRStatus : YAuthBase + { + [JsonProperty("default_uid")] + public int DefaultUid { get; set; } + + public string RetPath { get; set; } + + [JsonProperty("track_id")] + public string TrackId { get; set; } + + public string Id { get; set; } + + public string State { get; set; } + + public YAuthCaptcha Captcha { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YAuthStatus.cs b/YandexMusic.API/Models/Account/YAuthStatus.cs new file mode 100644 index 0000000..6742cc7 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthStatus.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Account +{ + public enum YAuthStatus + { + Ok, + Error + } +} diff --git a/YandexMusic.API/Models/Account/YAuthToken.cs b/YandexMusic.API/Models/Account/YAuthToken.cs new file mode 100644 index 0000000..d1aeaa7 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthToken.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthToken + { + [JsonProperty("csfr_token")] + public string CsfrToken { get; set; } + + [JsonProperty("track_id")] + public string TrackId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YAuthTypes.cs b/YandexMusic.API/Models/Account/YAuthTypes.cs new file mode 100644 index 0000000..378f392 --- /dev/null +++ b/YandexMusic.API/Models/Account/YAuthTypes.cs @@ -0,0 +1,47 @@ +namespace YandexMusic.API.Models.Account +{ + public class YAuthTypes : YAuthBase + { + [JsonProperty("primary_alias_type")] + public string PrimaryAliasType { get; set; } + + [JsonProperty("csrf_token")] + public string CsrfToken { get; set; } + + public string LocCsrf { get; set; } + + [JsonProperty("use_new_suggest_by_phone")] + public bool UseNewSuggestByPhone { get; set; } + + [JsonProperty("is_rfc_2fa_enabled")] + public bool IsRfc2faEnabled { get; set; } + + [JsonProperty("track_id")] + public string TrackId { get; set; } + + [JsonProperty("can_authorize")] + public string CanAuthorize { get; set; } + + [JsonProperty("preferred_auth_method")] + public YAuthMethod PreferredAuthMethod { get; set; } + + [JsonProperty("auth_methods")] + public List AuthMethods { get; set; } + + [JsonProperty("can_register")] + public bool CanRegister { get; set; } + + [JsonProperty("location_id")] + public string LocationId { get; set; } + + public string Country { get; set; } + + [JsonProperty("phone_number")] + public YPhoneNumber PhoneNumberNumber { get; set; } + + [JsonProperty("magic_link_email")] + public string MagicLinkEmail { get; set; } + + public string TractorTargetLocationHost { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YBar.cs b/YandexMusic.API/Models/Account/YBar.cs new file mode 100644 index 0000000..770bcc5 --- /dev/null +++ b/YandexMusic.API/Models/Account/YBar.cs @@ -0,0 +1,14 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Account +{ + public class YBar : YStyle + { + public string AlertId { get; set; } + public string Text { get; set; } + public string AlertType { get; set; } + public YButton Button { get; set; } + public bool CloseButton { get; set; } + public YCloseButtonStyles CloseButtonStyles { get; set; } + } +} diff --git a/YandexMusic.API/Models/Account/YLoginInfo.cs b/YandexMusic.API/Models/Account/YLoginInfo.cs new file mode 100644 index 0000000..829518b --- /dev/null +++ b/YandexMusic.API/Models/Account/YLoginInfo.cs @@ -0,0 +1,31 @@ +namespace YandexMusic.API.Models.Account +{ + public class YLoginInfo + { + public string Id { get; set; } + public string Login { get; set; } + [JsonProperty("client_id")] + public string ClientId { get; set; } + [JsonProperty("display_name")] + public string DisplayName { get; set; } + [JsonProperty("real_name")] + public string RealName { get; set; } + [JsonProperty("first_name")] + public string FirstName { get; set; } + [JsonProperty("last_name")] + public string LastName { get; set; } + public string Sex { get; set; } + [JsonProperty("default_email")] + public string DefaultEmail { get; set; } + public List Emails { get; set; } + public string Birthday { get; set; } + [JsonProperty("default_avatar_id")] + public string DefaultAvatarId { get; set; } + + public string AvatarUrl => $"https://avatars.mds.yandex.net/get-yapic/{DefaultAvatarId}/islands-200"; + [JsonProperty("is_avatar_empty")] + public bool IsAvatarEmpty { get; set; } + public string PsuId { get; set; } + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YPhoneNumber.cs b/YandexMusic.API/Models/Account/YPhoneNumber.cs new file mode 100644 index 0000000..70c9f2e --- /dev/null +++ b/YandexMusic.API/Models/Account/YPhoneNumber.cs @@ -0,0 +1,20 @@ +namespace YandexMusic.API.Models.Account +{ + public class YPhoneNumber + { + [JsonProperty("masked_e164")] + public string MaskedE164 { get; set; } + + public string E164 { get; set; } + + public string International { get; set; } + + [JsonProperty("masked_original")] + public string MaskedOriginal { get; set; } + + public string Original { get; set; } + + [JsonProperty("masked_international")] + public string MaskedInternational { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Account/YShortAccountInfo.cs b/YandexMusic.API/Models/Account/YShortAccountInfo.cs new file mode 100644 index 0000000..a1768da --- /dev/null +++ b/YandexMusic.API/Models/Account/YShortAccountInfo.cs @@ -0,0 +1,81 @@ +namespace YandexMusic.API.Models.Account +{ + public class YShortAccountInfo : YAuthBase + { + [JsonProperty("public_id")] + public string PublicId { get; set; } + + public string Uid { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public string Birthday { get; set; } + + [JsonProperty("has_password")] + public bool HasPassword { get; set; } + + public List Partitions { get; set; } + + [JsonProperty("primary_alias_type")] + public int PrimaryAliasType { get; set; } + + [JsonProperty("display_name")] + public string DisplayName { get; set; } + + [JsonProperty("normalized_display_login")] + public string NormalizedDisplayLogin { get; set; } + + [JsonProperty("x_token_issued_at")] + public int XTokenIssuedAt { get; set; } + + [JsonProperty("display_login")] + public string DisplayLogin { get; set; } + + [JsonProperty("public_name")] + public string PublicName { get; set; } + + [JsonProperty("avatar_url")] + public string AvatarUrl { get; set; } + + [JsonProperty("native_default_email")] + public string NativeDefaultEmail { get; set; } + + [JsonProperty("has_plus")] + public bool HasPlus { get; set; } + + [JsonProperty("location_id")] + public int LocationId { get; set; } + + [JsonProperty("gender")] + public string Gender { get; set; } + + [JsonProperty("is_avatar_empty")] + public bool IsAvatarEmpty { get; set; } + + [JsonProperty("machine_readable_login")] + public string MachineReadableLogin { get; set; } + + [JsonProperty("has_cards")] + public bool HasCards { get; set; } + + [JsonProperty("has_family")] + public bool HasFamily { get; set; } + + [JsonProperty("picture_login_forbidden")] + public bool PictureLoginForbidden { get; set; } + + [JsonProperty("can_account_join_master")] + public bool CanAccountJoinMaster { get; set; } + + [JsonProperty("secure_phone_number")] + public string SecurePhoneNumber { get; set; } + + [JsonProperty("x_token_client_id")] + public string XTokenClientId { get; set; } + + [JsonProperty("x_token_need_reset")] + public bool XTokenNeedReset { get; set; } + } +} diff --git a/YandexMusic.API/Models/Album/YAlbum.cs b/YandexMusic.API/Models/Album/YAlbum.cs new file mode 100644 index 0000000..80055c3 --- /dev/null +++ b/YandexMusic.API/Models/Album/YAlbum.cs @@ -0,0 +1,98 @@ +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Common.Cover; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Album +{ + public sealed class YLabelConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + JArray jArray = JArray.Load(reader); + JTokenType tokenType = jArray.FirstOrDefault()?.Type ?? JTokenType.String; + object label; + + try + { + label = tokenType switch + { + JTokenType.Object => jArray.ToObject>(), + _ => jArray.ToObject>() + }; + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации типа \"{objectType.Name}\".", ex); + } + + return label; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + JArray array = JArray.FromObject(value); + + array.WriteTo(writer); + } + } + + public class YAlbum : YBaseModel + { + public YButton ActionButton { get; set; } + public List Artists { get; set; } + public bool Available { get; set; } + public bool AvailableForMobile { get; set; } + public List AvailableForOptions { get; set; } + public bool AvailableForPremiumUsers { get; set; } + public bool AvailablePartially { get; set; } + public string BackgroundImageUrl { get; set; } + public string BackgroundVideoUrl { get; set; } + public List Bests { get; set; } + public List Buy { get; set; } + public bool ChildContent { get; set; } + public string ContentWarning { get; set; } + public string CoverUri { get; set; } + [JsonConverter(typeof(YCoverConverter))] + public YCover Cover { get; set; } + public YCustomWave CustomWave { get; set; } + public YDerivedColors DerivedColors { get; set; } + public string Description { get; set; } + public List Disclaimers { get; set; } + public List Duplicates { get; set; } + public bool HasTrailer { get; set; } + public string Genre { get; set; } + public string Id { get; set; } + [JsonConverter(typeof(YLabelConverter))] + public dynamic Labels { get; set; } + public int LikesCount { get; set; } + public bool ListeningFinished { get; set; } + public string MetaTagId { get; set; } + public YMetaType MetaType { get; set; } + public string OgImage { get; set; } + public YPager Pager { get; set; } + public List Prerolls { get; set; } + public bool Recent { get; set; } + public DateTime ReleaseDate { get; set; } + public string ShortDescription { get; set; } + public YSortOrder SortOrder { get; set; } + public string StorageDir { get; set; } + public string Title { get; set; } + public int TrackCount { get; set; } + public YTrackPosition TrackPosition { get; set; } + public YTrailer Trailer { get; set; } + public string Type { get; set; } + public string Version { get; set; } + public bool VeryImportant { get; set; } + public List> Volumes { get; set; } + public int Year { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YArtist.cs b/YandexMusic.API/Models/Artist/YArtist.cs new file mode 100644 index 0000000..c4c36fa --- /dev/null +++ b/YandexMusic.API/Models/Artist/YArtist.cs @@ -0,0 +1,41 @@ +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Common.Cover; + +namespace YandexMusic.API.Models.Artist +{ + public class YArtist : YBaseModel + { + public YButton ActionButton { get; set; } + public bool Available { get; set; } + public bool Composer { get; set; } + public List Countries { get; set; } + public YArtistCounts Counts { get; set; } + [JsonConverter(typeof(YCoverConverter))] + public YCover Cover { get; set; } + public List DbAliases { get; set; } +#warning Непонятная коллекция с содержимым разных типов + public List Decomposed { get; set; } + public YDerivedColors DerivedColors { get; set; } + public YDescription Description { get; set; } + public YDeprecation Deprecation { get; set; } + public List Disclaimers { get; set; } + public string EndDate { get; set; } + public string EnWikipediaLink { get; set; } + public List ExtraActions { get; set; } + public List Genres { get; set; } + public string Id { get; set; } + public string InitDate { get; set; } + public int LikesCount { get; set; } + public List Links { get; set; } + public string Name { get; set; } + public bool NoPicturesFromSearch { get; set; } + public string OgImage { get; set; } + public YArtistRatings Ratings { get; set; } + public bool TicketsAvailable { get; set; } + public DateTime Timestamp { get; set; } + public bool Various { get; set; } + public string YaMoneyId { get; set; } + public bool HasTrailer { get; set; } + public YTrailer Trailer { get; set; } + } +} diff --git a/YandexMusic.API/Models/Artist/YArtistBriefInfo.cs b/YandexMusic.API/Models/Artist/YArtistBriefInfo.cs new file mode 100644 index 0000000..085d684 --- /dev/null +++ b/YandexMusic.API/Models/Artist/YArtistBriefInfo.cs @@ -0,0 +1,36 @@ +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Common.Cover; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Artist +{ + public class YArtistBriefInfo + { + public YButton ActionButton { get; set; } + public List Albums { get; set; } + [JsonProperty(ItemConverterType = typeof(YCoverConverter))] + public List AllCovers { get; set; } + public List AlsoAlbums { get; set; } + public YArtist Artist { get; set; } + public string BackgroundVideoUrl { get; set; } + public YBandlinkScannerLink BandlinkScannerLink { get; set; } + public List Clips { get; set; } + public List Concerts { get; set; } + public YCustomWave CustomWave { get; set; } + public List ExtraActions { get; set; } + public bool HasPromotions { get; set; } + public bool HasTrailer { get; set; } + public List LastReleaseIds { get; set; } + public List LastReleases { get; set; } + public List PlaylistIds { get; set; } + public List Playlists { get; set; } + public List PopularTracks { get; set; } + public List SimilarArtists { get; set; } + public YStats Stats { get; set; } + public List Videos { get; set; } + public List Vinyls { get; set; } + public List Links { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YArtistCounts.cs b/YandexMusic.API/Models/Artist/YArtistCounts.cs new file mode 100644 index 0000000..2b24ed0 --- /dev/null +++ b/YandexMusic.API/Models/Artist/YArtistCounts.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Artist +{ + public class YArtistCounts + { + public int AlsoAlbums { get; set; } + public int AlsoTracks { get; set; } + public int DirectAlbums { get; set; } + public int Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YArtistRatings.cs b/YandexMusic.API/Models/Artist/YArtistRatings.cs new file mode 100644 index 0000000..4312010 --- /dev/null +++ b/YandexMusic.API/Models/Artist/YArtistRatings.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Artist +{ + public class YArtistRatings + { + public int Day { get; set; } + public int Month { get; set; } + public int Week { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YBandlinkScannerLink.cs b/YandexMusic.API/Models/Artist/YBandlinkScannerLink.cs new file mode 100644 index 0000000..e05c405 --- /dev/null +++ b/YandexMusic.API/Models/Artist/YBandlinkScannerLink.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Artist +{ + public class YBandlinkScannerLink + { + public string Title { get; set; } + public string Subtitle { get; set; } + public string Url { get; set; } + public string ImgUrl { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YConcert.cs b/YandexMusic.API/Models/Artist/YConcert.cs new file mode 100644 index 0000000..a867253 --- /dev/null +++ b/YandexMusic.API/Models/Artist/YConcert.cs @@ -0,0 +1,29 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Artist +{ + public class YConcert + { + public string Address { get; set; } + public string AfishaUrl { get; set; } + public YArtist Artist { get; set; } + public string City { get; set; } + public string ConcertTitle { get; set; } + public string ContentRating { get; set; } + public List Coordinates { get; set; } + public YCashback Cashback { get; set; } + [JsonProperty("data-session-id")] + public string DataSessionId { get; set; } + public DateTime DateTime { get; set; } + public string Hash { get; set; } + public string Id { get; set; } + public List Images { get; set; } + public string ImageUrl { get; set; } + public string Map { get; set; } + public string MapUrl { get; set; } + [JsonProperty("metro-stations")] + public List MetroStations { get; set; } + public string Place { get; set; } + public YPrice MinPrice { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YDeprecation.cs b/YandexMusic.API/Models/Artist/YDeprecation.cs new file mode 100644 index 0000000..3d2522b --- /dev/null +++ b/YandexMusic.API/Models/Artist/YDeprecation.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Artist +{ + public class YDeprecation + { + public string TargetArtistId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YMetroStation.cs b/YandexMusic.API/Models/Artist/YMetroStation.cs new file mode 100644 index 0000000..bba4d40 --- /dev/null +++ b/YandexMusic.API/Models/Artist/YMetroStation.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Artist +{ + public class YMetroStation + { + [JsonProperty("line-color")] + public string LineColor { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Artist/YTracksPage.cs b/YandexMusic.API/Models/Artist/YTracksPage.cs new file mode 100644 index 0000000..e4885fb --- /dev/null +++ b/YandexMusic.API/Models/Artist/YTracksPage.cs @@ -0,0 +1,12 @@ +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Artist +{ + public class YTracksPage + { + public YPager Pager { get; set; } + + public List Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCover.cs b/YandexMusic.API/Models/Common/Cover/YCover.cs new file mode 100644 index 0000000..5b6e855 --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCover.cs @@ -0,0 +1,71 @@ +namespace YandexMusic.API.Models.Common.Cover +{ + public sealed class YCoverConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(YCover).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + JObject jObject = JObject.Load(reader); + YCover cover; + + try + { + // Фиктивный тип, т.к. у такой обложки нет поля с типом + if (jObject["type"] == null) + jObject.Add("type", "color"); + + YCoverType type = jObject["error"] != null + ? YCoverType.Error + : jObject["type"].ToObject(); + + switch (type) + { + case YCoverType.Error: + cover = jObject.ToObject(); + break; + case YCoverType.Color: + cover = jObject.ToObject(); + break; + case YCoverType.FromAlbumCover: + case YCoverType.FromArtistPhotos: + cover = jObject.ToObject(); + break; + case YCoverType.Pic: + cover = jObject.ToObject(); + break; + case YCoverType.Mosaic: + cover = jObject.ToObject(); + break; + default: + cover = jObject.ToObject(); + break; + } + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации типа \"{objectType.Name}\".", ex); + } + + return cover; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + JObject cover = JObject.FromObject(value, serializer); + + cover.WriteTo(writer); + } + } + + public class YCover + { + public YCoverType Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCoverColor.cs b/YandexMusic.API/Models/Common/Cover/YCoverColor.cs new file mode 100644 index 0000000..de8af5f --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCoverColor.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common.Cover +{ + public class YCoverColor : YCover + { + public string Uri { get; set; } + public string Color { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCoverError.cs b/YandexMusic.API/Models/Common/Cover/YCoverError.cs new file mode 100644 index 0000000..1475748 --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCoverError.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common.Cover +{ + public class YCoverError : YCover + { + public string Error { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCoverImage.cs b/YandexMusic.API/Models/Common/Cover/YCoverImage.cs new file mode 100644 index 0000000..613d270 --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCoverImage.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common.Cover +{ + public class YCoverImage : YCover + { + public string Prefix { get; set; } + public string Uri { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCoverMosaic.cs b/YandexMusic.API/Models/Common/Cover/YCoverMosaic.cs new file mode 100644 index 0000000..91d00b5 --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCoverMosaic.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common.Cover +{ + public class YCoverMosaic : YCover + { + public bool Custom { get; set; } + public List ItemsUri { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCoverPic.cs b/YandexMusic.API/Models/Common/Cover/YCoverPic.cs new file mode 100644 index 0000000..42c606d --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCoverPic.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Common.Cover +{ + public class YCoverPic : YCover + { + public bool Custom { get; set; } + public string Dir { get; set; } + public bool IsCustom { get; set; } + public string Uri { get; set; } + public string Version { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/Cover/YCoverType.cs b/YandexMusic.API/Models/Common/Cover/YCoverType.cs new file mode 100644 index 0000000..ac5ec0c --- /dev/null +++ b/YandexMusic.API/Models/Common/Cover/YCoverType.cs @@ -0,0 +1,17 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Common.Cover +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum YCoverType + { + Color, + Error, + [EnumMember(Value = "from-artist-photos")] + FromArtistPhotos, + [EnumMember(Value = "from-album-cover")] + FromAlbumCover, + Mosaic, + Pic + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YBaseModel.cs b/YandexMusic.API/Models/Common/YBaseModel.cs new file mode 100644 index 0000000..9548767 --- /dev/null +++ b/YandexMusic.API/Models/Common/YBaseModel.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YBaseModel + { + [JsonIgnore] + public YExecutionContext Context { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YButton.cs b/YandexMusic.API/Models/Common/YButton.cs new file mode 100644 index 0000000..5f43da2 --- /dev/null +++ b/YandexMusic.API/Models/Common/YButton.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Common +{ + public class YButton : YStyle + { + public string Text { get; set; } + public string Url { get; set; } +#warning Дублирование? + public string Uri { get; set; } + public string Color { get; set; } + public bool ViewBrowser { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YCashback.cs b/YandexMusic.API/Models/Common/YCashback.cs new file mode 100644 index 0000000..dda2e79 --- /dev/null +++ b/YandexMusic.API/Models/Common/YCashback.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public class YCashback + { + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YChart.cs b/YandexMusic.API/Models/Common/YChart.cs new file mode 100644 index 0000000..b5515be --- /dev/null +++ b/YandexMusic.API/Models/Common/YChart.cs @@ -0,0 +1,12 @@ +using YandexMusic.API.Models.Landing.Entity.Entities; + +namespace YandexMusic.API.Models.Common +{ + public class YChart + { + public int Position { get; set; } + public int Listeners { get; set; } + public int Shift { get; set; } + public YChartProgress Progress { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YClip.cs b/YandexMusic.API/Models/Common/YClip.cs new file mode 100644 index 0000000..8096b11 --- /dev/null +++ b/YandexMusic.API/Models/Common/YClip.cs @@ -0,0 +1,19 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Common +{ + public class YClip + { + public List Artists { get; set; } + public string ClipId { get; set; } + public List Disclaimers { get; set; } + public int Duration { get; set; } + public bool Explicit { get; set; } + public string PlayerId { get; set; } + public string PreviewUrl { get; set; } + public string Uuid { get; set; } + public string Thumbnail { get; set; } + public string Title { get; set; } + public List TrackIds { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YCloseButtonStyles.cs b/YandexMusic.API/Models/Common/YCloseButtonStyles.cs new file mode 100644 index 0000000..6e7692c --- /dev/null +++ b/YandexMusic.API/Models/Common/YCloseButtonStyles.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YCloseButtonStyles + { + public YStyle White { get; set; } + public YStyle Black { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YCountsTracks.cs b/YandexMusic.API/Models/Common/YCountsTracks.cs new file mode 100644 index 0000000..7ef3e34 --- /dev/null +++ b/YandexMusic.API/Models/Common/YCountsTracks.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Common +{ + public class YCountsTracks + { + public int All { get; set; } + public int Favorite { get; set; } + public int Ugc { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YCustomWave.cs b/YandexMusic.API/Models/Common/YCustomWave.cs new file mode 100644 index 0000000..db10f30 --- /dev/null +++ b/YandexMusic.API/Models/Common/YCustomWave.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Common +{ + public class YCustomWave + { + public string AnimationUrl { get; set; } + public string BackgroundImageUrl { get; set; } + public string Header { get; set; } + public string Position { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YDerivedColors.cs b/YandexMusic.API/Models/Common/YDerivedColors.cs new file mode 100644 index 0000000..6b3295f --- /dev/null +++ b/YandexMusic.API/Models/Common/YDerivedColors.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Common +{ + public class YDerivedColors + { + public string Average { get; set; } + public string WaveText { get; set; } + public string MiniPlayer { get; set; } + public string Accent { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YDescription.cs b/YandexMusic.API/Models/Common/YDescription.cs new file mode 100644 index 0000000..736f6ce --- /dev/null +++ b/YandexMusic.API/Models/Common/YDescription.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YDescription + { + public string Text { get; set; } + public string Uri { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YError.cs b/YandexMusic.API/Models/Common/YError.cs new file mode 100644 index 0000000..0421110 --- /dev/null +++ b/YandexMusic.API/Models/Common/YError.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YError + { + public string Name { get; set; } + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YErrorResponse.cs b/YandexMusic.API/Models/Common/YErrorResponse.cs new file mode 100644 index 0000000..ea40cbb --- /dev/null +++ b/YandexMusic.API/Models/Common/YErrorResponse.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YErrorResponse : Exception + { + public YInvocationInfo InvocationInfo { get; set; } + public YError Error { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YExecutionContext.cs b/YandexMusic.API/Models/Common/YExecutionContext.cs new file mode 100644 index 0000000..9305479 --- /dev/null +++ b/YandexMusic.API/Models/Common/YExecutionContext.cs @@ -0,0 +1,57 @@ +using YandexMusic.API.Common; + +namespace YandexMusic.API.Models.Common +{ + public sealed class YExecutionContextConverter : JsonConverter + { + #region Поля + + private YandexMusicApi api; + private AuthStorage storage; + + #endregion Поля + + public override bool CanConvert(Type objectType) + { + return typeof(YBaseModel).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + try + { + YBaseModel obj = (YBaseModel)Activator.CreateInstance(objectType); + serializer.Populate(reader, obj); + + obj.Context = new YExecutionContext + { + API = api, + Storage = storage + }; + + return obj; + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации типа \"{objectType.Name}\".", ex); + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public YExecutionContextConverter(YandexMusicApi yandex, AuthStorage auth) + { + api = yandex; + storage = auth; + } + } + + public class YExecutionContext + { + public YandexMusicApi API { get; internal set; } + public AuthStorage Storage { get; internal set; } + } +} diff --git a/YandexMusic.API/Models/Common/YExtraAction.cs b/YandexMusic.API/Models/Common/YExtraAction.cs new file mode 100644 index 0000000..e4467a1 --- /dev/null +++ b/YandexMusic.API/Models/Common/YExtraAction.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Common +{ + public class YExtraAction + { + public string Type { get; set; } + public string Title { get; set; } + public string Color { get; set; } + public string Url { get; set; } + public bool ViewBrowser { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YId.cs b/YandexMusic.API/Models/Common/YId.cs new file mode 100644 index 0000000..33030b3 --- /dev/null +++ b/YandexMusic.API/Models/Common/YId.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public class YId + { + public string Id { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YInvocationInfo.cs b/YandexMusic.API/Models/Common/YInvocationInfo.cs new file mode 100644 index 0000000..4318bfe --- /dev/null +++ b/YandexMusic.API/Models/Common/YInvocationInfo.cs @@ -0,0 +1,13 @@ +namespace YandexMusic.API.Models.Common +{ + public class YInvocationInfo + { + [JsonProperty("app-name")] + public string AppName { get; set; } + [JsonProperty("exec-duration-millis")] + public int ExecDurationMillis { get; set; } + public string HostName { get; set; } + [JsonProperty("req-id")] + public string ReqId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YLabel.cs b/YandexMusic.API/Models/Common/YLabel.cs new file mode 100644 index 0000000..faa3d2b --- /dev/null +++ b/YandexMusic.API/Models/Common/YLabel.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YLabel + { + public string Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YLikedCounts.cs b/YandexMusic.API/Models/Common/YLikedCounts.cs new file mode 100644 index 0000000..91c564e --- /dev/null +++ b/YandexMusic.API/Models/Common/YLikedCounts.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YLikedCounts + { + public long LikedAlbums { get; set; } + public long LikedArtists { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YLink.cs b/YandexMusic.API/Models/Common/YLink.cs new file mode 100644 index 0000000..22b5434 --- /dev/null +++ b/YandexMusic.API/Models/Common/YLink.cs @@ -0,0 +1,13 @@ +namespace YandexMusic.API.Models.Common +{ + public class YLink + { + public string Href { get; set; } + public string Url { get; set; } + public string ImgUrl { get; set; } + public string SocialNetwork { get; set; } + public string Subtitle { get; set; } + public string Title { get; set; } + public YLinkType Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YLinkType.cs b/YandexMusic.API/Models/Common/YLinkType.cs new file mode 100644 index 0000000..b1fa892 --- /dev/null +++ b/YandexMusic.API/Models/Common/YLinkType.cs @@ -0,0 +1,25 @@ +namespace YandexMusic.API.Models.Common +{ + public enum YLinkType + { + /// + /// Официальный сайт + /// + Official, + + /// + /// Социальная сеть + /// + Social, + + /// + /// Twitter + /// + Twitter, + + /// + /// YouTube + /// + YouTube + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YLyrics.cs b/YandexMusic.API/Models/Common/YLyrics.cs new file mode 100644 index 0000000..ca8b34f --- /dev/null +++ b/YandexMusic.API/Models/Common/YLyrics.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Common +{ + public class YLyrics + { + public string Id { get; set; } + public string Lyrics { get; set; } + public string FullLyrics { get; set; } + public bool HasRights { get; set; } + public bool ShowTranslation { get; set; } + public string TextLanguage { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YLyricsInfo.cs b/YandexMusic.API/Models/Common/YLyricsInfo.cs new file mode 100644 index 0000000..3d4d057 --- /dev/null +++ b/YandexMusic.API/Models/Common/YLyricsInfo.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YLyricsInfo + { + public bool HasAvailableSyncLyrics { get; set; } + public bool HasAvailableTextLyrics { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YMajor.cs b/YandexMusic.API/Models/Common/YMajor.cs new file mode 100644 index 0000000..fe9ffb6 --- /dev/null +++ b/YandexMusic.API/Models/Common/YMajor.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YMajor + { + public string Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YMasterHub.cs b/YandexMusic.API/Models/Common/YMasterHub.cs new file mode 100644 index 0000000..13ea416 --- /dev/null +++ b/YandexMusic.API/Models/Common/YMasterHub.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YMasterHub + { + public YSubscription[] ActiveSubscriptions { get; set; } + public YSubscription[] AvailableSubscriptions { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YMetaType.cs b/YandexMusic.API/Models/Common/YMetaType.cs new file mode 100644 index 0000000..78cf0a8 --- /dev/null +++ b/YandexMusic.API/Models/Common/YMetaType.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public enum YMetaType + { + Music, + Podcast + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YOwner.cs b/YandexMusic.API/Models/Common/YOwner.cs new file mode 100644 index 0000000..ef95bf0 --- /dev/null +++ b/YandexMusic.API/Models/Common/YOwner.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Common +{ + public class YOwner + { + public string Login { get; set; } + public string Name { get; set; } + public string Sex { get; set; } + public string Uid { get; set; } + public bool Verified { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YPager.cs b/YandexMusic.API/Models/Common/YPager.cs new file mode 100644 index 0000000..89ee60a --- /dev/null +++ b/YandexMusic.API/Models/Common/YPager.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPager + { + public int Total { get; set; } + public int Page { get; set; } + public int PerPage { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YPeriod.cs b/YandexMusic.API/Models/Common/YPeriod.cs new file mode 100644 index 0000000..76c579d --- /dev/null +++ b/YandexMusic.API/Models/Common/YPeriod.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPeriod + { + public DateTime Start { get; set; } + public DateTime End { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YPermissions.cs b/YandexMusic.API/Models/Common/YPermissions.cs new file mode 100644 index 0000000..026118b --- /dev/null +++ b/YandexMusic.API/Models/Common/YPermissions.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPermissions + { + public List Default { get; set; } + public DateTime Until { get; set; } + public List Values { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YPhone.cs b/YandexMusic.API/Models/Common/YPhone.cs new file mode 100644 index 0000000..d2c7734 --- /dev/null +++ b/YandexMusic.API/Models/Common/YPhone.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPhone + { + public string Phone { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YPlus.cs b/YandexMusic.API/Models/Common/YPlus.cs new file mode 100644 index 0000000..df10819 --- /dev/null +++ b/YandexMusic.API/Models/Common/YPlus.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPlus + { + public bool HasPlus { get; set; } + public bool IsTutorialCompleted { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YPodcastEpisodeType.cs b/YandexMusic.API/Models/Common/YPodcastEpisodeType.cs new file mode 100644 index 0000000..0ed8cee --- /dev/null +++ b/YandexMusic.API/Models/Common/YPodcastEpisodeType.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Common +{ + public enum YPodcastEpisodeType + { + Full, + Trailer, + Bonus + } +} diff --git a/YandexMusic.API/Models/Common/YPrerolls.cs b/YandexMusic.API/Models/Common/YPrerolls.cs new file mode 100644 index 0000000..bfde8e2 --- /dev/null +++ b/YandexMusic.API/Models/Common/YPrerolls.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPrerolls + { + public string Id { get; set; } + public string Link { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YPrice.cs b/YandexMusic.API/Models/Common/YPrice.cs new file mode 100644 index 0000000..57a7043 --- /dev/null +++ b/YandexMusic.API/Models/Common/YPrice.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Common +{ + public class YPrice + { + public decimal Amount { get; set; } + public string Currency { get; set; } + public string CurrencySymbol { get; set; } + public decimal Value { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YProduct.cs b/YandexMusic.API/Models/Common/YProduct.cs new file mode 100644 index 0000000..04d3137 --- /dev/null +++ b/YandexMusic.API/Models/Common/YProduct.cs @@ -0,0 +1,22 @@ +namespace YandexMusic.API.Models.Common +{ + public class YProduct + { + public string IntroPeriodDuration { get; set; } + public string StartPeriodDuration { get; set; } + public string CommonPeriodDuration { get; set; } + public string TrialPeriodDuration { get; set; } + public bool Debug { get; set; } + public int Duration { get; set; } + public bool Family { get; set; } + public string Feature { get; set; } + public List Features { get; set; } + public bool Plus { get; set; } + public YPrice IntroPrice { get; set; } + public YPrice StartPrice { get; set; } + public YPrice Price { get; set; } + public string ProductId { get; set; } + public int TrialDuration { get; set; } + public YProductType Type { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YProductType.cs b/YandexMusic.API/Models/Common/YProductType.cs new file mode 100644 index 0000000..30751bc --- /dev/null +++ b/YandexMusic.API/Models/Common/YProductType.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public enum YProductType + { + Subscription + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YProfile.cs b/YandexMusic.API/Models/Common/YProfile.cs new file mode 100644 index 0000000..ce11719 --- /dev/null +++ b/YandexMusic.API/Models/Common/YProfile.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YProfile + { + public List Addresses { get; set; } + public string Provider { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YReminder.cs b/YandexMusic.API/Models/Common/YReminder.cs new file mode 100644 index 0000000..b631a16 --- /dev/null +++ b/YandexMusic.API/Models/Common/YReminder.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public class YReminder + { + public int Days { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YResponse.cs b/YandexMusic.API/Models/Common/YResponse.cs new file mode 100644 index 0000000..d9264e3 --- /dev/null +++ b/YandexMusic.API/Models/Common/YResponse.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Common +{ + /// + /// Модель ответа от API + /// + public class YResponse + { + public YInvocationInfo InvocationInfo { get; set; } + public T Result { get; set; } + public YPager Pager { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YRevision.cs b/YandexMusic.API/Models/Common/YRevision.cs new file mode 100644 index 0000000..05cad29 --- /dev/null +++ b/YandexMusic.API/Models/Common/YRevision.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public class YRevision + { + public int Revision { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YSearchType.cs b/YandexMusic.API/Models/Common/YSearchType.cs new file mode 100644 index 0000000..d2f65ab --- /dev/null +++ b/YandexMusic.API/Models/Common/YSearchType.cs @@ -0,0 +1,56 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Common +{ + /// + /// Тип объекта поиска + /// + public enum YSearchType + { + /// + /// Отсутствует значение + /// + None, + + /// + /// Артисты + /// + Artist, + + /// + /// Альбомы + /// + Album, + + /// + /// Все + /// + All, + + /// + /// Плейлисты + /// + Playlist, + + /// + /// Эпизод подкаста + /// + [EnumMember(Value = "podcast_episode")] + PodcastEpisode, + + /// + /// Видео + /// + Video, + + /// + /// Треки + /// + Track, + + /// + /// Пользователи + /// + User + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YSortOrder.cs b/YandexMusic.API/Models/Common/YSortOrder.cs new file mode 100644 index 0000000..9ffa67a --- /dev/null +++ b/YandexMusic.API/Models/Common/YSortOrder.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public enum YSortOrder + { + Asc, + Desc + } +} diff --git a/YandexMusic.API/Models/Common/YStats.cs b/YandexMusic.API/Models/Common/YStats.cs new file mode 100644 index 0000000..55f36fb --- /dev/null +++ b/YandexMusic.API/Models/Common/YStats.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YStats + { + public int LastMonthListeners { get; set; } + public int LastMonthListenersDelta { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YStorageDownloadFile.cs b/YandexMusic.API/Models/Common/YStorageDownloadFile.cs new file mode 100644 index 0000000..8ff22e5 --- /dev/null +++ b/YandexMusic.API/Models/Common/YStorageDownloadFile.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Common +{ + public class YStorageDownloadFile + { + public string Host { get; set; } + public string Path { get; set; } + public string S { get; set; } + public string Ts { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YStyle.cs b/YandexMusic.API/Models/Common/YStyle.cs new file mode 100644 index 0000000..f917837 --- /dev/null +++ b/YandexMusic.API/Models/Common/YStyle.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Common +{ + public class YStyle + { + public string BgColor { get; set; } + public string BorderColor { get; set; } + public string TextColor { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YSubscription.cs b/YandexMusic.API/Models/Common/YSubscription.cs new file mode 100644 index 0000000..5581791 --- /dev/null +++ b/YandexMusic.API/Models/Common/YSubscription.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Common +{ + public class YSubscription + { + public List AutoRenewable { get; set; } + public bool CanStartTrial { get; set; } + public bool HadAnySubscription { get; set; } + public bool McDonalds { get; set; } + public YPeriod NonAutoRenewable { get; set; } + public YReminder NonAutoRenewableRemainder { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YSubscriptionService.cs b/YandexMusic.API/Models/Common/YSubscriptionService.cs new file mode 100644 index 0000000..c6e6d0a --- /dev/null +++ b/YandexMusic.API/Models/Common/YSubscriptionService.cs @@ -0,0 +1,13 @@ +namespace YandexMusic.API.Models.Common +{ + public class YSubscriptionService + { + public DateTime Expires { get; set; } + public bool Finished { get; set; } + public decimal OrderId { get; set; } + public string ProductId { get; set; } + public YProduct Product { get; set; } + public string Vendor { get; set; } + public string VendorHelpUrl { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YTag.cs b/YandexMusic.API/Models/Common/YTag.cs new file mode 100644 index 0000000..da6d20e --- /dev/null +++ b/YandexMusic.API/Models/Common/YTag.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Common +{ + public class YTag + { + public string Id { get; set; } + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YTrackDownloadInfo.cs b/YandexMusic.API/Models/Common/YTrackDownloadInfo.cs new file mode 100644 index 0000000..44f3244 --- /dev/null +++ b/YandexMusic.API/Models/Common/YTrackDownloadInfo.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Common +{ + public class YTrackDownloadInfo + { + public int BitrateInKbps { get; set; } + public string Codec { get; set; } + public bool Direct { get; set; } + public string DownloadInfoUrl { get; set; } + public bool Gain { get; set; } + public bool Preview { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YTrackId.cs b/YandexMusic.API/Models/Common/YTrackId.cs new file mode 100644 index 0000000..1c7e9df --- /dev/null +++ b/YandexMusic.API/Models/Common/YTrackId.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Common +{ + public class YTrackId + { + public string Id { get; set; } + public string TrackId { get; set; } + public string AlbumId { get; set; } + public string From { get; set; } + } +} diff --git a/YandexMusic.API/Models/Common/YTrackSharingFlag.cs b/YandexMusic.API/Models/Common/YTrackSharingFlag.cs new file mode 100644 index 0000000..9b07ead --- /dev/null +++ b/YandexMusic.API/Models/Common/YTrackSharingFlag.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Common +{ + public enum YTrackSharingFlag + { + [EnumMember(Value = "VIDEO_ALLOWED")] + VideoAllowed, + [EnumMember(Value = "COVER_ONLY")] + CoverOnly + } +} diff --git a/YandexMusic.API/Models/Common/YTrackSource.cs b/YandexMusic.API/Models/Common/YTrackSource.cs new file mode 100644 index 0000000..6ae8cc5 --- /dev/null +++ b/YandexMusic.API/Models/Common/YTrackSource.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Common +{ + public enum YTrackSource + { + [EnumMember(Value = "OWN")] + Own, + + [EnumMember(Value = "UGC")] + [Description("User Generated Content")] + UGC, + + [EnumMember(Value = "OWN_REPLACED_TO_UGC")] + OwnReplacedToUGC, + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YTrailer.cs b/YandexMusic.API/Models/Common/YTrailer.cs new file mode 100644 index 0000000..1bc74b3 --- /dev/null +++ b/YandexMusic.API/Models/Common/YTrailer.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Common +{ + public class YTrailer + { + public bool Available { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YUser.cs b/YandexMusic.API/Models/Common/YUser.cs new file mode 100644 index 0000000..0d71baa --- /dev/null +++ b/YandexMusic.API/Models/Common/YUser.cs @@ -0,0 +1,17 @@ +namespace YandexMusic.API.Models.Common +{ + public class YUser + { + public string DeviceId { get; set; } + public string Experiments { get; set; } + public string FirstName { get; set; } + public string Lang { get; set; } + public string Login { get; set; } + public string Name { get; set; } + public string SecondName { get; set; } + public string Sign { get; set; } + public long Timestamp { get; set; } + public string Uid { get; set; } + public string YandexId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YVideo.cs b/YandexMusic.API/Models/Common/YVideo.cs new file mode 100644 index 0000000..b41d8fc --- /dev/null +++ b/YandexMusic.API/Models/Common/YVideo.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Common +{ + public class YVideo + { + public string Cover { get; set; } + public string EmbedUrl { get; set; } + public string Provider { get; set; } + public string ProviderVideoId { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Common/YVinyl.cs b/YandexMusic.API/Models/Common/YVinyl.cs new file mode 100644 index 0000000..ad27843 --- /dev/null +++ b/YandexMusic.API/Models/Common/YVinyl.cs @@ -0,0 +1,14 @@ +namespace YandexMusic.API.Models.Common +{ + public class YVinyl + { + public List ArtistIds { get; set; } + public string Media { get; set; } + public string OfferId { get; set; } + public string Picture { get; set; } + public decimal Price { get; set; } + public string Title { get; set; } + public string Url { get; set; } + public int Year { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YArtistsFromHistory.cs b/YandexMusic.API/Models/Feed/Event/YArtistsFromHistory.cs new file mode 100644 index 0000000..25ec5d9 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YArtistsFromHistory.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YArtistsFromHistory + { + public YArtist Artist { get; set; } + public List ArtistsFromHistory { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEvent.cs b/YandexMusic.API/Models/Feed/Event/YFeedEvent.cs new file mode 100644 index 0000000..5cea1ae --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEvent.cs @@ -0,0 +1,104 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public sealed class YFeedEventConverter : JsonConverter + { + private YFeedEventTitled GetEvent(JToken jObject) + { + YFeedEventTitled feedEvent; + + YFeedEventType type = jObject[jObject["typeForFrom"] != null ? "typeForFrom" : "type"] + .ToObject(); + + switch (type) + { + case YFeedEventType.GenreTop: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.NewAlbums: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.NewAlbumsOfFavoriteGenre: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.Notification: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.Promotion: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.RecentTrackLikeToTracks: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.RecommendedArtistsWithArtistsFromHistory: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.RecommendedSimilarArtists: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.RecommendedSimilarGenre: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.MissedTracksByArtist: + case YFeedEventType.RareArtist: + case YFeedEventType.RecommendedTracksByArtistFromHistory: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.NewTracksOfFavoriteGenre: + case YFeedEventType.TracksByGenre: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.WellForgottenOldArtists: + feedEvent = jObject.ToObject(); + break; + + case YFeedEventType.NeverHeardFromLibrary: + case YFeedEventType.WellForgottenOldTracks: + feedEvent = jObject.ToObject(); + break; + + default: + feedEvent = jObject.ToObject(); + break; + } + + return feedEvent; + } + + public override bool CanConvert(Type objectType) + { + return typeof(YFeedEventTitled).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + return JArray.Load(reader) + .Select(GetEvent) + .ToList(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + public class YFeedEvent + { + public string Id { get; set; } + public YFeedEventType Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventAlbums.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventAlbums.cs new file mode 100644 index 0000000..2acfeb3 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventAlbums.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventAlbums : YFeedEventTitled + { + public List Albums { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventArtist.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventArtist.cs new file mode 100644 index 0000000..e89f34b --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventArtist.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventArtist : YFeedEventTracks + { + public YArtist Artist { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventArtistWithArtists.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventArtistWithArtists.cs new file mode 100644 index 0000000..412673e --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventArtistWithArtists.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventArtistWithArtists : YFeedEventTitled + { + public List ArtistsWithArtistsFromHistory { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventArtists.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventArtists.cs new file mode 100644 index 0000000..4d180e5 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventArtists.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventArtists : YFeedEventTitled + { + public List Artists { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventGenreAlbums.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventGenreAlbums.cs new file mode 100644 index 0000000..dea9b71 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventGenreAlbums.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventGenreAlbums : YFeedEventAlbums + { + public string Genre { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracks.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracks.cs new file mode 100644 index 0000000..73d6719 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracks.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventGenreTracks : YFeedEventTracks + { + public string Genre { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracksTop.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracksTop.cs new file mode 100644 index 0000000..9b26809 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventGenreTracksTop.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventGenreTracksTop : YFeedEventGenreTracks + { + public bool RadioIsAvailable { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventLikeTrack.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventLikeTrack.cs new file mode 100644 index 0000000..0a7a2b1 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventLikeTrack.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventLikeTrack : YFeedEventTracks + { + public YTrack LikedTrack { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventNotification.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventNotification.cs new file mode 100644 index 0000000..8db2406 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventNotification.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventNotification : YFeedEventTitled + { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventPromotion.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventPromotion.cs new file mode 100644 index 0000000..ea2716a --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventPromotion.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventPromotion : YFeedEventTitled + { + public YFeedPromotion Promo { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventPromotionType.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventPromotionType.cs new file mode 100644 index 0000000..3f7d1b0 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventPromotionType.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public enum YFeedEventPromotionType + { + Albums, + Tracks + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventSimilarArtists.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventSimilarArtists.cs new file mode 100644 index 0000000..2ad0537 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventSimilarArtists.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventSimilarArtists : YFeedEventTitled + { + public YArtist SimilarToArtist { get; set; } + public List SimilarArtists { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventSimilarGenre.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventSimilarGenre.cs new file mode 100644 index 0000000..0136615 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventSimilarGenre.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventSimilarGenre : YFeedEventTracks + { + public string SimilarToGenre { get; set; } + public string SimilarGenre { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventTitle.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventTitle.cs new file mode 100644 index 0000000..df2f444 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventTitle.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventTitle + { + public string Id { get; set; } + public YFeedEventTitleType Type { get; set; } + public string Name { get; set; } + public string Text { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventTitleType.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventTitleType.cs new file mode 100644 index 0000000..f124547 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventTitleType.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public enum YFeedEventTitleType + { + Artist, + Genre, + Text, + Track + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventTitled.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventTitled.cs new file mode 100644 index 0000000..fc510bd --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventTitled.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventTitled : YFeedEvent + { + public List Title { get; set; } + public YFeedEventType TypeForFrom { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventTracks.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventTracks.cs new file mode 100644 index 0000000..b259c59 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventTracks.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedEventTracks : YFeedEventTitled + { + public List Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedEventType.cs b/YandexMusic.API/Models/Feed/Event/YFeedEventType.cs new file mode 100644 index 0000000..b111c02 --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedEventType.cs @@ -0,0 +1,59 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Feed.Event +{ + public enum YFeedEventType + { + [EnumMember(Value = "genre-top")] + GenreTop, + + [EnumMember(Value = "missed-tracks-by-artist")] + MissedTracksByArtist, + + [EnumMember(Value = "never-heard-from-library")] + NeverHeardFromLibrary, + + [EnumMember(Value = "new-albums")] + NewAlbums, + + [EnumMember(Value = "new-albums-of-favorite-genre")] + NewAlbumsOfFavoriteGenre, + + [EnumMember(Value = "new-tracks-of-favorite-genre")] + NewTracksOfFavoriteGenre, + + Notification, + + Promotion, + + [EnumMember(Value = "rare-artist")] + RareArtist, + + [EnumMember(Value = "recent-track-like-to-tracks")] + RecentTrackLikeToTracks, + + [EnumMember(Value = "recommended-artists-with-artists-from-history")] + RecommendedArtistsWithArtistsFromHistory, + + [EnumMember(Value = "recommended-similar-artists")] + RecommendedSimilarArtists, + + [EnumMember(Value = "recommended-similar-genre")] + RecommendedSimilarGenre, + + [EnumMember(Value = "recommended-tracks-by-artist-from-history")] + RecommendedTracksByArtistFromHistory, + + [EnumMember(Value = "similar-tracks-from-history")] + SimilarTracksFromHistory, + + [EnumMember(Value = "tracks-by-genre")] + TracksByGenre, + + [EnumMember(Value = "well-forgotten-old-artists")] + WellForgottenOldArtists, + + [EnumMember(Value = "well-forgotten-old-tracks")] + WellForgottenOldTracks + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/Event/YFeedPromotion.cs b/YandexMusic.API/Models/Feed/Event/YFeedPromotion.cs new file mode 100644 index 0000000..13c95fe --- /dev/null +++ b/YandexMusic.API/Models/Feed/Event/YFeedPromotion.cs @@ -0,0 +1,24 @@ +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Feed.Event +{ + public class YFeedPromotion + { + public string PromoId { get; set; } + public string Description { get; set; } + public string Background { get; set; } + public string ImagePosition { get; set; } + public YFeedEventPromotionType PromotionType { get; set; } + public DateTime StartDate { get; set; } + public string SubTitle { get; set; } + public string SubtitleUrl { get; set; } + public string Title { get; set; } + public string TitleUrl { get; set; } + public List Tags { get; set; } + + public List Albums { get; set; } + public List Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/YFeed.cs b/YandexMusic.API/Models/Feed/YFeed.cs new file mode 100644 index 0000000..a39858d --- /dev/null +++ b/YandexMusic.API/Models/Feed/YFeed.cs @@ -0,0 +1,16 @@ +using YandexMusic.API.Models.Landing.Entity.Entities; + +namespace YandexMusic.API.Models.Feed +{ + public class YFeed + { + public DateTime NextRevision { get; set; } + public bool CanGetMoreEvents { get; set; } + public bool Pumpkin { get; set; } + public bool IsWizardPassed { get; set; } + public List Days { get; set; } + public List GeneratedPlaylists { get; set; } + public List Headlines { get; set; } + public DateTime Today { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/YFeedDay.cs b/YandexMusic.API/Models/Feed/YFeedDay.cs new file mode 100644 index 0000000..45c9cce --- /dev/null +++ b/YandexMusic.API/Models/Feed/YFeedDay.cs @@ -0,0 +1,14 @@ +using YandexMusic.API.Models.Feed.Event; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Feed +{ + public class YFeedDay + { + public DateTime Day { get; set; } + [JsonConverter(typeof(YFeedEventConverter))] + public List Events { get; set; } + public List TracksToPlay { get; set; } + public List TracksToPlayWithAds { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/YFeedDayTrackWithAds.cs b/YandexMusic.API/Models/Feed/YFeedDayTrackWithAds.cs new file mode 100644 index 0000000..4a8090a --- /dev/null +++ b/YandexMusic.API/Models/Feed/YFeedDayTrackWithAds.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Feed +{ + public class YFeedDayTrackWithAds + { + public YFeedDayTrackWithAdsType Type { get; set; } + public YTrack Track { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/YFeedDayTrackWithAdsType.cs b/YandexMusic.API/Models/Feed/YFeedDayTrackWithAdsType.cs new file mode 100644 index 0000000..0963411 --- /dev/null +++ b/YandexMusic.API/Models/Feed/YFeedDayTrackWithAdsType.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed +{ + public enum YFeedDayTrackWithAdsType + { + Track + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/YHeadline.cs b/YandexMusic.API/Models/Feed/YHeadline.cs new file mode 100644 index 0000000..3f0dcb9 --- /dev/null +++ b/YandexMusic.API/Models/Feed/YHeadline.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Feed +{ + public class YHeadline + { + public string Id { get; set; } + public string Message { get; set; } + public YHeadlineType Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Feed/YHeadlineType.cs b/YandexMusic.API/Models/Feed/YHeadlineType.cs new file mode 100644 index 0000000..0c03b00 --- /dev/null +++ b/YandexMusic.API/Models/Feed/YHeadlineType.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Feed +{ + public enum YHeadlineType + { + Notification + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Label/YLabelAlbums.cs b/YandexMusic.API/Models/Label/YLabelAlbums.cs new file mode 100644 index 0000000..00aec45 --- /dev/null +++ b/YandexMusic.API/Models/Label/YLabelAlbums.cs @@ -0,0 +1,11 @@ +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Label +{ + public class YLabelAlbums + { + public YPager Pager { get; set; } + public List Albums { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Label/YLabelArtists.cs b/YandexMusic.API/Models/Label/YLabelArtists.cs new file mode 100644 index 0000000..b6f958f --- /dev/null +++ b/YandexMusic.API/Models/Label/YLabelArtists.cs @@ -0,0 +1,11 @@ +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Label +{ + public class YLabelArtists + { + public YPager Pager { get; set; } + public List Artists { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContext.cs b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContext.cs new file mode 100644 index 0000000..be31d5a --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContext.cs @@ -0,0 +1,58 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities.Context +{ + public sealed class YPlayContextConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(YLandingEntity).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + JObject jObject = JObject.Load(reader); + + YPlayContext context; + try + { + YPlayContextType type = jObject["context"].ToObject(); + + switch (type) + { + case YPlayContextType.Album: + context = jObject.ToObject(); + break; + case YPlayContextType.Artist: + context = jObject.ToObject(); + break; + case YPlayContextType.Playlist: + context = jObject.ToObject(); + break; + default: + context = jObject.ToObject(); + break; + } + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации типа \"{jObject["type"]}\".", ex); + } + + return context; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + public class YPlayContext + { + public string Client { get; set; } + public YPlayContextType Context { get; set; } + public string ContextItem { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextAlbum.cs b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextAlbum.cs new file mode 100644 index 0000000..a0fb1f1 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextAlbum.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Models.Landing.Entity.Entities.Context +{ + public class YPlayContextAlbum : YPlayContext + { + public YAlbum Payload { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextArtist.cs b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextArtist.cs new file mode 100644 index 0000000..24657dd --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextArtist.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Landing.Entity.Entities.Context +{ + public class YPlayContextArtist : YPlayContext + { + public YArtist Payload { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextPlaylist.cs b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextPlaylist.cs new file mode 100644 index 0000000..3ae9d26 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextPlaylist.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Playlist; + +namespace YandexMusic.API.Models.Landing.Entity.Entities.Context +{ + public class YPlayContextPlaylist : YPlayContext + { + public YPlaylist Payload { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextType.cs b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextType.cs new file mode 100644 index 0000000..6b4f332 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/Context/YPlayContextType.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities.Context +{ + public enum YPlayContextType + { + Album, + Artist, + Playlist + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YAlbumMenuItem.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YAlbumMenuItem.cs new file mode 100644 index 0000000..a843ff2 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YAlbumMenuItem.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YAlbumMenuItem : YBlockEntity + { + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YBlockEntity.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YBlockEntity.cs new file mode 100644 index 0000000..9d9ee4c --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YBlockEntity.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YBlockEntity + { + public string BlockEntityDataId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YCategory.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YCategory.cs new file mode 100644 index 0000000..e1ac974 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YCategory.cs @@ -0,0 +1,16 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YCategory : YBlockEntity + { + public string BackgroundImageUri { get; set; } + public string CategoryId { get; set; } + public bool HasBackgroundImageText { get; set; } + public string Url { get; set; } + public string UrlScheme { get; set; } + public string VoiceTitle { get; set; } + public string TextColor { get; set; } + public string TextBackgroundColor { get; set; } + public string Title { get; set; } + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YChartItem.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YChartItem.cs new file mode 100644 index 0000000..0ed85d0 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YChartItem.cs @@ -0,0 +1,11 @@ +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YChartItem + { + public YTrack Track { get; set; } + public YChart Chart { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YChartProgress.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YChartProgress.cs new file mode 100644 index 0000000..f5c768d --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YChartProgress.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public enum YChartProgress + { + New, + Up, + Down, + Same + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YClientWidget.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YClientWidget.cs new file mode 100644 index 0000000..c5b5fc4 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YClientWidget.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YClientWidget : YBlockEntity + { + public string ImagePath { get; set; } + public string Subtitle { get; set; } + public string Text { get; set; } + public string Title { get; set; } + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbum.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbum.cs new file mode 100644 index 0000000..bcc970b --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbum.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityAlbum : YLandingEntity + { + public YAlbum Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbumMenuItem.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbumMenuItem.cs new file mode 100644 index 0000000..741da5d --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityAlbumMenuItem.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityAlbumMenuItem : YLandingEntity + { + public YAlbumMenuItem Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityCategory.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityCategory.cs new file mode 100644 index 0000000..8d0752f --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityCategory.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityCategory : YLandingEntity + { + public YCategory Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityChart.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityChart.cs new file mode 100644 index 0000000..ff31d7e --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityChart.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityChart : YLandingEntity + { + public YChartItem Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityClientWidget.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityClientWidget.cs new file mode 100644 index 0000000..a9491b9 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityClientWidget.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityClientWidget : YLandingEntity + { + public YClientWidget Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPersonalPlaylist.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPersonalPlaylist.cs new file mode 100644 index 0000000..2b6aed2 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPersonalPlaylist.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityPersonalPlaylist : YLandingEntity + { + public YPersonalPlaylist Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlayContext.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlayContext.cs new file mode 100644 index 0000000..c2db5dd --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlayContext.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Landing.Entity.Entities.Context; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityPlayContext : YLandingEntity + { + [JsonConverter(typeof(YPlayContextConverter))] + public YPlayContext Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlaylist.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlaylist.cs new file mode 100644 index 0000000..9e7cc81 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPlaylist.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Playlist; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityPlaylist : YLandingEntity + { + public YPlaylist Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPodcast.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPodcast.cs new file mode 100644 index 0000000..6573655 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPodcast.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityPodcast : YLandingEntity + { + public string Description { get; set; } + public string DescriptionFormatted { get; set; } + public DateTime LastUpdated { get; set; } + public YPodcast Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPromotion.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPromotion.cs new file mode 100644 index 0000000..984b634 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityPromotion.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityPromotion : YLandingEntity + { + public YPromotion Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityStation.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityStation.cs new file mode 100644 index 0000000..399d3cd --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingEntityStation.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingEntityStation : YLandingEntity + { + public YCategory Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YLandingStation.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingStation.cs new file mode 100644 index 0000000..6ac03bf --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YLandingStation.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Radio; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YLandingStation + { + public YStationId Id { get; set; } + public YStationDescription Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YPersonalPlaylist.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YPersonalPlaylist.cs new file mode 100644 index 0000000..fd28fe8 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YPersonalPlaylist.cs @@ -0,0 +1,14 @@ +using YandexMusic.API.Models.Playlist; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YPersonalPlaylist + { + public YPlaylist Data { get; set; } + public List Description { get; set; } + public bool Notify { get; set; } + public string PreviewDescription { get; set; } + public bool Ready { get; set; } + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YPodcast.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YPodcast.cs new file mode 100644 index 0000000..eb703fc --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YPodcast.cs @@ -0,0 +1,12 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YPodcast + { + public string Description { get; set; } + public string DescriptionFormatted { get; set; } + public DateTime LastUpdated { get; set; } + public YAlbum Podcast { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/Entities/YPromotion.cs b/YandexMusic.API/Models/Landing/Entity/Entities/YPromotion.cs new file mode 100644 index 0000000..974e2d0 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/Entities/YPromotion.cs @@ -0,0 +1,15 @@ +namespace YandexMusic.API.Models.Landing.Entity.Entities +{ + public class YPromotion + { + public string PromoId { get; set; } + public string Title { get; set; } + public string Subtitle { get; set; } + public string Heading { get; set; } + public string UrlScheme { get; set; } + public string Url { get; set; } + public string TextColor { get; set; } + public string Gradient { get; set; } + public string Image { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/YLandingEntity.cs b/YandexMusic.API/Models/Landing/Entity/YLandingEntity.cs new file mode 100644 index 0000000..f3caa16 --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/YLandingEntity.cs @@ -0,0 +1,90 @@ +using YandexMusic.API.Models.Landing.Entity.Entities; + +namespace YandexMusic.API.Models.Landing.Entity +{ + public sealed class YLandingEntityConverter : JsonConverter + { + private YLandingEntity GetEntity(JToken jObject) + { + YLandingEntity entity; + + try + { + YLandingEntityType type = jObject["type"].ToObject(); + + switch (type) + { + case YLandingEntityType.Album: + entity = jObject.ToObject(); + break; + case YLandingEntityType.ChartItem: + entity = jObject.ToObject(); + break; + case YLandingEntityType.PersonalPlaylist: + entity = jObject.ToObject(); + break; + case YLandingEntityType.PlayContext: + entity = jObject.ToObject(); + break; + case YLandingEntityType.Playlist: + entity = jObject.ToObject(); + break; + case YLandingEntityType.Podcast: + entity = jObject.ToObject(); + break; + case YLandingEntityType.Promotion: + entity = jObject.ToObject(); + break; + case YLandingEntityType.Category: + entity = jObject.ToObject(); + break; + case YLandingEntityType.Station: + entity = jObject.ToObject(); + break; + case YLandingEntityType.MenuItemAlbum: + case YLandingEntityType.MenuItemPlaylist: + entity = jObject.ToObject(); + break; + case YLandingEntityType.ClientWidget: + entity = jObject.ToObject(); + break; + default: + entity = jObject.ToObject(); + break; + } + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации типа \"{jObject["type"]}\".", ex); + } + + return entity; + } + + public override bool CanConvert(Type objectType) + { + return typeof(YLandingEntity).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + return JArray.Load(reader) + .Select(GetEntity) + .ToList(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + public abstract class YLandingEntity + { + public string Id { get; set; } + public YLandingEntityType Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/Entity/YLandingEntityType.cs b/YandexMusic.API/Models/Landing/Entity/YLandingEntityType.cs new file mode 100644 index 0000000..2aee6bf --- /dev/null +++ b/YandexMusic.API/Models/Landing/Entity/YLandingEntityType.cs @@ -0,0 +1,26 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Landing.Entity +{ + public enum YLandingEntityType + { + Album, + [EnumMember(Value = "chart-item")] + ChartItem, + [EnumMember(Value = "personal-playlist")] + PersonalPlaylist, + [EnumMember(Value = "play-context")] + PlayContext, + Playlist, + Podcast, + Promotion, + Category, + Station, + [EnumMember(Value = "menu-item-album")] + MenuItemAlbum, + [EnumMember(Value = "menu-item-playlist")] + MenuItemPlaylist, + [EnumMember(Value = "client-widget")] + ClientWidget + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YChildrenLanding.cs b/YandexMusic.API/Models/Landing/YChildrenLanding.cs new file mode 100644 index 0000000..7db7269 --- /dev/null +++ b/YandexMusic.API/Models/Landing/YChildrenLanding.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Landing +{ + public class YChildrenLanding + { + public string Title { get; set; } + public bool RupEnabled { get; set; } + public List Blocks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLanding.cs b/YandexMusic.API/Models/Landing/YLanding.cs new file mode 100644 index 0000000..2bce23d --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLanding.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Landing +{ + public class YLanding + { + public List Blocks { get; set; } + public string ContentId { get; set; } + public YLandingHeaderSpecialBlock HeaderSpecialBlock { get; set; } + public bool Pumpkin { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLandingBlock.cs b/YandexMusic.API/Models/Landing/YLandingBlock.cs new file mode 100644 index 0000000..03c3cd5 --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLandingBlock.cs @@ -0,0 +1,21 @@ +using YandexMusic.API.Models.Landing.Entity; + +namespace YandexMusic.API.Models.Landing +{ + public class YLandingBlock + { + public string Id { get; set; } + public string BackgroundImageUrl { get; set; } + public string BackgroundVideoUrl { get; set; } + public YLandingBlockData Data { get; set; } + public string Description { get; set; } + [JsonConverter(typeof(YLandingEntityConverter))] + public List Entities { get; set; } + public YLandingBlockPlayContext PlayContext { get; set; } + public string ViewAllUrl { get; set; } + public string viewAllUrlScheme { get; set; } + public string Title { get; set; } + public YLandingBlockType Type { get; set; } + public string TypeForFrom { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLandingBlockData.cs b/YandexMusic.API/Models/Landing/YLandingBlockData.cs new file mode 100644 index 0000000..b70521f --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLandingBlockData.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Landing +{ + public class YLandingBlockData + { + public bool IsWizardPassed { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLandingBlockType.cs b/YandexMusic.API/Models/Landing/YLandingBlockType.cs new file mode 100644 index 0000000..e91df59 --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLandingBlockType.cs @@ -0,0 +1,29 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Landing +{ + public enum YLandingBlockType + { + Chart, + [EnumMember(Value = "client-widget")] + ClientWidget, + [EnumMember(Value = "categories-tab")] + CategoriesTab, + [EnumMember(Value = "editorial-playlists")] + EditorialPlaylists, + Menu, + Mixes, + [EnumMember(Value = "new-releases")] + NewReleases, + [EnumMember(Value = "new-playlists")] + NewPlaylists, + [EnumMember(Value = "personal-playlists")] + PersonalPlaylists, + [EnumMember(Value = "play-contexts")] + PlayContexts, + Playlists, + Podcasts, + Promotions, + Radio + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLandingHeaderButton.cs b/YandexMusic.API/Models/Landing/YLandingHeaderButton.cs new file mode 100644 index 0000000..ff3f468 --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLandingHeaderButton.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Landing +{ + public class YLandingHeaderButton + { + public string Deeplink { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLandingHeaderSpecialBlock.cs b/YandexMusic.API/Models/Landing/YLandingHeaderSpecialBlock.cs new file mode 100644 index 0000000..fe1a204 --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLandingHeaderSpecialBlock.cs @@ -0,0 +1,13 @@ +namespace YandexMusic.API.Models.Landing +{ + public class YLandingHeaderSpecialBlock + { + public string AnimationUrl { get; set; } + public string BgImageUrl { get; set; } + public YLandingHeaderButton Button { get; set; } + public string DoodleImageUrl { get; set; } + public string EndGradientColor { get; set; } + public string StartGradientColor { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Landing/YLandingPlayContext.cs b/YandexMusic.API/Models/Landing/YLandingPlayContext.cs new file mode 100644 index 0000000..8e2c983 --- /dev/null +++ b/YandexMusic.API/Models/Landing/YLandingPlayContext.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Landing +{ + public class YLandingBlockPlayContext + { + public string Uid { get; set; } + public string Kind { get; set; } + public string PlaylistUuid { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibrary.cs b/YandexMusic.API/Models/Library/YLibrary.cs new file mode 100644 index 0000000..c70b05c --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibrary.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Library +{ + public class YLibrary + { + public string PlaylistUuid { get; set; } + public int Revision { get; set; } + public List Tracks { get; set; } + public string Uid { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibraryAlbum.cs b/YandexMusic.API/Models/Library/YLibraryAlbum.cs new file mode 100644 index 0000000..042466c --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibraryAlbum.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Library +{ + public class YLibraryAlbum + { + public string Id { get; set; } + public DateTime Timestamp { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibraryPlaylists.cs b/YandexMusic.API/Models/Library/YLibraryPlaylists.cs new file mode 100644 index 0000000..0856c20 --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibraryPlaylists.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Playlist; + +namespace YandexMusic.API.Models.Library +{ + public class YLibraryPlaylists + { + public YPlaylist Playlist { get; set; } + public DateTime Timestamp { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibrarySection.cs b/YandexMusic.API/Models/Library/YLibrarySection.cs new file mode 100644 index 0000000..4fe100f --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibrarySection.cs @@ -0,0 +1,28 @@ +namespace YandexMusic.API.Models.Library +{ + /// + /// Раздел библиотеки + /// + public enum YLibrarySection + { + /// + /// Альбомы + /// + Albums, + + /// + /// Исполнители + /// + Artists, + + /// + /// Плейлисты + /// + Playlists, + + /// + /// Треки + /// + Tracks + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibrarySectionType.cs b/YandexMusic.API/Models/Library/YLibrarySectionType.cs new file mode 100644 index 0000000..a8fdb63 --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibrarySectionType.cs @@ -0,0 +1,18 @@ +namespace YandexMusic.API.Models.Library +{ + /// + /// Раздел библиотеки + /// + public enum YLibrarySectionType + { + /// + /// Лайки + /// + Likes, + + /// + /// Дизлайки + /// + Dislikes + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibraryTrack.cs b/YandexMusic.API/Models/Library/YLibraryTrack.cs new file mode 100644 index 0000000..870c354 --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibraryTrack.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Library +{ + public class YLibraryTrack + { + public string AlbumId { get; set; } + public string Id { get; set; } + public DateTime Timestamp { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YLibraryTracks.cs b/YandexMusic.API/Models/Library/YLibraryTracks.cs new file mode 100644 index 0000000..8931028 --- /dev/null +++ b/YandexMusic.API/Models/Library/YLibraryTracks.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Library +{ + public class YLibraryTracks + { + public YLibrary Library { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YListenedTrack.cs b/YandexMusic.API/Models/Library/YListenedTrack.cs new file mode 100644 index 0000000..acf00a4 --- /dev/null +++ b/YandexMusic.API/Models/Library/YListenedTrack.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Library +{ + public class YListenedTrack + { + public YTrackId TrackId { get; set; } + public DateTime TimeStamp { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YRecentlyListened.cs b/YandexMusic.API/Models/Library/YRecentlyListened.cs new file mode 100644 index 0000000..1fe39a6 --- /dev/null +++ b/YandexMusic.API/Models/Library/YRecentlyListened.cs @@ -0,0 +1,12 @@ +using YandexMusic.API.Models.Landing.Entity.Entities.Context; + +namespace YandexMusic.API.Models.Library +{ + public class YRecentlyListened + { + public string Client { get; set; } + public YPlayContextType Context { get; set; } + public string ContextItem { get; set; } + public List Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Library/YRecentlyListenedContext.cs b/YandexMusic.API/Models/Library/YRecentlyListenedContext.cs new file mode 100644 index 0000000..17b44cc --- /dev/null +++ b/YandexMusic.API/Models/Library/YRecentlyListenedContext.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Library +{ + public class YRecentlyListenedContext + { + public List Contexts { get; set; } + public List OtherTracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/Items/YPin.cs b/YandexMusic.API/Models/Pins/Items/YPin.cs new file mode 100644 index 0000000..8cfec7e --- /dev/null +++ b/YandexMusic.API/Models/Pins/Items/YPin.cs @@ -0,0 +1,68 @@ +namespace YandexMusic.API.Models.Pins.Items +{ + public sealed class YPinConverter : JsonConverter + { + private YPin GetEvent(JToken jObject) + { + YPin pin; + + YPinType type = jObject["type"] + .ToObject(); + + switch (type) + { + case YPinType.Album: + pin = jObject.ToObject>(); + break; + + case YPinType.Artist: + pin = jObject.ToObject>(); + break; + + case YPinType.Playlist: + pin = jObject.ToObject>(); + break; + + case YPinType.Wave: + pin = jObject.ToObject>(); + break; + + default: + pin = jObject.ToObject(); + break; + } + + return pin; + } + + public override bool CanConvert(Type objectType) + { + return typeof(YPin).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + return JArray.Load(reader) + .Select(GetEvent) + .ToList(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + public abstract class YPin + { + public YPinType Type { get; set; } + } + + public class YPin : YPin + { + public T Data { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/Items/YPinAlbumData.cs b/YandexMusic.API/Models/Pins/Items/YPinAlbumData.cs new file mode 100644 index 0000000..410c7b3 --- /dev/null +++ b/YandexMusic.API/Models/Pins/Items/YPinAlbumData.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Pins.Items +{ + public class YPinAlbumData + { + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/Items/YPinArtistData.cs b/YandexMusic.API/Models/Pins/Items/YPinArtistData.cs new file mode 100644 index 0000000..2af8bd9 --- /dev/null +++ b/YandexMusic.API/Models/Pins/Items/YPinArtistData.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Pins.Items +{ + public class YPinArtistData + { + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/Items/YPinPlaylistData.cs b/YandexMusic.API/Models/Pins/Items/YPinPlaylistData.cs new file mode 100644 index 0000000..4dc1745 --- /dev/null +++ b/YandexMusic.API/Models/Pins/Items/YPinPlaylistData.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Pins.Items +{ + public class YPinPlaylistData + { + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/Items/YPinWaveData.cs b/YandexMusic.API/Models/Pins/Items/YPinWaveData.cs new file mode 100644 index 0000000..4e6864c --- /dev/null +++ b/YandexMusic.API/Models/Pins/Items/YPinWaveData.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Pins.Items +{ + public class YPinWaveData + { + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/YPinType.cs b/YandexMusic.API/Models/Pins/YPinType.cs new file mode 100644 index 0000000..0baa048 --- /dev/null +++ b/YandexMusic.API/Models/Pins/YPinType.cs @@ -0,0 +1,19 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Pins +{ + public enum YPinType + { + [EnumMember(Value = "album_item")] + Album, + + [EnumMember(Value = "artist_item")] + Artist, + + [EnumMember(Value = "playlist_item")] + Playlist, + + [EnumMember(Value = "wave_item")] + Wave + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Pins/YPins.cs b/YandexMusic.API/Models/Pins/YPins.cs new file mode 100644 index 0000000..a911d20 --- /dev/null +++ b/YandexMusic.API/Models/Pins/YPins.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Pins.Items; + +namespace YandexMusic.API.Models.Pins +{ + public class YPins + { + [JsonConverter(typeof(YPinConverter))] + public List Pins { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YArtistPlaylistType.cs b/YandexMusic.API/Models/Playlist/YArtistPlaylistType.cs new file mode 100644 index 0000000..13ab599 --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YArtistPlaylistType.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Playlist +{ + public enum YArtistPlaylistType + { + [EnumMember(Value = "TOP")] + Top, + [EnumMember(Value = "SIMILAR")] + Similar + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YGeneratedPlaylistType.cs b/YandexMusic.API/Models/Playlist/YGeneratedPlaylistType.cs new file mode 100644 index 0000000..779c7bf --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YGeneratedPlaylistType.cs @@ -0,0 +1,42 @@ +namespace YandexMusic.API.Models.Playlist +{ + public enum YGeneratedPlaylistType + { + None, + + /// + /// Редакторский список + /// + Editorial, + + /// + /// Плейлист дня + /// + PlaylistOfTheDay, + + /// + /// Премьера + /// + RecentTracks, + + /// + /// Дежавю + /// + NeverHeard, + + /// + /// Тайник + /// + MissedLikes, + + /// + /// Алиса + /// + Origin, + + /// + /// Кинопоиск + /// + Kinopoisk + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YPlaylist.cs b/YandexMusic.API/Models/Playlist/YPlaylist.cs new file mode 100644 index 0000000..b30c232 --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YPlaylist.cs @@ -0,0 +1,82 @@ +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Common.Cover; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Playlist +{ + public class YPlaylist : YBaseModel + { + #region Поля + + public YPlaylistUidPair GetKey() + { + return new YPlaylistUidPair + { + Uid = Owner.Uid, + Kind = Kind + }; + } + + #endregion Поля + + #region Свойства + + public YButton ActionButton { get; set; } + public string AnimatedCoverUri { get; set; } + public bool Available { get; set; } + public YArtistPlaylistType ArtistPlaylistType { get; set; } + public string BackgroundColor { get; set; } + public string BackgroundImageUrl { get; set; } + public string BackgroundVideoUrl { get; set; } + public bool Collective { get; set; } + [JsonConverter(typeof(YCoverConverter))] + public YCover Cover { get; set; } + [JsonConverter(typeof(YCoverConverter))] + public YCover CoverWithoutText { get; set; } + public YCustomWave CustomWave { get; set; } + public List RecentTracks { get; set; } + public DateTime Created { get; set; } + public YDerivedColors DerivedColors { get; set; } + public string Description { get; set; } + public string DescriptionFormatted { get; set; } + public bool DoNotIndex { get; set; } + public long DurationMs { get; set; } + public bool EverPlayed { get; set; } + public string GeneratedPlaylistType { get; set; } + public bool HasTrailer { get; set; } + public string IdForFrom { get; set; } + public string Image { get; set; } + public bool IsBanner { get; set; } + public bool IsPremiere { get; set; } + public string Kind { get; set; } + public List LastOwnerPlaylists { get; set; } + public int LikesCount { get; set; } + public YPlaylistMadeFor MadeFor { get; set; } + public string MetrikaId { get; set; } + public string Modified { get; set; } + public string OgImage { get; set; } + public string OgTitle { get; set; } + public string OgDescription { get; set; } + public YOwner Owner { get; set; } + public YPager Pager { get; set; } + public decimal PersonalColor { get; set; } + public YPlaylistPlayCounter PlayCounter { get; set; } + public string PlaylistUuid { get; set; } + public List Prerolls { get; set; } + public int Revision { get; set; } + public List SimilarPlaylists { get; set; } + public int Snapshot { get; set; } + public List Tags { get; set; } + public string TextColor { get; set; } + public string Title { get; set; } + public int TrackCount { get; set; } + public List TrackIds { get; set; } + public List Tracks { get; set; } + public YTrailer Trailer { get; set; } + public string Uid { get; set; } + public string UrlPart { get; set; } + public string Visibility { get; set; } + + #endregion Свойства + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YPlaylistChange.cs b/YandexMusic.API/Models/Playlist/YPlaylistChange.cs new file mode 100644 index 0000000..50452ce --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YPlaylistChange.cs @@ -0,0 +1,14 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Playlist +{ + public class YPlaylistChange + { + public int? At { get; set; } + public int? From { get; set; } + [JsonProperty("op")] + public YPlaylistChangeType Operation { get; set; } + public int? To { get; set; } + public IEnumerable Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YPlaylistChangeType.cs b/YandexMusic.API/Models/Playlist/YPlaylistChangeType.cs new file mode 100644 index 0000000..f161bd4 --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YPlaylistChangeType.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Playlist +{ + public enum YPlaylistChangeType + { + Insert, + Delete + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YPlaylistMadeFor.cs b/YandexMusic.API/Models/Playlist/YPlaylistMadeFor.cs new file mode 100644 index 0000000..2c838eb --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YPlaylistMadeFor.cs @@ -0,0 +1,28 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Playlist +{ + public class YPlaylistMadeFor + { + public class YMadeForCaseForms + { + #region Свойства + + public string Accusative { get; set; } + public string Dative { get; set; } + public string Genitive { get; set; } + public string Instrumental { get; set; } + public string Nominative { get; set; } + public string Prepositional { get; set; } + + #endregion Свойства + } + + #region Свойства + + public YMadeForCaseForms CaseForms { get; set; } + public YOwner UserInfo { get; set; } + + #endregion Свойства + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YPlaylistPlayCounter.cs b/YandexMusic.API/Models/Playlist/YPlaylistPlayCounter.cs new file mode 100644 index 0000000..eef5b39 --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YPlaylistPlayCounter.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Playlist +{ + public class YPlaylistPlayCounter + { + public string Description { get; set; } + public string DescriptionNext { get; set; } + public bool Updated { get; set; } + public int? Value { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Playlist/YPlaylistUidPair.cs b/YandexMusic.API/Models/Playlist/YPlaylistUidPair.cs new file mode 100644 index 0000000..f195fdc --- /dev/null +++ b/YandexMusic.API/Models/Playlist/YPlaylistUidPair.cs @@ -0,0 +1,13 @@ +namespace YandexMusic.API.Models.Playlist +{ + public class YPlaylistUidPair + { + public string Kind { get; set; } + public string Uid { get; set; } + + public override string ToString() + { + return $"{Uid}:{Kind}"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Queue/YContext.cs b/YandexMusic.API/Models/Queue/YContext.cs new file mode 100644 index 0000000..e7ddc8f --- /dev/null +++ b/YandexMusic.API/Models/Queue/YContext.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Queue +{ + public class YContext + { + public string Type { get; set; } + public string Id { get; set; } + public string Login { get; set; } + public string Description { get; set; } + } +} diff --git a/YandexMusic.API/Models/Queue/YNewQueue.cs b/YandexMusic.API/Models/Queue/YNewQueue.cs new file mode 100644 index 0000000..c76466c --- /dev/null +++ b/YandexMusic.API/Models/Queue/YNewQueue.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Queue +{ + public class YNewQueue : YId + { + public string Modified { get; set; } + } +} diff --git a/YandexMusic.API/Models/Queue/YQueue.cs b/YandexMusic.API/Models/Queue/YQueue.cs new file mode 100644 index 0000000..c8bb27c --- /dev/null +++ b/YandexMusic.API/Models/Queue/YQueue.cs @@ -0,0 +1,15 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Queue +{ + public class YQueue + { + public string Id { get; set; } + public YContext Context { get; set; } + public List Tracks { get; set; } + public int? CurrentIndex { get; set; } + public string Modified { get; set; } + public string From { get; set; } + public bool IsInteractive { get; set; } + } +} diff --git a/YandexMusic.API/Models/Queue/YQueueItem.cs b/YandexMusic.API/Models/Queue/YQueueItem.cs new file mode 100644 index 0000000..6a27b32 --- /dev/null +++ b/YandexMusic.API/Models/Queue/YQueueItem.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Queue +{ + public class YQueueItem + { + public string Id { get; set; } + public YContext Context { get; set; } + public YContext InitialContext { get; set; } + public string Modified { get; set; } + } +} diff --git a/YandexMusic.API/Models/Queue/YQueueItemsContainer.cs b/YandexMusic.API/Models/Queue/YQueueItemsContainer.cs new file mode 100644 index 0000000..98c51cf --- /dev/null +++ b/YandexMusic.API/Models/Queue/YQueueItemsContainer.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Queue +{ + public class YQueueItemsContainer + { + public List Queues { get; set; } + } +} diff --git a/YandexMusic.API/Models/Queue/YUpdatedQueue.cs b/YandexMusic.API/Models/Queue/YUpdatedQueue.cs new file mode 100644 index 0000000..1b8482a --- /dev/null +++ b/YandexMusic.API/Models/Queue/YUpdatedQueue.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Queue +{ + public class YUpdatedQueue + { + public string Status { get; set; } + public bool MostRecentQueue { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/Restriction/YRestriction.cs b/YandexMusic.API/Models/Radio/Restriction/YRestriction.cs new file mode 100644 index 0000000..fc2ab84 --- /dev/null +++ b/YandexMusic.API/Models/Radio/Restriction/YRestriction.cs @@ -0,0 +1,54 @@ +namespace YandexMusic.API.Models.Radio.Restriction +{ + public sealed class YRestrictionConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(YRestriction).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + JObject jObject = JObject.Load(reader); + YRestriction restriction; + + try + { + YRestrictionType type = jObject["type"].ToObject(); + + switch (type) + { + case YRestrictionType.Enum: + restriction = jObject.ToObject(); + break; + case YRestrictionType.DiscreteScale: + restriction = jObject.ToObject(); + break; + default: + restriction = jObject.ToObject(); + break; + } + } + catch (Exception ex) + { + throw new Exception($"Ошибка десериализации типа \"{objectType.Name}\".", ex); + } + + return restriction; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + public class YRestriction + { + public string Name { get; set; } + public YRestrictionType Type { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/Restriction/YRestrictionDiscreteScale.cs b/YandexMusic.API/Models/Radio/Restriction/YRestrictionDiscreteScale.cs new file mode 100644 index 0000000..bd9cbcd --- /dev/null +++ b/YandexMusic.API/Models/Radio/Restriction/YRestrictionDiscreteScale.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Radio.Restriction +{ + public class YRestrictionDiscreteScale : YRestriction + { + public YRestrictionValue Min { get; set; } + public YRestrictionValue Max { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/Restriction/YRestrictionEnum.cs b/YandexMusic.API/Models/Radio/Restriction/YRestrictionEnum.cs new file mode 100644 index 0000000..fcc7fd7 --- /dev/null +++ b/YandexMusic.API/Models/Radio/Restriction/YRestrictionEnum.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Radio.Restriction +{ + public class YRestrictionEnum : YRestriction + { + public List> PossibleValues { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/Restriction/YRestrictionType.cs b/YandexMusic.API/Models/Radio/Restriction/YRestrictionType.cs new file mode 100644 index 0000000..db69964 --- /dev/null +++ b/YandexMusic.API/Models/Radio/Restriction/YRestrictionType.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Radio.Restriction +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum YRestrictionType + { + [EnumMember(Value = "discrete-scale")] + DiscreteScale, + Enum + } +} diff --git a/YandexMusic.API/Models/Radio/Restriction/YRestrictionValue.cs b/YandexMusic.API/Models/Radio/Restriction/YRestrictionValue.cs new file mode 100644 index 0000000..2d62671 --- /dev/null +++ b/YandexMusic.API/Models/Radio/Restriction/YRestrictionValue.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Radio.Restriction +{ + public class YRestrictionValue + { + public string Name { get; set; } + public T Value { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/YAdParams.cs b/YandexMusic.API/Models/Radio/YAdParams.cs new file mode 100644 index 0000000..b1abf40 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YAdParams.cs @@ -0,0 +1,14 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YAdParams + { + public decimal AdVolume { get; set; } + public string CategoryId { get; set; } + public int GenreId { get; set; } + public string GenreName { get; set; } + public string OtherParams { get; set; } + public string PageRef { get; set; } + public string PartnerId { get; set; } + public string TargetRef { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YBrand.cs b/YandexMusic.API/Models/Radio/YBrand.cs new file mode 100644 index 0000000..90035f8 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YBrand.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YBrand + { + public string AdvertiserUrl { get; set; } + public string BackgroundColor { get; set; } + public string BackgroundImageUri { get; set; } + public string GlowColor { get; set; } + public string MainImageUri { get; set; } + public string Theme { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YSequenceItem.cs b/YandexMusic.API/Models/Radio/YSequenceItem.cs new file mode 100644 index 0000000..6c003d4 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YSequenceItem.cs @@ -0,0 +1,12 @@ +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Radio +{ + public class YSequenceItem + { + public bool Liked { get; set; } + public YTrack Track { get; set; } + public YTrackParameters TrackParameters { get; set; } + public string Type { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/YStation.cs b/YandexMusic.API/Models/Radio/YStation.cs new file mode 100644 index 0000000..c43afdd --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStation.cs @@ -0,0 +1,18 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Radio +{ + public class YStation : YBaseModel + { + public YAdParams AdParams { get; set; } + public string CustomName { get; set; } + public YStationData Data { get; set; } + public string Explanation { get; set; } + public List Prerolls { get; set; } + public string RupTitle { get; set; } + public string RupDescription { get; set; } + public YStationSettings Settings { get; set; } + public YStationSettings2 Settings2 { get; set; } + public YStationDescription Station { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationData.cs b/YandexMusic.API/Models/Radio/YStationData.cs new file mode 100644 index 0000000..c694373 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationData.cs @@ -0,0 +1,13 @@ +using YandexMusic.API.Models.Artist; + +namespace YandexMusic.API.Models.Radio +{ + public class YStationData + { + public List Artists { get; set; } + public YBrand Brand { get; set; } + public string Description { get; set; } + public string ImageUri { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationDescription.cs b/YandexMusic.API/Models/Radio/YStationDescription.cs new file mode 100644 index 0000000..7deb637 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationDescription.cs @@ -0,0 +1,17 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationDescription + { + public string FullImageUrl { get; set; } + public YStationIcon GeocellIcon { get; set; } + public YStationIcon Icon { get; set; } + public YStationId Id { get; set; } + public string IdForFrom { get; set; } + public string MtsFullImageUrl { get; set; } + public YStationIcon MtsIcon { get; set; } + public string Name { get; set; } + public YStationId ParentId { get; set; } + public YStationRestrictions Restrictions { get; set; } + public YStationRestrictions2 Restrictions2 { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationFeedback.cs b/YandexMusic.API/Models/Radio/YStationFeedback.cs new file mode 100644 index 0000000..267e581 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationFeedback.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Radio +{ + internal sealed class YStationFeedback + { + public YStationFeedbackType Type { get; set; } + public long Timestamp { get; set; } + public string From { get; set; } + public double TotalPlayedSeconds { get; set; } + public string TrackId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationFeedbackType.cs b/YandexMusic.API/Models/Radio/YStationFeedbackType.cs new file mode 100644 index 0000000..6264f08 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationFeedbackType.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Radio +{ + public enum YStationFeedbackType + { + RadioStarted, + TrackStarted, + TrackFinished, + Skip + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationIcon.cs b/YandexMusic.API/Models/Radio/YStationIcon.cs new file mode 100644 index 0000000..5c381da --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationIcon.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationIcon + { + public string BackgroundColor { get; set; } + public string ImageUrl { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/YStationId.cs b/YandexMusic.API/Models/Radio/YStationId.cs new file mode 100644 index 0000000..9646dfe --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationId.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationId + { + public string Tag { get; set; } + public string Type { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/YStationRestrictions.cs b/YandexMusic.API/Models/Radio/YStationRestrictions.cs new file mode 100644 index 0000000..b0cafd4 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationRestrictions.cs @@ -0,0 +1,16 @@ +using YandexMusic.API.Models.Radio.Restriction; + +namespace YandexMusic.API.Models.Radio +{ + public class YStationRestrictions + { + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction Diversity { get; set; } + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction Energy { get; set; } + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction Language { get; set; } + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction Mood { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationRestrictions2.cs b/YandexMusic.API/Models/Radio/YStationRestrictions2.cs new file mode 100644 index 0000000..d3dd9fb --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationRestrictions2.cs @@ -0,0 +1,14 @@ +using YandexMusic.API.Models.Radio.Restriction; + +namespace YandexMusic.API.Models.Radio +{ + public class YStationRestrictions2 + { + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction Diversity { get; set; } + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction Language { get; set; } + [JsonConverter(typeof(YRestrictionConverter))] + public YRestriction MoodEnergy { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationSequence.cs b/YandexMusic.API/Models/Radio/YStationSequence.cs new file mode 100644 index 0000000..eeaffff --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationSequence.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationSequence + { + public string BatchId { get; set; } + public YStationId Id { get; set; } + public bool Pumpkin { get; set; } + public string RadioSessionId { get; set; } + public List Sequence { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationSettings.cs b/YandexMusic.API/Models/Radio/YStationSettings.cs new file mode 100644 index 0000000..e52eaba --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationSettings.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationSettings + { + public string Diversity { get; set; } + public string Energy { get; set; } + public string Language { get; set; } + public string Mood { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationSettings2.cs b/YandexMusic.API/Models/Radio/YStationSettings2.cs new file mode 100644 index 0000000..e5fad02 --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationSettings2.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationSettings2 + { + public string Diversity { get; set; } + public string Language { get; set; } + public string MoodEnergy { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Radio/YStationsDashboard.cs b/YandexMusic.API/Models/Radio/YStationsDashboard.cs new file mode 100644 index 0000000..f9b7f6d --- /dev/null +++ b/YandexMusic.API/Models/Radio/YStationsDashboard.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YStationsDashboard + { + public string DashboardId { get; set; } + public bool Pumpkin { get; set; } + public List Stations { get; set; } + } +} diff --git a/YandexMusic.API/Models/Radio/YTrackParameters.cs b/YandexMusic.API/Models/Radio/YTrackParameters.cs new file mode 100644 index 0000000..e7ca49c --- /dev/null +++ b/YandexMusic.API/Models/Radio/YTrackParameters.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Radio +{ + public class YTrackParameters + { + public int Bpm { get; set; } + public int Hue { get; set; } + public decimal Energy { get; set; } + } +} diff --git a/YandexMusic.API/Models/Search/Album/YSearchAlbumModel.cs b/YandexMusic.API/Models/Search/Album/YSearchAlbumModel.cs new file mode 100644 index 0000000..e98ee42 --- /dev/null +++ b/YandexMusic.API/Models/Search/Album/YSearchAlbumModel.cs @@ -0,0 +1,11 @@ +using YandexMusic.API.Models.Album; + +namespace YandexMusic.API.Models.Search.Album +{ + public class YSearchAlbumModel : YAlbum + { + public List AvailableRegions { get; set; } + public int OriginalReleaseYear { get; set; } + public List Regions { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Artist/YSearchArtist.cs b/YandexMusic.API/Models/Search/Artist/YSearchArtist.cs new file mode 100644 index 0000000..a8d6980 --- /dev/null +++ b/YandexMusic.API/Models/Search/Artist/YSearchArtist.cs @@ -0,0 +1,15 @@ +using YandexMusic.API.Models.Common.Cover; + +namespace YandexMusic.API.Models.Search.Artist +{ + public class YSearchArtist + { + public bool Composer { get; set; } + [JsonConverter(typeof(YCoverConverter))] + public YCover Cover { get; set; } + public List Decomposed { get; set; } + public string Id { get; set; } + public string Name { get; set; } + public bool Various { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Artist/YSearchArtistCounter.cs b/YandexMusic.API/Models/Search/Artist/YSearchArtistCounter.cs new file mode 100644 index 0000000..d94de1b --- /dev/null +++ b/YandexMusic.API/Models/Search/Artist/YSearchArtistCounter.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Search.Artist +{ + public class YSearchArtistCounter + { + public int? AlsoAlbums { get; set; } + public int? AlsoTracks { get; set; } + public int? DirectAlbums { get; set; } + public int? Tracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Artist/YSearchArtistModel.cs b/YandexMusic.API/Models/Search/Artist/YSearchArtistModel.cs new file mode 100644 index 0000000..2ec7b5d --- /dev/null +++ b/YandexMusic.API/Models/Search/Artist/YSearchArtistModel.cs @@ -0,0 +1,11 @@ +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Search.Track; + +namespace YandexMusic.API.Models.Search.Artist +{ + public class YSearchArtistModel : YArtist + { + public List PopularTracks { get; set; } + public List Regions { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Playlist/YSearchPlaylistModel.cs b/YandexMusic.API/Models/Search/Playlist/YSearchPlaylistModel.cs new file mode 100644 index 0000000..821c846 --- /dev/null +++ b/YandexMusic.API/Models/Search/Playlist/YSearchPlaylistModel.cs @@ -0,0 +1,10 @@ +using YandexMusic.API.Models.Playlist; + +namespace YandexMusic.API.Models.Search.Playlist +{ + public class YSearchPlaylistModel : YPlaylist + { + public string CoverUri { get; set; } + public List Regions { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Track/YSearchTrackModel.cs b/YandexMusic.API/Models/Search/Track/YSearchTrackModel.cs new file mode 100644 index 0000000..280199e --- /dev/null +++ b/YandexMusic.API/Models/Search/Track/YSearchTrackModel.cs @@ -0,0 +1,13 @@ +using YandexMusic.API.Models.Search.Album; +using YandexMusic.API.Models.Track; + +namespace YandexMusic.API.Models.Search.Track +{ + public class YSearchTrackModel : YTrack + { + public new List Albums { get; set; } + public bool AvailableAsRbt { get; set; } + public bool Explicit { get; set; } + public List Regions { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Track/YSearchTrackType.cs b/YandexMusic.API/Models/Search/Track/YSearchTrackType.cs new file mode 100644 index 0000000..4c9f798 --- /dev/null +++ b/YandexMusic.API/Models/Search/Track/YSearchTrackType.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Search.Track +{ + public enum YSearchTrackType + { + Music + } +} diff --git a/YandexMusic.API/Models/Search/User/YSearchUserModel.cs b/YandexMusic.API/Models/Search/User/YSearchUserModel.cs new file mode 100644 index 0000000..668efd1 --- /dev/null +++ b/YandexMusic.API/Models/Search/User/YSearchUserModel.cs @@ -0,0 +1,6 @@ +namespace YandexMusic.API.Models.Search.User +{ + public class YSearchUserModel + { + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/Video/YSearchVideoModel.cs b/YandexMusic.API/Models/Search/Video/YSearchVideoModel.cs new file mode 100644 index 0000000..ce0d0c3 --- /dev/null +++ b/YandexMusic.API/Models/Search/Video/YSearchVideoModel.cs @@ -0,0 +1,13 @@ +namespace YandexMusic.API.Models.Search.Video +{ + public class YSearchVideoModel + { + public int Duration { get; set; } + public string HtmlAutoPlayVideoPlayer { get; set; } + public List Regions { get; set; } + public string Text { get; set; } + public string ThumbnailUrl { get; set; } + public string Title { get; set; } + public string YoutubeUrl { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/YSearch.cs b/YandexMusic.API/Models/Search/YSearch.cs new file mode 100644 index 0000000..79b560e --- /dev/null +++ b/YandexMusic.API/Models/Search/YSearch.cs @@ -0,0 +1,35 @@ +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Search.Album; +using YandexMusic.API.Models.Search.Artist; +using YandexMusic.API.Models.Search.Playlist; +using YandexMusic.API.Models.Search.Track; +using YandexMusic.API.Models.Search.User; +using YandexMusic.API.Models.Search.Video; + +namespace YandexMusic.API.Models.Search +{ + public class YSearch + { + public YSearchResult Albums { get; set; } + public YSearchResult Artists { get; set; } + + public YSearchBest Best { get; set; } + + public bool MisspellCorrected { get; set; } + public string MisspellOriginal { get; set; } + public string MisspellResult { get; set; } + public bool NoCorrect { get; set; } + public int Page { get; set; } + public int PerPage { get; set; } + public YSearchResult Playlists { get; set; } + [JsonProperty("podcast_episodes")] + public YSearchResult PodcastEpisode { get; set; } + public string SearchRequestId { get; set; } + public string Text { get; set; } + public YSearchResult Tracks { get; set; } + + public YSearchType Type { get; set; } + public YSearchResult Users { get; set; } + public YSearchResult Videos { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/YSearchBest.cs b/YandexMusic.API/Models/Search/YSearchBest.cs new file mode 100644 index 0000000..65eb083 --- /dev/null +++ b/YandexMusic.API/Models/Search/YSearchBest.cs @@ -0,0 +1,70 @@ +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Search.Album; +using YandexMusic.API.Models.Search.Artist; +using YandexMusic.API.Models.Search.Playlist; +using YandexMusic.API.Models.Search.Track; +using YandexMusic.API.Models.Search.Video; + +namespace YandexMusic.API.Models.Search +{ + /// + /// Конвертер для поля Result + /// + internal class YSearchBestConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + JObject obj = JObject.Load(reader); + JsonObjectContract contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); + YSearchBest best = existingValue as YSearchBest ?? (YSearchBest)contract.DefaultCreator(); + + best.Type = (YSearchType)Enum.Parse(typeof(YSearchType), obj["type"].ToString(), true); + + switch (best.Type) + { + case YSearchType.Track: + best.Result = JsonConvert.DeserializeObject(obj["result"].ToString()); + break; + case YSearchType.Album: + best.Result = JsonConvert.DeserializeObject(obj["result"].ToString()); + break; + case YSearchType.Artist: + best.Result = JsonConvert.DeserializeObject(obj["result"].ToString()); + break; + case YSearchType.Playlist: + best.Result = JsonConvert.DeserializeObject(obj["result"].ToString()); + break; + case YSearchType.PodcastEpisode: + best.Result = JsonConvert.DeserializeObject(obj["result"].ToString()); + break; + case YSearchType.Video: + best.Result = JsonConvert.DeserializeObject(obj["result"].ToString()); + break; + } + + return best; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override bool CanWrite => false; + } + + [JsonConverter(typeof(YSearchBestConverter))] + public class YSearchBest + { + public dynamic Result { get; set; } + public YSearchType Type { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/YSearchResult.cs b/YandexMusic.API/Models/Search/YSearchResult.cs new file mode 100644 index 0000000..fc76e46 --- /dev/null +++ b/YandexMusic.API/Models/Search/YSearchResult.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Search +{ + public class YSearchResult + { + public int Order { get; set; } + public int PerPage { get; set; } + public List Results { get; set; } + public int Total { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Search/YSearchSuggest.cs b/YandexMusic.API/Models/Search/YSearchSuggest.cs new file mode 100644 index 0000000..469f92a --- /dev/null +++ b/YandexMusic.API/Models/Search/YSearchSuggest.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Search +{ + public class YSearchSuggest + { + public YSearchBest Best { get; set; } + public List Suggestions { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrack.cs b/YandexMusic.API/Models/Track/YTrack.cs new file mode 100644 index 0000000..d9a6bab --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrack.cs @@ -0,0 +1,84 @@ +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Track +{ + public class YTrack : YBaseModel, IEquatable + { + public List Albums { get; set; } + public List Artists { get; set; } + public bool Available { get; set; } + public bool AvailableForPremiumUsers { get; set; } + public bool AvailableFullWithoutPermission { get; set; } + public List AvailableForOptions { get; set; } + public string BackgroundVideoUri { get; set; } + public bool Best { get; set; } + public YChart Chart { get; set; } + public string ContentWarning { get; set; } + public string CoverUri { get; set; } + public List ClipIds { get; set; } + public YDerivedColors DerivedColors { get; set; } + public List Disclaimers { get; set; } + public long DurationMs { get; set; } + public string Error { get; set; } + public YTrackFade Fade { get; set; } + public long FileSize { get; set; } + public string Id { get; set; } + public bool IsSuitableForChildren { get; set; } + public YMajor Major { get; set; } + public YTrackNormalization Normalization { get; set; } + public YTrackNormalizationR128 R128 { get; set; } + public string OgImage { get; set; } + public bool LyricsAvailable { get; set; } + public YLyricsInfo LyricsInfo { get; set; } + public string PlayerId { get; set; } + public long PreviewDurationMs { get; set; } + public YPodcastEpisodeType PodcastEpisodeType { get; set; } + public DateTime PubDate { get; set; } + public string RealId { get; set; } + public bool RememberPosition { get; set; } + public string ShortDescription { get; set; } + public List SpecialAudioResources { get; set; } + public string StorageDir { get; set; } + public YTrack Substituted { get; set; } + public string Title { get; set; } + public YTrackSharingFlag TrackSharingFlag { get; set; } + public YTrackSource TrackSource { get; set; } + public string Type { get; set; } + public string Version { get; set; } + + public YTrackAlbumPair GetKey() + { + return new YTrackAlbumPair + { + Id = Id, + AlbumId = Albums?.FirstOrDefault()?.Id + }; + } + + #region IEquatable + + public bool Equals(YTrack other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return GetKey().Equals(other.GetKey()); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((YTrack)obj); + } + + public override int GetHashCode() + { + return GetKey().GetHashCode(); + } + + #endregion IEquatable + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackAlbumPair.cs b/YandexMusic.API/Models/Track/YTrackAlbumPair.cs new file mode 100644 index 0000000..4aff2ee --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackAlbumPair.cs @@ -0,0 +1,40 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackAlbumPair : IEquatable + { + public string AlbumId { get; set; } + public string Id { get; set; } + + public override string ToString() + { + return string.Join(":", new[] { Id, AlbumId }.Where(s => !string.IsNullOrEmpty(s))); + } + + #region IEquatable + + public bool Equals(YTrackAlbumPair other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Id, other.Id) && string.Equals(AlbumId, other.AlbumId); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((YTrackAlbumPair)obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((Id != null ? Id.GetHashCode() : 0) * 397) ^ (AlbumId != null ? AlbumId.GetHashCode() : 0); + } + } + + #endregion IEquatable + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackContainer.cs b/YandexMusic.API/Models/Track/YTrackContainer.cs new file mode 100644 index 0000000..055512b --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackContainer.cs @@ -0,0 +1,38 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackContainer : IEquatable + { + public string Id { get; set; } + public string AlbumId { get; set; } + public decimal OriginalIndex { get; set; } + public decimal OriginalShuffleIndex { get; set; } + + public bool Recent { get; set; } + public DateTime Timestamp { get; set; } + public YTrack Track { get; set; } + + #region IEquatable + + public bool Equals(YTrackContainer other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Track.GetKey() == other.Track.GetKey(); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((YTrackContainer)obj); + } + + public override int GetHashCode() + { + return Track != null ? Track.GetHashCode() : 0; + } + + #endregion IEquatable + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackFade.cs b/YandexMusic.API/Models/Track/YTrackFade.cs new file mode 100644 index 0000000..759ba16 --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackFade.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackFade + { + public decimal InStart { get; set; } + public decimal InStop { get; set; } + public decimal OutStart { get; set; } + public decimal OutStop { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackNormalization.cs b/YandexMusic.API/Models/Track/YTrackNormalization.cs new file mode 100644 index 0000000..5a07529 --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackNormalization.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackNormalization + { + public double Gain { get; set; } + public double Peak { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackNormalizationR128.cs b/YandexMusic.API/Models/Track/YTrackNormalizationR128.cs new file mode 100644 index 0000000..032aff6 --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackNormalizationR128.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackNormalizationR128 + { + public double I { get; set; } + public double Tp { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackPosition.cs b/YandexMusic.API/Models/Track/YTrackPosition.cs new file mode 100644 index 0000000..435d83f --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackPosition.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackPosition + { + public int? Index { get; set; } + public int? Volume { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackSimilar.cs b/YandexMusic.API/Models/Track/YTrackSimilar.cs new file mode 100644 index 0000000..bb2dbd0 --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackSimilar.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Track +{ + public class YTrackSimilar + { + public YTrack Track { get; set; } + public List SimilarTracks { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Track/YTrackSupplement.cs b/YandexMusic.API/Models/Track/YTrackSupplement.cs new file mode 100644 index 0000000..8452a37 --- /dev/null +++ b/YandexMusic.API/Models/Track/YTrackSupplement.cs @@ -0,0 +1,12 @@ +using YandexMusic.API.Models.Common; + +namespace YandexMusic.API.Models.Track +{ + public class YTrackSupplement + { + public string Id { get; set; } + public List Clips { get; set; } + public YLyrics Lyrics { get; set; } + public List Videos { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ugc/YUgcUpload.cs b/YandexMusic.API/Models/Ugc/YUgcUpload.cs new file mode 100644 index 0000000..f9adb30 --- /dev/null +++ b/YandexMusic.API/Models/Ugc/YUgcUpload.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Ugc +{ + public class YUgcUpload + { + [JsonProperty("poll-result")] + public string PollResult { get; set; } + [JsonProperty("post-target")] + public string PostTarget { get; set; } + [JsonProperty("ugc-track-id")] + public string UgcTrackId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Messages/YYnisonErrorMessage.cs b/YandexMusic.API/Models/Ynison/Messages/YYnisonErrorMessage.cs new file mode 100644 index 0000000..d713ec9 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Messages/YYnisonErrorMessage.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison.Messages +{ + public class YYnisonErrorMessage : Exception + { + public YYnisonError Error { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Messages/YYnisonMessage.cs b/YandexMusic.API/Models/Ynison/Messages/YYnisonMessage.cs new file mode 100644 index 0000000..24368dd --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Messages/YYnisonMessage.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison.Messages +{ + public class YYnisonMessage + { + public string Rid { get; set; } = Guid.NewGuid().ToString(); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Messages/YYnisonMessageType.cs b/YandexMusic.API/Models/Ynison/Messages/YYnisonMessageType.cs new file mode 100644 index 0000000..a015066 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Messages/YYnisonMessageType.cs @@ -0,0 +1,12 @@ +namespace YandexMusic.API.Models.Ynison.Messages +{ + /// + /// Типы сообщений Ynison + /// + public enum YYnisonMessageType + { + Redirect, + State, + Error + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateFullStateMessage.cs b/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateFullStateMessage.cs new file mode 100644 index 0000000..bdc680f --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateFullStateMessage.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison.Messages +{ + public class YYnisonUpdateFullStateMessage : YYnisonUpdateMessage + { + public YYnisonFullState UpdateFullState { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateMessage.cs b/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateMessage.cs new file mode 100644 index 0000000..671f7d6 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdateMessage.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Ynison.Messages +{ + public class YYnisonUpdateMessage : YYnisonMessage + { +#warning нужен enum + public string ActivityInterceptionType { get; set; } = "DO_NOT_INTERCEPT_BY_DEFAULT"; + + public decimal PlayerActionTimestampMs { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdatePlayerStateMessage.cs b/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdatePlayerStateMessage.cs new file mode 100644 index 0000000..132bc61 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Messages/YYnisonUpdatePlayerStateMessage.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison.Messages +{ + public class YYnisonUpdatePlayerStateMessage : YYnisonUpdateMessage + { + public YYnisonPlayerState UpdatePlayerState { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonEntityOptions.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonEntityOptions.cs new file mode 100644 index 0000000..2c56365 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonEntityOptions.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Ynison.Wave +{ + public class YYnisonEntityOptions + { + public List TrackSources { get; set; } + public YYnisonWaveEntityOptional WaveEntityOptional { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonPhonotekaSource.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonPhonotekaSource.cs new file mode 100644 index 0000000..3d0b3a4 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonPhonotekaSource.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison.Wave +{ + public class YYnisonPhonotekaSource + { + public YYnisonEntityContext EntityContext { get; set; } + public YYnisonId AlbumId { get; set; } + public YYnisonId PlaylistId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonTrackSource.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonTrackSource.cs new file mode 100644 index 0000000..2c71286 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonTrackSource.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison.Wave +{ + public class YYnisonTrackSource + { + public decimal Key { get; set; } + public YYnisonWaveSource WaveSource { get; set; } + public YYnisonPhonotekaSource PhonotekaSource { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveEntityOptional.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveEntityOptional.cs new file mode 100644 index 0000000..12031da --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveEntityOptional.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison.Wave +{ + public class YYnisonWaveEntityOptional + { + public string SessionId { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveQueue.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveQueue.cs new file mode 100644 index 0000000..cfe8759 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveQueue.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison.Wave +{ + public class YYnisonWaveQueue + { + public List RecommendedPlayableList { get; set; } + public int LivePlayableIndex { get; set; } + public YYnisonEntityOptions EntityOptions { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSource.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSource.cs new file mode 100644 index 0000000..a5e1cc8 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSource.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison.Wave +{ + public class YYnisonWaveSource + { + public YYnisonWaveSourceType SourceType { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSourceType.cs b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSourceType.cs new file mode 100644 index 0000000..22296d1 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/Wave/YYnisonWaveSourceType.cs @@ -0,0 +1,10 @@ +using System.Runtime.Serialization; + +namespace YandexMusic.API.Models.Ynison.Wave +{ + public enum YYnisonWaveSourceType + { + [EnumMember(Value = "ONLINE_BY_DEFAULT")] + OnlineByDefault + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonDevice.cs b/YandexMusic.API/Models/Ynison/YYnisonDevice.cs new file mode 100644 index 0000000..196fefe --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonDevice.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonDevice + { + public YYnisonDeviceInfo Info { get; set; } + public YYnisonDeviceCapabilities Capabilities { get; set; } = new(); + public YYnisonDeviceVolumeInfo VolumeInfo { get; set; } = new(); + public bool IsShadow { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonDeviceCapabilities.cs b/YandexMusic.API/Models/Ynison/YYnisonDeviceCapabilities.cs new file mode 100644 index 0000000..ae56d52 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonDeviceCapabilities.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonDeviceCapabilities + { + public bool CanBePlayer { get; set; } + public bool CanBeRemoteController { get; set; } + public decimal VolumeGranularity { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonDeviceFull.cs b/YandexMusic.API/Models/Ynison/YYnisonDeviceFull.cs new file mode 100644 index 0000000..d0fb6d1 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonDeviceFull.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonDeviceFull : YYnisonDevice + { + public YYnisonSession Session { get; set; } + public decimal Volume { get; set; } + // Эта опция даёт ошибку 500 при попытке отправки на инициализации + public bool IsOffline { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonDeviceInfo.cs b/YandexMusic.API/Models/Ynison/YYnisonDeviceInfo.cs new file mode 100644 index 0000000..b852580 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonDeviceInfo.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonDeviceInfo + { + public string DeviceId { get; set; } + public string Type { get; set; } + public string Title { get; set; } + public string AppName { get; set; } + public string AppVersion { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonDeviceVolumeInfo.cs b/YandexMusic.API/Models/Ynison/YYnisonDeviceVolumeInfo.cs new file mode 100644 index 0000000..6a5d8b6 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonDeviceVolumeInfo.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonDeviceVolumeInfo + { + public decimal Volume { get; set; } + public YYnisonVersion Version { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonEntityContext.cs b/YandexMusic.API/Models/Ynison/YYnisonEntityContext.cs new file mode 100644 index 0000000..43d75c7 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonEntityContext.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison +{ + public enum YYnisonEntityContext + { + BasedOnEntityByDefault + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonEntityType.cs b/YandexMusic.API/Models/Ynison/YYnisonEntityType.cs new file mode 100644 index 0000000..9db48c1 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonEntityType.cs @@ -0,0 +1,16 @@ +namespace YandexMusic.API.Models.Ynison +{ + public enum YYnisonEntityType + { + Unspecified, + Album, + Artist, + Various, + Radio, + Generative, + FmRadio, + VideoWave, + LocalTracks, + Playlist + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonError.cs b/YandexMusic.API/Models/Ynison/YYnisonError.cs new file mode 100644 index 0000000..e74c103 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonError.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonError + { + public YYnisonErrorDetails Details { get; set; } + public int GrpcCode { get; set; } + public int HttpCode { get; set; } + public string HttpStatus { get; set; } + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonErrorDetails.cs b/YandexMusic.API/Models/Ynison/YYnisonErrorDetails.cs new file mode 100644 index 0000000..090fd47 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonErrorDetails.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonErrorDetails + { + public string YnisonErrorCode { get; set; } + public string YnisonBackoffMillis { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonFullState.cs b/YandexMusic.API/Models/Ynison/YYnisonFullState.cs new file mode 100644 index 0000000..d0c82ae --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonFullState.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonFullState + { + public YYnisonPlayerState PlayerState { get; set; } + public YYnisonDevice Device { get; set; } + public bool IsCurrentlyActive { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonId.cs b/YandexMusic.API/Models/Ynison/YYnisonId.cs new file mode 100644 index 0000000..6a30e35 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonId.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonId + { + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonKeepAliveParams.cs b/YandexMusic.API/Models/Ynison/YYnisonKeepAliveParams.cs new file mode 100644 index 0000000..21e5990 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonKeepAliveParams.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonKeepAliveParams + { + public int KeepAliveTimeSeconds { get; set; } + public int KeepAliveTimeoutSeconds { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonPlayableItem.cs b/YandexMusic.API/Models/Ynison/YYnisonPlayableItem.cs new file mode 100644 index 0000000..69de6ce --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonPlayableItem.cs @@ -0,0 +1,20 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonPlayableItem + { + public string AlbumIdOptional { get; set; } + + public string CoverUrlOptional { get; set; } + +#warning нужен enum + public string From { get; set; } + + public string PlayableId { get; set; } + + public YYnisonPlayableItemType PlayableType { get; set; } + + public string Title { get; set; } + + public YYnisonTrackInfo TrackInfo { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonPlayableItemType.cs b/YandexMusic.API/Models/Ynison/YYnisonPlayableItemType.cs new file mode 100644 index 0000000..5649d80 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonPlayableItemType.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison +{ + public enum YYnisonPlayableItemType + { + Track + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonPlayerQueue.cs b/YandexMusic.API/Models/Ynison/YYnisonPlayerQueue.cs new file mode 100644 index 0000000..2b95bd8 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonPlayerQueue.cs @@ -0,0 +1,22 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonPlayerQueue + { + public int CurrentPlayableIndex { get; set; } = -1; + + public string EntityId { get; set; } + + public YYnisonEntityType EntityType { get; set; } = YYnisonEntityType.Various; + + public YYnisonEntityContext EntityContext { get; set; } = YYnisonEntityContext.BasedOnEntityByDefault; + + public YYnisonQueueOptions Options { get; set; } = new(); + + public List PlayableList { get; set; } = new(); + public YYnisonQueue Queue { get; set; } + + public string FromOptional { get; set; } + + public YYnisonVersion Version { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonPlayerState.cs b/YandexMusic.API/Models/Ynison/YYnisonPlayerState.cs new file mode 100644 index 0000000..5c00c68 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonPlayerState.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonPlayerState + { + public YYnisonPlayerQueue PlayerQueue { get; set; } + + public YYnisonPlayerStateStatus Status { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonPlayerStateStatus.cs b/YandexMusic.API/Models/Ynison/YYnisonPlayerStateStatus.cs new file mode 100644 index 0000000..3f8f0c4 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonPlayerStateStatus.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonPlayerStateStatus + { + public decimal DurationMs { get; set; } + public bool Paused { get; set; } = true; + public decimal PlaybackSpeed { get; set; } = 1; + public decimal ProgressMs { get; set; } + public YYnisonVersion Version { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonQueue.cs b/YandexMusic.API/Models/Ynison/YYnisonQueue.cs new file mode 100644 index 0000000..6f92d31 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonQueue.cs @@ -0,0 +1,9 @@ +using YandexMusic.API.Models.Ynison.Wave; + +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonQueue + { + public YYnisonWaveQueue WaveQueue { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonQueueOptions.cs b/YandexMusic.API/Models/Ynison/YYnisonQueueOptions.cs new file mode 100644 index 0000000..781e988 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonQueueOptions.cs @@ -0,0 +1,8 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonQueueOptions + { +#warning нужен enum + public string RepeatMode { get; set; } = "NONE"; + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonRedirect.cs b/YandexMusic.API/Models/Ynison/YYnisonRedirect.cs new file mode 100644 index 0000000..1536a63 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonRedirect.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonRedirect + { + public string Host { get; set; } + public string RedirectTicket { get; set; } + public string SessionId { get; set; } + public YYnisonKeepAliveParams KeepAliveParams { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonSession.cs b/YandexMusic.API/Models/Ynison/YYnisonSession.cs new file mode 100644 index 0000000..851754b --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonSession.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonSession : YYnisonId + { + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonState.cs b/YandexMusic.API/Models/Ynison/YYnisonState.cs new file mode 100644 index 0000000..a831547 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonState.cs @@ -0,0 +1,11 @@ +using YandexMusic.API.Models.Ynison.Messages; + +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonState : YYnisonMessage + { + public List Devices { get; set; } + public YYnisonPlayerState PlayerState { get; set; } + public decimal TimestampMs { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonTrackInfo.cs b/YandexMusic.API/Models/Ynison/YYnisonTrackInfo.cs new file mode 100644 index 0000000..1aad080 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonTrackInfo.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonTrackInfo + { + public decimal TrackSourceKey { get; set; } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Ynison/YYnisonVersion.cs b/YandexMusic.API/Models/Ynison/YYnisonVersion.cs new file mode 100644 index 0000000..f2d0a73 --- /dev/null +++ b/YandexMusic.API/Models/Ynison/YYnisonVersion.cs @@ -0,0 +1,9 @@ +namespace YandexMusic.API.Models.Ynison +{ + public class YYnisonVersion + { + public string DeviceId { get; set; } + public string Version { get; set; } = Math.Floor(0x8000000000000000 * new Random().NextDouble()).ToString("##############################") + "0"; + public decimal TimestampMs { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + } +} \ No newline at end of file diff --git a/YandexMusic.API/README.md b/YandexMusic.API/README.md new file mode 100644 index 0000000..5f168a1 --- /dev/null +++ b/YandexMusic.API/README.md @@ -0,0 +1,230 @@ +# YandexMusic.API + +Асинхронная библиотека для неофициального API Яндекс Музыки, разработанная на .NET 10. + +## Описание + +**YandexMusic.API** — это комплексная C# библиотека для взаимодействия с неофициальным API Яндекс Музыки. Библиотека предоставляет полный набор методов для работы с музыкой, альбомами, исполнителями, плейлистами, радио и другими функциями сервиса. + +## Основные возможности + +- ✅ **Полная поддержка API Яндекс Музыки** - Все основные методы и функции +- ✅ **Асинхронная архитектура** - Основана на async/await для максимальной производительности +- ✅ **Модульная структура** - Отдельные API классы для каждой функциональности +- ✅ **Сериализация JSON** - Использует современный System.Text.Json +- ✅ **Поддержка WebSocket** - Протокол Ynison для real-time функций +- ✅ **Типобезопасность** - Полная поддержка nullable reference types +- ✅ **Документация на русском** - XML документация всех публичных членов + +## Поддерживаемые компоненты + +### API Методы + +- **Album API** - Получение информации об альбомах +- **Artist API** - Работа с данными исполнителей +- **Label API** - Управление лейблами +- **Landing API** - Доступ к рекомендациям главной страницы +- **Library API** - Работа с библиотекой пользователя (лайки, дизлайки) +- **Playlist API** - Управление плейлистами +- **Pins API** - Управление закреплёнными объектами +- **Radio API** - Управление радиостанциями +- **Search API** - Поиск по музыке +- **Track API** - Работа с треками +- **Queue API** - Управление очередью воспроизведения +- **User API** - Управление пользователем и авторизацией +- **UGC API** - Загрузка пользовательского контента +- **Ynison API** - WebSocket протокол для real-time взаимодействия + +### Модели данных + +- Полные модели для альбомов, треков, исполнителей, плейлистов +- Модели поиска и рекомендаций +- Модели для управления библиотекой +- Модели для радио и станций +- Модели для WebSocket взаимодействия + +## Требования + +- **.NET 10** или выше +- C# 12 или выше + +## Технологический стек + +- **Runtime**: .NET 10 +- **Язык**: C# 12 +- **Сериализация**: System.Text.Json +- **Асинхронность**: async/await +- **WebSocket**: Native .NET WebSocket support + +## Архитектура + +### Структура проекта + +``` +YandexMusic.API/ +├── API/ # Основные классы API для каждой функции +├── Models/ # Модели данных +├── Requests/ # Построители запросов +├── Common/ # Общая функциональность и провайдеры +├── Extensions/ # Расширения для удобства +├── YandexMusicApi.cs # Главный класс API +└── README.md # Документация +``` + +### Основные компоненты + +#### YandexMusicApi +Главный класс API, предоставляющий доступ ко всем веткам функциональности через свойства. + +```csharp +var api = new YandexMusicApi(); +``` + +#### AuthStorage +Управление учётными данными пользователя, токенами и сессией. + +#### RequestProvider +Интерфейс для обработки HTTP запросов и ответов. Имеет несколько реализаций: +- `DefaultRequestProvider` - стандартный провайдер +- `CommonRequestProvider` - расширенный провайдер +- `MockRequestProvider` - для тестирования + +#### API Classes +Каждый API класс наследуется от `YCommonAPI` и предоставляет синхронные и асинхронные методы. + +## Использование + +### Базовый пример + +```csharp +using YandexMusic.API; + +// Создание экземпляра API +var api = new YandexMusicApi(); + +// Получение информации о треке +var track = await api.Track.GetTrackAsync(trackId); + +// Поиск музыки +var searchResults = await api.Search.SearchAsync("Яндекс"); + +// Работа с плейлистами +var playlists = await api.Playlist.GetPlaylistAsync(playlistId); +``` + +### Авторизация + +```csharp +var storage = new AuthStorage(); + +// Авторизация пользователя +// Используйте соответствующие методы AuthStorage +storage.IsAuthorized = true; +storage.Token = "ваш_токен"; +``` + +### Работа с провайдерами + +```csharp +var storage = new AuthStorage(); + +// Использование пользовательского провайдера +var customProvider = new MyCustomRequestProvider(); +var storage = new AuthStorage(customProvider); +``` + +## Особенности .NET 10 + +Проект полностью использует возможности .NET 10: + +- **C# 12 Features** - Last language version +- **Nullable Reference Types** - Полная поддержка nullable context +- **Implicit Usings** - Автоматический импорт стандартных пространств имён +- **Modern async/await** - Асинхронная обработка +- **System.Text.Json** - Встроенная сериализация JSON + +## Документация + +Все публичные типы и методы имеют XML документацию на русском языке. Документация автоматически обновляется и доступна через IntelliSense в IDE. + +### Пример документации + +```csharp +/// Получает информацию о треке по идентификатору. +/// Идентификатор трека +/// Модель трека +public async Task GetTrackAsync(string trackId) +{ + // ... +} +``` + +## Миграция с предыдущих версий + +Если вы переходите с более старой версии: + +1. **Обновите .NET версию** до 10.0 +2. **System.Text.Json** уже используется везде вместо Newtonsoft.Json +3. **API сигнатуры** остаются совместимы +4. **Regions удалены** - код лучше организован + +## Провайдеры и расширяемость + +Архитектура позволяет легко расширять функциональность: + +```csharp +public interface IRequestProvider +{ + Task GetWebResponseAsync(HttpRequestMessage message); + Task GetDataFromResponseAsync(YandexMusicApi api, HttpResponseMessage response); +} + +// Реализуйте ваш провайдер +public class MyProvider : IRequestProvider +{ + // Ваша реализация +} +``` + +## Отладка + +Проект поддерживает встроенную отладку через `DebugSettings`: + +```csharp +var debug = new DebugSettings +{ + ClearDirectory = true +}; + +var storage = new AuthStorage(settings: debug); +``` + +## Производительность + +- **Асинхронные операции** - Не блокируют потоки +- **Кэширование** - Встроенная поддержка кэширования ответов +- **Оптимизированная сериализация** - System.Text.Json быстрее Newtonsoft +- **Минимальные зависимости** - Только необходимые пакеты + +## Лицензия + +Это неофициальная библиотека для API Яндекс Музыки. Используйте на свой риск. + +## Автор + +**FrigaT** - Разработчик библиотеки + +## Поддержка + +Для вопросов и предложений свяжитесь с разработчиком или создайте issue в репозитории. + +## Благодарности + +Спасибо сообществу .NET разработчиков и всем участникам проекта. + +--- + +**Версия**: 0.0.1 +**Платформа**: .NET 10 +**Язык**: C# 12 +**Обновлено**: 2024 diff --git a/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs new file mode 100644 index 0000000..a8d019f --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs @@ -0,0 +1,27 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "registration-validations/auth/multi_step/commit_password")] + internal class YGetAuthAppPasswordBuilder : YRequestBuilder + { + public YGetAuthAppPasswordBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "track_id", storage.AuthToken.TrackId }, + { "password", tuple }, + { "retpath", "https://passport.yandex.ru/am/finish?status=ok&from=Login" } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs new file mode 100644 index 0000000..2ade7a6 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs @@ -0,0 +1,30 @@ +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "registration-validations/textcaptcha")] + internal class YGetAuthCaptchaBuilder : YRequestBuilder + { + public YGetAuthCaptchaBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "track_id", storage.AuthToken.TrackId }, + }); + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Add("X-Requested-With", "XMLHttpRequest"); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs new file mode 100644 index 0000000..e97f4e1 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs @@ -0,0 +1,37 @@ +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YMobileProxyRequest(WebRequestMethods.Http.Post, "1/bundle/oauth/token_by_sessionid")] + internal class YGetAuthCookiesBuilder : YRequestBuilder + { + public YGetAuthCookiesBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + CookieCollection cookieCollection = new() { + storage.Context.Cookies.GetCookies(new Uri("https://yandex.ru/")), + storage.Context.Cookies.GetCookies(new Uri("https://passport.yandex.ru/")) + }; + + headers.Add("Ya-Client-Cookie", string.Join(";", cookieCollection.Select(c => $"{c.Name}={c.Value}"))); + headers.Add("Ya-Client-Host", "passport.yandex.ru"); + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "client_id", YConstants.XClientId }, + { "client_secret", YConstants.XClientSecret } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthInfoBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthInfoBuilder.cs new file mode 100644 index 0000000..bad7e32 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthInfoBuilder.cs @@ -0,0 +1,18 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YApiRequest(WebRequestMethods.Http.Get, "account/status")] + public class YGetAuthInfoBuilder : YRequestBuilder, object> + { + public YGetAuthInfoBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs new file mode 100644 index 0000000..1059b89 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs @@ -0,0 +1,30 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "registration-validations/auth/send_magic_letter")] + internal class YGetAuthLetterBuilder : YRequestBuilder + { + public YGetAuthLetterBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + if (storage.AuthToken == null) + { + throw new Exception("Не найдена сессия входа."); + } + + return new FormUrlEncodedContent(new Dictionary + { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "track_id", storage.AuthToken.TrackId }, + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs new file mode 100644 index 0000000..4a90466 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs @@ -0,0 +1,32 @@ +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "registration-validations/checkHuman")] + internal class YGetAuthLoginCaptchaBuilder : YRequestBuilder + { + public YGetAuthLoginCaptchaBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "track_id", storage.AuthToken.TrackId }, + { "answer", tuple } + }); + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Add("X-Requested-With", "XMLHttpRequest"); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs new file mode 100644 index 0000000..4eb7d33 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "auth/letter/status/")] + internal class YGetAuthLoginLetterBuilder : YRequestBuilder + { + public YGetAuthLoginLetterBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "track_id", storage.AuthToken.TrackId }, + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs new file mode 100644 index 0000000..6d0954b --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "auth/new/magic/status/")] + internal class YGetAuthLoginQRBuilder : YRequestBuilder + { + public YGetAuthLoginQRBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "track_id", storage.AuthToken.TrackId } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs new file mode 100644 index 0000000..1ea5fd0 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "registration-validations/auth/multi_step/start")] + internal class YGetAuthLoginUserBuilder : YRequestBuilder + { + public YGetAuthLoginUserBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent((string token, string login) tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", tuple.token }, + { "login", tuple.login } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthMethodsBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthMethodsBuilder.cs new file mode 100644 index 0000000..95a7576 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthMethodsBuilder.cs @@ -0,0 +1,24 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Get, "am")] + internal class YGetAuthMethodsBuilder : YRequestBuilder + { + public YGetAuthMethodsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams(string tuple) + { + return new NameValueCollection { + { "app_platform", "android" } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs new file mode 100644 index 0000000..720f74d --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs @@ -0,0 +1,26 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YPassportRequest(WebRequestMethods.Http.Post, "registration-validations/auth/password/submit")] + internal class YGetAuthQRBuilder : YRequestBuilder + { + public YGetAuthQRBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "csrf_token", storage.AuthToken.CsfrToken }, + { "retpath", "https://passport.yandex.ru/profile" }, + { "with_code", "1" }, + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs b/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs new file mode 100644 index 0000000..a23433e --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs @@ -0,0 +1,17 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YLoginRequest(WebRequestMethods.Http.Get, "info")] + public class YGetLoginInfoBuilder : YRequestBuilder + { + public YGetLoginInfoBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetMusicTokenBuilder.cs b/YandexMusic.API/Requests/Account/YGetMusicTokenBuilder.cs new file mode 100644 index 0000000..8054614 --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetMusicTokenBuilder.cs @@ -0,0 +1,36 @@ +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YOAuthMobile(WebRequestMethods.Http.Post, "/1/token")] + internal class YGetMusicTokenBuilder : YRequestBuilder + { + public YGetMusicTokenBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(string tuple) + { + return new FormUrlEncodedContent(new Dictionary + { + { "client_id", YConstants.ClientId }, + { "client_secret", YConstants.ClientSecret }, + { "grant_type", "x-token" }, + { "access_token", storage.AccessToken.AccessToken } + }); + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Remove("Authorization"); + + base.SetCustomHeaders(headers); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs b/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs new file mode 100644 index 0000000..758961d --- /dev/null +++ b/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs @@ -0,0 +1,31 @@ +using System.Collections.Specialized; +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Account +{ + [YMobileProxyRequest(WebRequestMethods.Http.Get, "/1/bundle/account/short_info/")] + internal class YGetShortAccountInifoBuilder : YRequestBuilder + { + public YGetShortAccountInifoBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams(object tuple) + { + return new NameValueCollection { + { "avatar_size", "islands-300" } + }; + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Add("Ya-Consumer-Authorization", $"OAuth {storage.AccessToken.AccessToken}"); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Album/YGetAlbumBuilder.cs b/YandexMusic.API/Requests/Album/YGetAlbumBuilder.cs new file mode 100644 index 0000000..9dc8f7c --- /dev/null +++ b/YandexMusic.API/Requests/Album/YGetAlbumBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Album +{ + [YApiRequest(WebRequestMethods.Http.Get, "albums/{albumId}/with-tracks")] + public class YGetAlbumBuilder : YRequestBuilder, string> + { + public YGetAlbumBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string albumId) + { + return new Dictionary { + { "albumId", albumId } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Album/YGetAlbumsBuilder.cs b/YandexMusic.API/Requests/Album/YGetAlbumsBuilder.cs new file mode 100644 index 0000000..8031851 --- /dev/null +++ b/YandexMusic.API/Requests/Album/YGetAlbumsBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Album; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Album +{ + [YApiRequest(WebRequestMethods.Http.Post, "albums")] + public class YGetAlbumsBuilder : YRequestBuilder>, IEnumerable> + { + public YGetAlbumsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(IEnumerable albumIds) + { + return new FormUrlEncodedContent(new Dictionary { + { "album-ids", string.Join(",", albumIds) } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Artist/YGetArtistBuilder.cs b/YandexMusic.API/Requests/Artist/YGetArtistBuilder.cs new file mode 100644 index 0000000..9e5c5c7 --- /dev/null +++ b/YandexMusic.API/Requests/Artist/YGetArtistBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Artist +{ + [YApiRequest(WebRequestMethods.Http.Get, "artists/{artistId}/brief-info")] + public class YGetArtistBuilder : YRequestBuilder, string> + { + public YGetArtistBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string artistId) + { + return new Dictionary { + { "artistId", artistId } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs b/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs new file mode 100644 index 0000000..907ee0f --- /dev/null +++ b/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs @@ -0,0 +1,32 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Artist +{ + [YApiRequest(WebRequestMethods.Http.Get, "artists/{artistId}/tracks")] + public class YGetArtistTrackBuilder : YRequestBuilder, (string id, int page, int pageSize)> + { + public YGetArtistTrackBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) { } + + protected override Dictionary GetSubstitutions((string id, int page, int pageSize) tuple) + { + return new Dictionary { + { "artistId", tuple.id }, + }; + } + + protected override NameValueCollection GetQueryParams((string id, int page, int pageSize) tuple) + { + return new NameValueCollection { + { "page", tuple.page.ToString() }, + { "pageSize", tuple.pageSize.ToString() }, + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Artist/YGetArtistsBuilder.cs b/YandexMusic.API/Requests/Artist/YGetArtistsBuilder.cs new file mode 100644 index 0000000..9860d42 --- /dev/null +++ b/YandexMusic.API/Requests/Artist/YGetArtistsBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Artist; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Artist +{ + [YApiRequest(WebRequestMethods.Http.Post, "artists")] + public class YGetArtistsBuilder : YRequestBuilder>, IEnumerable> + { + public YGetArtistsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(IEnumerable artistIds) + { + return new FormUrlEncodedContent(new Dictionary { + { "artist-Ids", string.Join(",", artistIds) } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YApiRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YApiRequestAttribute.cs new file mode 100644 index 0000000..4610b03 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YApiRequestAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YApiRequestAttribute : YBasePathRequestAttribute + { + public YApiRequestAttribute(string method, string url) : base(method, url) + { + basePath = "https://api.music.yandex.net"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YBasePathRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YBasePathRequestAttribute.cs new file mode 100644 index 0000000..e842aee --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YBasePathRequestAttribute.cs @@ -0,0 +1,32 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + /// + /// Атрибут запроса относительно базового адреса + /// + public class YBasePathRequestAttribute : YRequestAttribute + { + #region Поля + + protected string basePath; + + #endregion Поля + + #region Свойства + public override string Url => GetFullUrl(); + + #endregion Свойства + + #region Вспомогательные функции + + private string GetFullUrl() + { + return $"{basePath.TrimEnd('/')}/{path.TrimStart('/')}"; + } + + #endregion Вспомогательные функции + + public YBasePathRequestAttribute(string method, string url) : base(method, url) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YLoginRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YLoginRequestAttribute.cs new file mode 100644 index 0000000..cc21fa4 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YLoginRequestAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YLoginRequestAttribute : YBasePathRequestAttribute + { + public YLoginRequestAttribute(string method, string url) : base(method, url) + { + basePath = "https://login.yandex.ru"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YMobileProxyRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YMobileProxyRequestAttribute.cs new file mode 100644 index 0000000..130b831 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YMobileProxyRequestAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YMobileProxyRequestAttribute : YBasePathRequestAttribute + { + public YMobileProxyRequestAttribute(string method, string url) : base(method, url) + { + basePath = "https://mobileproxy.passport.yandex.net"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YOAuthMobileAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YOAuthMobileAttribute.cs new file mode 100644 index 0000000..29a9961 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YOAuthMobileAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YOAuthMobileAttribute : YBasePathRequestAttribute + { + public YOAuthMobileAttribute(string method, string url) : base(method, url) + { + basePath = "https://oauth.mobile.yandex.net"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YOAuthRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YOAuthRequestAttribute.cs new file mode 100644 index 0000000..16eef58 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YOAuthRequestAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YOAuthRequestAttribute : YBasePathRequestAttribute + { + public YOAuthRequestAttribute(string method, string url) : base(method, url) + { + basePath = "https://oauth.yandex.ru"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YPassportRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YPassportRequestAttribute.cs new file mode 100644 index 0000000..a678f45 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YPassportRequestAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YPassportRequestAttribute : YBasePathRequestAttribute + { + public YPassportRequestAttribute(string method, string url) : base(method, url) + { + basePath = "https://passport.yandex.ru"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YRequestAttribute.cs new file mode 100644 index 0000000..0bcd8f2 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YRequestAttribute.cs @@ -0,0 +1,27 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + /// + /// Атрибут запроса без привязки к базовому адресу + /// + public class YRequestAttribute : Attribute + { + #region Поля + + protected string path; + + #endregion Поля + + #region Свойства + + public string Method { get; } + public virtual string Url => path; + + #endregion Свойства + + public YRequestAttribute(string method, string url) + { + Method = method; + path = url; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/Attributes/YWebApiRequestAttribute.cs b/YandexMusic.API/Requests/Common/Attributes/YWebApiRequestAttribute.cs new file mode 100644 index 0000000..22314f4 --- /dev/null +++ b/YandexMusic.API/Requests/Common/Attributes/YWebApiRequestAttribute.cs @@ -0,0 +1,10 @@ +namespace YandexMusic.API.Requests.Common.Attributes +{ + public class YWebApiRequestAttribute : YBasePathRequestAttribute + { + public YWebApiRequestAttribute(string method, string url) : base(method, url) + { + basePath = "https://music.yandex.ru"; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/HttpContext.cs b/YandexMusic.API/Requests/Common/HttpContext.cs new file mode 100644 index 0000000..7666088 --- /dev/null +++ b/YandexMusic.API/Requests/Common/HttpContext.cs @@ -0,0 +1,26 @@ +using System.Net; + +namespace YandexMusic.API.Requests.Common +{ + public class HttpContext + { + public CookieContainer Cookies; + + public HttpContext() + { + Cookies = new CookieContainer(); + } + + public IWebProxy WebProxy { get; set; } + + public long GetTimeInterval() + { + DateTime dt = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now); + DateTime dt1970 = new(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + TimeSpan tsInterval = dt.Subtract(dt1970); + long iMilliseconds = Convert.ToInt64(tsInterval.TotalMilliseconds); + + return iMilliseconds; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/YConstants.cs b/YandexMusic.API/Requests/Common/YConstants.cs new file mode 100644 index 0000000..7dbef77 --- /dev/null +++ b/YandexMusic.API/Requests/Common/YConstants.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Requests.Common +{ + internal class YConstants + { + public static string ClientId = "23cabbbdc6cd418abb4b39c32c41195d"; + public static string ClientSecret = "53bc75238f0c4d08a118e51fe9203300"; + + public const string XClientId = "c0ebe342af7d48fbbbfcf2d2eedb8f9e"; + public const string XClientSecret = "ad0a908f0aa341a182a37ecd75bc319e"; + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/YRequest.cs b/YandexMusic.API/Requests/Common/YRequest.cs new file mode 100644 index 0000000..21ad639 --- /dev/null +++ b/YandexMusic.API/Requests/Common/YRequest.cs @@ -0,0 +1,48 @@ +using YandexMusic.API.Common; +using YandexMusic.API.Common.Providers; + +namespace YandexMusic.API.Requests.Common +{ + internal class YRequest + { + #region Поля + + private HttpRequestMessage msg; + private IRequestProvider provider; + + protected YandexMusicApi api; + + #endregion Поля + + + + public YRequest(HttpRequestMessage message, YandexMusicApi yandex, AuthStorage auth) + { + msg = message; + api = yandex; + provider = auth.Provider; + } + + public async Task GetResponseAsync() + { + if (msg == null) + return default; + + HttpResponseMessage response = await provider.GetWebResponseAsync(msg); + + if (typeof(T) == typeof(HttpResponseMessage)) + return (T)(object)response; + + try + { + return await provider.GetDataFromResponseAsync(api, response); + } + finally + { + response.Dispose(); + } + } + + + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/YRequestBuilder.cs b/YandexMusic.API/Requests/Common/YRequestBuilder.cs new file mode 100644 index 0000000..daa463b --- /dev/null +++ b/YandexMusic.API/Requests/Common/YRequestBuilder.cs @@ -0,0 +1,158 @@ +using System.Collections.Specialized; +using System.Net; +using System.Net.Http.Headers; +using System.Reflection; +using System.Text; +using System.Web; + +using YandexMusic.API.Common; +using YandexMusic.API.Extensions; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Common +{ + public class YRequestBuilder + { + #region Поля + + private readonly JsonSerializerSettings jsonSettings = new() + { + Converters = new List { + new StringEnumConverter { + NamingStrategy = new CamelCaseNamingStrategy() + } + }, + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + private YRequestAttribute requestInfo; + private Dictionary subs; + + protected YandexMusicApi api; + protected AuthStorage storage; + protected string device; + + #endregion Поля + + #region Свойства + + protected YandexMusicApi API => api; + protected AuthStorage Storage => storage; + + #endregion Свойства + + #region Вспомогательные функции + + private Uri BuildUri(ParamsTuple tuple) + { + NameValueCollection queryParams = GetQueryParams(tuple); + NameValueCollection modifiedParams = HttpUtility.ParseQueryString(string.Empty); + + // Подстановка в параметры + foreach (string key in queryParams.Keys) + { + modifiedParams.Set(key, ReplaceSubs(queryParams.Get(key))); + } + + string endpoint = ReplaceSubs(requestInfo.Url); + + UriBuilder builder = new(endpoint) + { + Query = modifiedParams.ToString() ?? string.Empty + }; + + + return builder.Uri; + } + + private HttpRequestMessage CreateMessage(ParamsTuple tuple) + { + HttpRequestMessage msg = new() + { + RequestUri = BuildUri(tuple), + Method = new HttpMethod(requestInfo.Method), + Content = GetContent(tuple) + }; + + msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptCharset.GetName(), Encoding.UTF8.WebName); + msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptEncoding.GetName(), "gzip"); + + // Добавление заголовка авторизации + if (!string.IsNullOrEmpty(storage.Token)) + msg.Headers.TryAddWithoutValidation(HttpRequestHeader.Authorization.GetName(), $"OAuth {storage.Token}"); + + SetCustomHeaders(msg.Headers); + + return msg; + } + + protected string ReplaceSubs(string str) + { + string[] sub = str.GetMatches(@"\{.+?\}"); + + foreach (string s in sub) + { + if (!subs.TryGetValue(s.ReplaceRegex(@"[\{\}]", string.Empty), out string value)) + throw new Exception($"Не найдена подстановка {s}"); + + str = str.Replace(s, value); + } + + return str; + } + protected virtual Dictionary GetSubstitutions(ParamsTuple tuple) + { + return new Dictionary(); + } + + protected virtual NameValueCollection GetQueryParams(ParamsTuple tuple) + { + return new NameValueCollection(); + } + + protected virtual HttpContent GetContent(ParamsTuple tuple) + { + return null; + } + + protected virtual void SetCustomHeaders(HttpRequestHeaders headers) + { + } + + protected string SerializeJson(object data) + { + return JsonConvert.SerializeObject(data, jsonSettings); + } + + #endregion Вспомогательные функции + + + + public YRequestBuilder(YandexMusicApi yandex, AuthStorage auth) + { + requestInfo = GetType() + .GetCustomAttributes() + .FirstOrDefault(); + + if (requestInfo == null) + throw new NotImplementedException($"Отсутствует атрибут {nameof(YRequestAttribute)}"); + + api = yandex; + storage = auth; + + // Устройство по умолчанию + device = $"os=CSharp; os_version=; manufacturer=K1llM@n; model=Yandex Music API; clid=; device_id={storage.DeviceId}; uuid=random"; + } + + internal YRequest Build(ParamsTuple tuple) + { + subs = GetSubstitutions(tuple); + HttpRequestMessage msg = CreateMessage(tuple); + + return new YRequest(msg, api, storage); + } + + + } +} diff --git a/YandexMusic.API/Requests/Feed/YGetFeedBuilder.cs b/YandexMusic.API/Requests/Feed/YGetFeedBuilder.cs new file mode 100644 index 0000000..e17eb7a --- /dev/null +++ b/YandexMusic.API/Requests/Feed/YGetFeedBuilder.cs @@ -0,0 +1,18 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Feed; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Feed +{ + [YApiRequest(WebRequestMethods.Http.Get, "feed")] + public class YGetFeedBuilder : YRequestBuilder, object> + { + public YGetFeedBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Label/YGetLabelAlbumsBuilder.cs b/YandexMusic.API/Requests/Label/YGetLabelAlbumsBuilder.cs new file mode 100644 index 0000000..9cff2a7 --- /dev/null +++ b/YandexMusic.API/Requests/Label/YGetLabelAlbumsBuilder.cs @@ -0,0 +1,33 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Label; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Label +{ + [YApiRequest(WebRequestMethods.Http.Get, "labels/{labelId}/albums")] + public class YGetLabelAlbumsBuilder : YRequestBuilder, (YLabel label, int pageNumber)> + { + public YGetLabelAlbumsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams((YLabel label, int pageNumber) tuple) + { + return new NameValueCollection { + { "page", tuple.pageNumber.ToString() } + }; + } + + protected override Dictionary GetSubstitutions((YLabel label, int pageNumber) tuple) + { + return new Dictionary { + { "labelId", tuple.label.Id } + }; + } + } +} diff --git a/YandexMusic.API/Requests/Label/YGetLabelArtistsBuilder.cs b/YandexMusic.API/Requests/Label/YGetLabelArtistsBuilder.cs new file mode 100644 index 0000000..f3055c3 --- /dev/null +++ b/YandexMusic.API/Requests/Label/YGetLabelArtistsBuilder.cs @@ -0,0 +1,33 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Label; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Label +{ + [YApiRequest(WebRequestMethods.Http.Get, "labels/{labelId}/artists")] + public class YGetLabelArtistsBuilder : YRequestBuilder, (YLabel label, int pageNumber)> + { + public YGetLabelArtistsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams((YLabel label, int pageNumber) tuple) + { + return new NameValueCollection { + { "page", tuple.pageNumber.ToString() } + }; + } + + protected override Dictionary GetSubstitutions((YLabel label, int pageNumber) tuple) + { + return new Dictionary { + { "labelId", tuple.label.Id } + }; + } + } +} diff --git a/YandexMusic.API/Requests/Landing/YGetChildrenLandingBuilder.cs b/YandexMusic.API/Requests/Landing/YGetChildrenLandingBuilder.cs new file mode 100644 index 0000000..2e5cf91 --- /dev/null +++ b/YandexMusic.API/Requests/Landing/YGetChildrenLandingBuilder.cs @@ -0,0 +1,18 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Landing; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Landing +{ + [YApiRequest(WebRequestMethods.Http.Get, "children-landing/catalogue")] + public class YGetChildrenLandingBuilder : YRequestBuilder, object> + { + public YGetChildrenLandingBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Landing/YGetLandingBuilder.cs b/YandexMusic.API/Requests/Landing/YGetLandingBuilder.cs new file mode 100644 index 0000000..1023cb9 --- /dev/null +++ b/YandexMusic.API/Requests/Landing/YGetLandingBuilder.cs @@ -0,0 +1,29 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Landing; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Landing +{ + [YApiRequest(WebRequestMethods.Http.Get, "landing3")] + public class YGetLandingBuilder : YRequestBuilder, YLandingBlockType[]> + { + public YGetLandingBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams(YLandingBlockType[] tuple) + { + string blocks = string.Join(",", tuple + .Select(b => SerializeJson(b).Replace("\"", string.Empty))); + + return new NameValueCollection { + { "blocks", blocks } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Library/YGetLibraryRecentlyListenedBuilder.cs b/YandexMusic.API/Requests/Library/YGetLibraryRecentlyListenedBuilder.cs new file mode 100644 index 0000000..e360120 --- /dev/null +++ b/YandexMusic.API/Requests/Library/YGetLibraryRecentlyListenedBuilder.cs @@ -0,0 +1,37 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Landing.Entity.Entities.Context; +using YandexMusic.API.Models.Library; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Library +{ + [YApiRequest(WebRequestMethods.Http.Get, "/users/{uid}/contexts")] + public class YGetLibraryRecentlyListenedBuilder : YRequestBuilder, + (IEnumerable contextTypes, int trackCount, int contextCount)> + { + public YGetLibraryRecentlyListenedBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams((IEnumerable contextTypes, int trackCount, int contextCount) tuple) + { + return new NameValueCollection { + { "trackCount", tuple.trackCount.ToString() }, + { "contextCount", tuple.contextCount.ToString() }, + { "types", string.Join(",", tuple.contextTypes.Select(x => x.ToString().ToLowerInvariant())) } + }; + } + + protected override Dictionary GetSubstitutions((IEnumerable contextTypes, int trackCount, int contextCount) tuple) + { + return new Dictionary { + { "uid", storage.User.Uid } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Library/YGetLibrarySectionBuilder.cs b/YandexMusic.API/Requests/Library/YGetLibrarySectionBuilder.cs new file mode 100644 index 0000000..3322bb7 --- /dev/null +++ b/YandexMusic.API/Requests/Library/YGetLibrarySectionBuilder.cs @@ -0,0 +1,27 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Library; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Library +{ + [YApiRequest(WebRequestMethods.Http.Get, "users/{uid}/{type}/{section}")] + public class YGetLibrarySectionBuilder : YRequestBuilder, (YLibrarySection section, YLibrarySectionType type)> + { + public YGetLibrarySectionBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((YLibrarySection section, YLibrarySectionType type) tuple) + { + return new Dictionary { + { "uid", storage.User.Uid }, + { "type", tuple.type.ToString().ToLower() }, + { "section", tuple.section.ToString().ToLower() }, + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Library/YLibraryAddBuilder.cs b/YandexMusic.API/Requests/Library/YLibraryAddBuilder.cs new file mode 100644 index 0000000..31af84d --- /dev/null +++ b/YandexMusic.API/Requests/Library/YLibraryAddBuilder.cs @@ -0,0 +1,34 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Library; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Library +{ + [YApiRequest(WebRequestMethods.Http.Post, "users/{uid}/{type}/{section}/add-multiple")] + public class YLibraryAddBuilder : YRequestBuilder, (string id, YLibrarySection section, YLibrarySectionType type)> + { + public YLibraryAddBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string id, YLibrarySection section, YLibrarySectionType type) tuple) + { + return new Dictionary { + { "uid", storage.User.Uid }, + { "type", tuple.type.ToString().ToLower() }, + { "section", tuple.section.ToString().ToLower() }, + }; + } + + protected override HttpContent GetContent((string id, YLibrarySection section, YLibrarySectionType type) tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { $"{tuple.section.ToString().ToLower().TrimEnd('s')}-ids", tuple.id } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Library/YLibraryRemoveBuilder.cs b/YandexMusic.API/Requests/Library/YLibraryRemoveBuilder.cs new file mode 100644 index 0000000..cd07fc4 --- /dev/null +++ b/YandexMusic.API/Requests/Library/YLibraryRemoveBuilder.cs @@ -0,0 +1,34 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Library; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Library +{ + [YApiRequest(WebRequestMethods.Http.Post, "users/{uid}/{type}/{section}/remove")] + public class YLibraryRemoveBuilder : YRequestBuilder, (string id, YLibrarySection section, YLibrarySectionType type)> + { + public YLibraryRemoveBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string id, YLibrarySection section, YLibrarySectionType type) tuple) + { + return new Dictionary { + { "uid", storage.User.Uid }, + { "type", tuple.type.ToString().ToLower() }, + { "section", tuple.section.ToString().ToLower() }, + }; + } + + protected override HttpContent GetContent((string id, YLibrarySection section, YLibrarySectionType type) tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { $"{tuple.section.ToString().ToLower().TrimEnd('s')}-ids", tuple.id } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Pins/YGetPinsBuilder.cs b/YandexMusic.API/Requests/Pins/YGetPinsBuilder.cs new file mode 100644 index 0000000..044c927 --- /dev/null +++ b/YandexMusic.API/Requests/Pins/YGetPinsBuilder.cs @@ -0,0 +1,18 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Pins; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Pins +{ + [YApiRequest(WebRequestMethods.Http.Get, "pins")] + public class YGetPinsBuilder : YRequestBuilder, object> + { + public YGetPinsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YGetPlaylistBuilder.cs b/YandexMusic.API/Requests/Playlist/YGetPlaylistBuilder.cs new file mode 100644 index 0000000..7c10838 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YGetPlaylistBuilder.cs @@ -0,0 +1,26 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Get, "users/{user}/playlists/{kind}")] + public class YGetPlaylistBuilder : YRequestBuilder, (string user, string kind)> + { + public YGetPlaylistBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string user, string kind) tuple) + { + return new Dictionary { + { "user", tuple.user }, + { "kind", tuple.kind }, + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YGetPlaylistByUuidBuilder.cs b/YandexMusic.API/Requests/Playlist/YGetPlaylistByUuidBuilder.cs new file mode 100644 index 0000000..9efcdc1 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YGetPlaylistByUuidBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Get, "playlist/{uuid}")] + public class YGetPlaylistByUuidBuilder : YRequestBuilder, string> + { + public YGetPlaylistByUuidBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string uuid) + { + return new Dictionary { + { "uuid", uuid }, + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YGetPlaylistFavoritesBuilder.cs b/YandexMusic.API/Requests/Playlist/YGetPlaylistFavoritesBuilder.cs new file mode 100644 index 0000000..43b7642 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YGetPlaylistFavoritesBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Get, "users/{uid}/playlists/list")] + public class YGetPlaylistFavoritesBuilder : YRequestBuilder>, object> + { + public YGetPlaylistFavoritesBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(object tuple) + { + return new Dictionary { + { "uid", storage.User.Uid } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YGetPlaylistsBuilder.cs b/YandexMusic.API/Requests/Playlist/YGetPlaylistsBuilder.cs new file mode 100644 index 0000000..fdc2860 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YGetPlaylistsBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Post, "playlists/list")] + public class YGetPlaylistsBuilder : YRequestBuilder>, IEnumerable<(string User, string Kind)>> + { + public YGetPlaylistsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(IEnumerable<(string User, string Kind)> playlistIds) + { + return new FormUrlEncodedContent(new Dictionary { + { "playlist-Ids", string.Join(",", playlistIds.Select(t => $"{t.User}:{t.Kind}")) } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YPlaylistChangeBuilder.cs b/YandexMusic.API/Requests/Playlist/YPlaylistChangeBuilder.cs new file mode 100644 index 0000000..ee300c4 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YPlaylistChangeBuilder.cs @@ -0,0 +1,35 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Post, "users/{uid}/playlists/{kind}/change")] + public class YPlaylistChangeBuilder : YRequestBuilder, (YPlaylist playlist, IEnumerable changes)> + { + public YPlaylistChangeBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((YPlaylist playlist, IEnumerable changes) tuple) + { + return new Dictionary { + { "uid", storage.User.Uid }, + { "kind", tuple.playlist.Kind } + }; + } + + protected override HttpContent GetContent((YPlaylist playlist, IEnumerable changes) tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "kind", tuple.playlist.Kind }, + { "revision", tuple.playlist.Revision.ToString() }, + { "diff", SerializeJson(tuple.changes) } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YPlaylistCreateBuilder.cs b/YandexMusic.API/Requests/Playlist/YPlaylistCreateBuilder.cs new file mode 100644 index 0000000..2769d75 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YPlaylistCreateBuilder.cs @@ -0,0 +1,33 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Post, "users/{uid}/playlists/create")] + public class YPlaylistCreateBuilder : YRequestBuilder, string> + { + public YPlaylistCreateBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string name) + { + return new Dictionary { + { "uid", storage.User.Uid } + }; + } + + protected override HttpContent GetContent(string name) + { + return new FormUrlEncodedContent(new Dictionary { + { "title", name }, + { "visibility", "public" } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YPlaylistRemoveBuilder.cs b/YandexMusic.API/Requests/Playlist/YPlaylistRemoveBuilder.cs new file mode 100644 index 0000000..de3636b --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YPlaylistRemoveBuilder.cs @@ -0,0 +1,33 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Post, "users/{uid}/playlists/{kind}/name")] + public class YPlaylistRenameBuilder : YRequestBuilder, (string kind, string name)> + { + public YPlaylistRenameBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string kind, string name) tuple) + { + return new Dictionary { + { "uid", storage.User.Uid }, + { "kind", tuple.kind } + }; + } + + protected override HttpContent GetContent((string kind, string name) tuple) + { + return new FormUrlEncodedContent(new Dictionary { + { "value", tuple.name } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Playlist/YPlaylistRenameBuilder.cs b/YandexMusic.API/Requests/Playlist/YPlaylistRenameBuilder.cs new file mode 100644 index 0000000..1cd8550 --- /dev/null +++ b/YandexMusic.API/Requests/Playlist/YPlaylistRenameBuilder.cs @@ -0,0 +1,24 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Playlist +{ + [YApiRequest(WebRequestMethods.Http.Post, "users/{uid}/playlists/{kind}/delete")] + public class YPlaylistRemoveBuilder : YRequestBuilder + { + public YPlaylistRemoveBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string kind) + { + return new Dictionary { + { "uid", storage.User.Uid }, + { "kind", kind } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Queue/YGetQueueBuilder.cs b/YandexMusic.API/Requests/Queue/YGetQueueBuilder.cs new file mode 100644 index 0000000..d564833 --- /dev/null +++ b/YandexMusic.API/Requests/Queue/YGetQueueBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Queue; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Queue +{ + [YApiRequest(WebRequestMethods.Http.Get, "queues/{queueId}")] + public class YGetQueueBuilder : YRequestBuilder, string> + { + public YGetQueueBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string queueId) + { + return new Dictionary { + { "queueId", queueId } + }; + } + } +} diff --git a/YandexMusic.API/Requests/Queue/YQueueCreateBuilder.cs b/YandexMusic.API/Requests/Queue/YQueueCreateBuilder.cs new file mode 100644 index 0000000..aef7005 --- /dev/null +++ b/YandexMusic.API/Requests/Queue/YQueueCreateBuilder.cs @@ -0,0 +1,44 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Queue; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Queue +{ + [YApiRequest(WebRequestMethods.Http.Post, "queues")] + public class YQueueCreateBuilder : YRequestBuilder, YQueue> + { + public YQueueCreateBuilder(YandexMusicApi yandex, AuthStorage auth, string device = null) : base(yandex, auth) + { + if (device != null) + this.device = device; + } + + protected override HttpContent GetContent(YQueue queue) + { + JsonSerializerOptions settings = new() + { + Converters = { + new JsonStringEnumConverter() + }, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + return JsonContent.Create(queue, new MediaTypeHeaderValue("application/json"), settings); + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Add("X-Yandex-Music-Device", device); + } + } +} diff --git a/YandexMusic.API/Requests/Queue/YQueueUpdatePositionBuilder.cs b/YandexMusic.API/Requests/Queue/YQueueUpdatePositionBuilder.cs new file mode 100644 index 0000000..fe5e730 --- /dev/null +++ b/YandexMusic.API/Requests/Queue/YQueueUpdatePositionBuilder.cs @@ -0,0 +1,42 @@ +using System.Collections.Specialized; +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Queue; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Queue +{ + [YApiRequest(WebRequestMethods.Http.Post, "queues/{queueId}/update-position")] + public class YQueueUpdatePositionBuilder : YRequestBuilder, (string queueId, int currentIndex, bool isInteractive)> + { + public YQueueUpdatePositionBuilder(YandexMusicApi yandex, AuthStorage auth, string device = null) : base(yandex, auth) + { + if (device != null) + this.device = device; + } + + protected override Dictionary GetSubstitutions((string queueId, int currentIndex, bool isInteractive) tuple) + { + return new Dictionary { + { "queueId", tuple.queueId }, + }; + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Add("X-Yandex-Music-Device", device); + } + + protected override NameValueCollection GetQueryParams((string queueId, int currentIndex, bool isInteractive) tuple) + { + return new NameValueCollection { + { "currentIndex", tuple.currentIndex.ToString() }, + { "isInteractive", tuple.isInteractive.ToString().ToLower() } + }; + } + } +} diff --git a/YandexMusic.API/Requests/Queue/YQueuesListBuilder.cs b/YandexMusic.API/Requests/Queue/YQueuesListBuilder.cs new file mode 100644 index 0000000..898d64a --- /dev/null +++ b/YandexMusic.API/Requests/Queue/YQueuesListBuilder.cs @@ -0,0 +1,26 @@ +using System.Net; +using System.Net.Http.Headers; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Queue; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Queue +{ + [YApiRequest(WebRequestMethods.Http.Get, "queues")] + public class YQueuesListBuilder : YRequestBuilder, string> + { + public YQueuesListBuilder(YandexMusicApi yandex, AuthStorage auth, string device = null) : base(yandex, auth) + { + if (device != null) + this.device = device; + } + + protected override void SetCustomHeaders(HttpRequestHeaders headers) + { + headers.Add("X-Yandex-Music-Device", device); + } + } +} diff --git a/YandexMusic.API/Requests/Radio/YGetStationBuilder.cs b/YandexMusic.API/Requests/Radio/YGetStationBuilder.cs new file mode 100644 index 0000000..df3254e --- /dev/null +++ b/YandexMusic.API/Requests/Radio/YGetStationBuilder.cs @@ -0,0 +1,26 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Radio +{ + [YApiRequest(WebRequestMethods.Http.Get, "rotor/station/{type}:{tag}/info")] + public class YGetStationBuilder : YRequestBuilder>, (string type, string tag)> + { + public YGetStationBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string type, string tag) tuple) + { + return new Dictionary { + { "type", tuple.type }, + { "tag", tuple.tag } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Radio/YGetStationTracksBuilder.cs b/YandexMusic.API/Requests/Radio/YGetStationTracksBuilder.cs new file mode 100644 index 0000000..64cb14b --- /dev/null +++ b/YandexMusic.API/Requests/Radio/YGetStationTracksBuilder.cs @@ -0,0 +1,39 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Radio +{ + [YApiRequest(WebRequestMethods.Http.Get, "rotor/station/{type}:{tag}/tracks")] + public class YGetStationTracksBuilder : YRequestBuilder, (YStationDescription station, string prevTrackId)> + { + public YGetStationTracksBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((YStationDescription station, string prevTrackId) tuple) + { + return new Dictionary { + { "type", tuple.station.Id.Type }, + { "tag", tuple.station.Id.Tag }, + }; + } + + protected override NameValueCollection GetQueryParams((YStationDescription station, string prevTrackId) tuple) + { + NameValueCollection query = new() { + { "settings2", "true" } + }; + + if (!string.IsNullOrEmpty(tuple.prevTrackId)) + query.Add("queue", tuple.prevTrackId); + + return base.GetQueryParams(tuple); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Radio/YGetStationsBuilder.cs b/YandexMusic.API/Requests/Radio/YGetStationsBuilder.cs new file mode 100644 index 0000000..c41a393 --- /dev/null +++ b/YandexMusic.API/Requests/Radio/YGetStationsBuilder.cs @@ -0,0 +1,18 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Radio +{ + [YApiRequest(WebRequestMethods.Http.Get, "rotor/stations/list")] + public class YGetStationsBuilder : YRequestBuilder>, object> + { + public YGetStationsBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Radio/YGetStationsDashboardBuilder.cs b/YandexMusic.API/Requests/Radio/YGetStationsDashboardBuilder.cs new file mode 100644 index 0000000..8a7f152 --- /dev/null +++ b/YandexMusic.API/Requests/Radio/YGetStationsDashboardBuilder.cs @@ -0,0 +1,18 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Radio +{ + [YApiRequest(WebRequestMethods.Http.Get, "rotor/stations/dashboard")] + public class YGetStationsDashboardBuilder : YRequestBuilder, object> + { + public YGetStationsDashboardBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Radio/YSetSettings2Builder.cs b/YandexMusic.API/Requests/Radio/YSetSettings2Builder.cs new file mode 100644 index 0000000..a9b5ab6 --- /dev/null +++ b/YandexMusic.API/Requests/Radio/YSetSettings2Builder.cs @@ -0,0 +1,46 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Radio +{ + [YApiRequest(WebRequestMethods.Http.Post, "rotor/station/{type}:{tag}/settings2")] + public class YSetSettings2Builder : YRequestBuilder, (YStationDescription station, YStationSettings2 settings2)> + { + public YSetSettings2Builder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((YStationDescription station, YStationSettings2 settings2) tuple) + { + return new Dictionary { + { "type", tuple.station.Id.Type }, + { "tag", tuple.station.Id.Tag }, + }; + } + + protected override HttpContent GetContent((YStationDescription station, YStationSettings2 settings2) tuple) + { + JsonSerializerOptions settings = new() + { + Converters = { + new JsonStringEnumConverter() + }, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + return JsonContent.Create(tuple.settings2, new MediaTypeHeaderValue("application/json"), settings); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Radio/YSetStationFeedbackBuilder.cs b/YandexMusic.API/Requests/Radio/YSetStationFeedbackBuilder.cs new file mode 100644 index 0000000..0c532d2 --- /dev/null +++ b/YandexMusic.API/Requests/Radio/YSetStationFeedbackBuilder.cs @@ -0,0 +1,72 @@ +using System.Collections.Specialized; +using System.Net; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Radio; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Radio +{ + [YApiRequest(WebRequestMethods.Http.Post, "rotor/station/{type}:{tag}/feedback")] + public class YSetStationFeedbackBuilder : YRequestBuilder + { + public YSetStationFeedbackBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((YStationFeedbackType type, YStation station, YTrack track, string batchId, double totalPlayedSeconds) tuple) + { + return new Dictionary { + { "type", tuple.station.Station.Id.Type }, + { "tag", tuple.station.Station.Id.Tag } + }; + } + + protected override HttpContent GetContent((YStationFeedbackType type, YStation station, YTrack track, string batchId, double totalPlayedSeconds) tuple) + { + long timestamp = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); + + JsonSerializerOptions settings = new() + { + Converters = { + new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) + }, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + YStationFeedback feedBack = new() + { + Type = tuple.type, + From = tuple.station.Station.IdForFrom, + Timestamp = timestamp + }; + + if (tuple.track != null) + feedBack.TrackId = tuple.track.Id; + + if (tuple.totalPlayedSeconds > 0) + feedBack.TotalPlayedSeconds = tuple.totalPlayedSeconds; + + return JsonContent.Create(feedBack, new MediaTypeHeaderValue("application/json"), settings); ; + } + + protected override NameValueCollection GetQueryParams((YStationFeedbackType type, YStation station, YTrack track, string batchId, double totalPlayedSeconds) tuple) + { + NameValueCollection query = new(); + + if (!string.IsNullOrWhiteSpace(tuple.batchId)) + query.Add("batch-id", tuple.batchId); + + return query; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Search/YSearchBuilder.cs b/YandexMusic.API/Requests/Search/YSearchBuilder.cs new file mode 100644 index 0000000..d23926e --- /dev/null +++ b/YandexMusic.API/Requests/Search/YSearchBuilder.cs @@ -0,0 +1,29 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Search; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Search +{ + [YApiRequest(WebRequestMethods.Http.Get, "search")] + public class YSearchBuilder : YRequestBuilder, (string searchText, YSearchType searchType, int page, int pageSize)> + { + public YSearchBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams((string searchText, YSearchType searchType, int page, int pageSize) tuple) + { + return new NameValueCollection { + { "text", tuple.searchText }, + { "type", tuple.searchType.ToString() }, + { "page", tuple.page.ToString() }, + { "pageSize", tuple.pageSize.ToString() } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Search/YSearchSuggestBuilder.cs b/YandexMusic.API/Requests/Search/YSearchSuggestBuilder.cs new file mode 100644 index 0000000..ff566d7 --- /dev/null +++ b/YandexMusic.API/Requests/Search/YSearchSuggestBuilder.cs @@ -0,0 +1,26 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Search; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Search +{ + [YApiRequest(WebRequestMethods.Http.Get, "search/suggest")] + public class YSearchSuggestBuilder : YRequestBuilder, string> + { + public YSearchSuggestBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams(string searchText) + { + return new NameValueCollection { + { "part", searchText } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YGetTrackSimilarBuilder.cs b/YandexMusic.API/Requests/Track/YGetTrackSimilarBuilder.cs new file mode 100644 index 0000000..b7bb568 --- /dev/null +++ b/YandexMusic.API/Requests/Track/YGetTrackSimilarBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Track +{ + [YApiRequest(WebRequestMethods.Http.Get, "tracks/{trackId}/similar")] + public class YGetTrackSimilarBuilder : YRequestBuilder, string> + { + public YGetTrackSimilarBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string trackId) + { + return new Dictionary { + { "trackId", trackId } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YGetTrackSupplementBuilder.cs b/YandexMusic.API/Requests/Track/YGetTrackSupplementBuilder.cs new file mode 100644 index 0000000..efdb9cd --- /dev/null +++ b/YandexMusic.API/Requests/Track/YGetTrackSupplementBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Track +{ + [YApiRequest(WebRequestMethods.Http.Get, "tracks/{trackId}/supplement")] + public class YGetTrackSupplementBuilder : YRequestBuilder, string> + { + public YGetTrackSupplementBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string trackId) + { + return new Dictionary { + { "trackId", trackId } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YGetTracksBuilder.cs b/YandexMusic.API/Requests/Track/YGetTracksBuilder.cs new file mode 100644 index 0000000..07040e9 --- /dev/null +++ b/YandexMusic.API/Requests/Track/YGetTracksBuilder.cs @@ -0,0 +1,26 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Track +{ + [YApiRequest(WebRequestMethods.Http.Post, "tracks")] + public class YGetTracksBuilder : YRequestBuilder>, IEnumerable> + { + public YGetTracksBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent(IEnumerable trackIds) + { + return new FormUrlEncodedContent(new Dictionary { + { "track-ids", string.Join(",", trackIds) }, + { "with-positions", "true" } + }); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YSendTrackInfoBuilder.cs b/YandexMusic.API/Requests/Track/YSendTrackInfoBuilder.cs new file mode 100644 index 0000000..78f5a2a --- /dev/null +++ b/YandexMusic.API/Requests/Track/YSendTrackInfoBuilder.cs @@ -0,0 +1,38 @@ +using System.Globalization; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Track; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Track +{ + [YApiRequest(WebRequestMethods.Http.Post, "play-audio")] + public class YSendTrackInfoBuilder : YRequestBuilder + { + public YSendTrackInfoBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override HttpContent GetContent((YTrack track, string from, bool fromCache, string playId, string playlistId, double totalPlayedSeconds, double endPositionSeconds) tuple) + { + Dictionary formData = new() { + { "track_id", tuple.track.Id }, + { "from-cache", tuple.fromCache.ToString() }, + { "play_id", tuple.playId }, + { "uid", Storage.User.Uid }, + { "timestamp", DateTime.Now.ToString("o", CultureInfo.InvariantCulture) }, + { "client-now", DateTime.Now.ToString("o", CultureInfo.InvariantCulture) }, + { "album-id", tuple.track.Albums.FirstOrDefault()?.Id }, + { "from", tuple.from }, + { "playlist-id", tuple.playlistId }, + { "track-length-seconds", ((double)(tuple.track.DurationMs / 1000)).ToString(CultureInfo.InvariantCulture) }, + { "total-played-seconds", tuple.totalPlayedSeconds.ToString(CultureInfo.InvariantCulture) }, + { "end-position-seconds", tuple.endPositionSeconds.ToString(CultureInfo.InvariantCulture) }, + }; + + return new FormUrlEncodedContent(formData); + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs b/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs new file mode 100644 index 0000000..965920c --- /dev/null +++ b/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs @@ -0,0 +1,43 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Track +{ + [YRequest(WebRequestMethods.Http.Get, "{src}")] + public class YStorageDownloadFileBuilder : YRequestBuilder + { + public YStorageDownloadFileBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions(string src) + { + return new Dictionary { + { "src", src.Split('?')[0] } + }; + } + + protected override NameValueCollection GetQueryParams(string src) + { + NameValueCollection query = new() { + { "format", "json" } + }; + + src.Split('?')[1] + .Split('&') + .ToList() + .ForEach(p => + { + string[] param = p.Split('='); + query.Add(param[0], param[1]); + }); + + return query; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YTrackDownloadInfoBuilder.cs b/YandexMusic.API/Requests/Track/YTrackDownloadInfoBuilder.cs new file mode 100644 index 0000000..bbeea59 --- /dev/null +++ b/YandexMusic.API/Requests/Track/YTrackDownloadInfoBuilder.cs @@ -0,0 +1,32 @@ +using System.Collections.Specialized; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Track +{ + [YApiRequest(WebRequestMethods.Http.Get, "tracks/{trackKey}/download-info")] + public class YTrackDownloadInfoBuilder : YRequestBuilder>, (string trackKey, bool direct)> + { + public YTrackDownloadInfoBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string trackKey, bool direct) tuple) + { + return new Dictionary { + { "trackKey", tuple.trackKey } + }; + } + + protected override NameValueCollection GetQueryParams((string trackKey, bool direct) tuple) + { + return new NameValueCollection { + { "direct", tuple.direct.ToString() } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Ugc/YUgcGetUploadLinkBuilder.cs b/YandexMusic.API/Requests/Ugc/YUgcGetUploadLinkBuilder.cs new file mode 100644 index 0000000..acc4359 --- /dev/null +++ b/YandexMusic.API/Requests/Ugc/YUgcGetUploadLinkBuilder.cs @@ -0,0 +1,33 @@ +using System.Collections.Specialized; +using System.Globalization; +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Playlist; +using YandexMusic.API.Models.Ugc; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Ugc +{ + [YWebApiRequest(WebRequestMethods.Http.Get, "handlers/ugc-upload.jsx")] + public class YUgcGetUploadLinkBuilder : YRequestBuilder + { + private static readonly Lazy Random = new(() => new Random()); + + public YUgcGetUploadLinkBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override NameValueCollection GetQueryParams((YPlaylist playlist, string fileName) tuple) + { + return new NameValueCollection { + { "filename", tuple.fileName }, + { "kind", tuple.playlist.Kind }, + { "visibility", "private" }, + { "external-domain", "music.yandex.ru" }, + { "ncrnd", Random.Value.NextDouble().ToString(CultureInfo.InvariantCulture) } + }; + } + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs b/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs new file mode 100644 index 0000000..1d87a54 --- /dev/null +++ b/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs @@ -0,0 +1,32 @@ +using System.Net; + +using YandexMusic.API.Common; +using YandexMusic.API.Models.Common; +using YandexMusic.API.Requests.Common; +using YandexMusic.API.Requests.Common.Attributes; + +namespace YandexMusic.API.Requests.Ugc +{ + [YRequest(WebRequestMethods.Http.Post, "{postTargetLink}")] + public class YUgcUploadBuilder : YRequestBuilder, (string postTargetLink, byte[] fileBytes)> + { + public YUgcUploadBuilder(YandexMusicApi yandex, AuthStorage auth) : base(yandex, auth) + { + } + + protected override Dictionary GetSubstitutions((string postTargetLink, byte[] fileBytes) tuple) + { + return new Dictionary { + { "postTargetLink", tuple.postTargetLink } + }; + } + + protected override HttpContent GetContent((string postTargetLink, byte[] fileBytes) tuple) + { + return new MultipartFormDataContent { + { new ByteArrayContent(tuple.fileBytes), "file" } + }; + } + + } +} \ No newline at end of file diff --git a/YandexMusic.API/YandexMusic.API.csproj b/YandexMusic.API/YandexMusic.API.csproj new file mode 100644 index 0000000..57c3c05 --- /dev/null +++ b/YandexMusic.API/YandexMusic.API.csproj @@ -0,0 +1,13 @@ + + + net10.0 + YandexMusic.API + 0.0.1 + FrigaT + Асинхронная библиотека для неофициального API Яндекс Музыки. + yandex;music;api;async + last + enable + enable + + \ No newline at end of file diff --git a/YandexMusic.API/YandexMusicApi.cs b/YandexMusic.API/YandexMusicApi.cs new file mode 100644 index 0000000..2428767 --- /dev/null +++ b/YandexMusic.API/YandexMusicApi.cs @@ -0,0 +1,41 @@ +namespace YandexMusic.API; + +/// Главный класс API Яндекс Музыки. Предоставляет доступ ко всем веткам API. +public class YandexMusicApi +{ + /// API для работы с альбомами. + public YAlbumAPI Album { get; internal set; } = null!; + /// API для работы с исполнителями. + public YArtistAPI Artist { get; internal set; } = null!; + /// API для работы с лейблами. + public YLabelAPI Label { get; internal set; } = null!; + /// API для работы с главной страницей (лендингом). + public YLandingAPI Landing { get; internal set; } = null!; + /// API для работы с библиотекой (лайки, дизлайки). + public YLibraryAPI Library { get; internal set; } = null!; + /// API для работы с плейлистами. + public YPlaylistAPI Playlist { get; internal set; } = null!; + /// API для работы с закреплёнными объектами. + public YPinsAPI Pins { get; internal set; } = null!; + /// API для работы с радио. + public YRadioAPI Radio { get; internal set; } = null!; + /// API для поиска. + public YSearchAPI Search { get; internal set; } = null!; + /// API для работы с треками. + public YTrackAPI Track { get; internal set; } = null!; + /// API для работы с очередями. + public YQueueAPI Queue { get; internal set; } = null!; + /// API для работы с пользователем и авторизацией. + public YUserAPI User { get; internal set; } = null!; + /// API для загрузки пользовательского контента. + public YUgcAPI UserGeneratedContent { get; internal set; } = null!; + /// API для работы с протоколом Ynison (WebSocket). + public YYnisonAPI Ynison { get; internal set; } = null!; + + /// Создаёт экземпляр API с инициализацией всех подсистем. + public YandexMusicApi() + { + foreach (var property in GetType().GetProperties()) + property.SetValue(this, Activator.CreateInstance(property.PropertyType, this)); + } +} \ No newline at end of file diff --git a/YandexMusic.slnx b/YandexMusic.slnx new file mode 100644 index 0000000..9e4b8a0 --- /dev/null +++ b/YandexMusic.slnx @@ -0,0 +1,3 @@ + + +