├── ExampleConsoleApp ├── .gitignore ├── appsettings.json ├── ExampleConsoleApp.csproj ├── Startup.cs ├── Program.cs └── SpotifyAuthentication.cs ├── SpotifyIcon.png ├── SpotifyWebApi ├── Api │ ├── Follow │ │ └── IFollowApi.cs │ ├── UserLibrary │ │ └── IUserLibraryApi.cs │ ├── Personalization │ │ └── IPersonalizationApi.cs │ ├── UserProfile │ │ ├── IUserProfileApi.cs │ │ └── UserProfileApi.cs │ ├── Search │ │ └── ISearchApi.cs │ ├── Track │ │ ├── ITrackApi.cs │ │ └── TrackApi.cs │ ├── Browse │ │ ├── BrowseApi.cs │ │ └── IBrowseApi.cs │ ├── Album │ │ ├── IAlbumApi.cs │ │ └── AlbumApi.cs │ ├── Artist │ │ ├── IArtistApi.cs │ │ └── ArtistApi.cs │ ├── BaseApi.cs │ ├── Player │ │ ├── PlayerApi.cs │ │ └── IPlayerApi.cs │ └── Playlist │ │ ├── PlaylistApi.cs │ │ └── IPlaylistApi.cs ├── Properties │ └── AssemblyInfo.cs ├── FodyWeavers.xml ├── Model │ ├── Cursor.cs │ ├── DeviceList.cs │ ├── List │ │ ├── MultipleAlbums.cs │ │ ├── MultipleTracks.cs │ │ ├── MultipleArtists.cs │ │ └── MultiplePlaylists.cs │ ├── Enum │ │ ├── ContextType.cs │ │ ├── RepeatState.cs │ │ ├── AlbumType.cs │ │ ├── EnumExtensions.cs │ │ └── Scopes.cs │ ├── Copyright.cs │ ├── Error.cs │ ├── Followers.cs │ ├── Exception │ │ ├── NotFoundException.cs │ │ ├── ForbiddenException.cs │ │ ├── BadRequestException.cs │ │ ├── UserNotPremiumException.cs │ │ ├── InternalServerErrorException.cs │ │ ├── InvalidUriException.cs │ │ ├── ServiceUnavailableException.cs │ │ ├── TooManyRequestsException.cs │ │ ├── BadGatewayException.cs │ │ └── ValidationException.cs │ ├── Auth │ │ ├── TokenAuthenticationType.cs │ │ └── Token.cs │ ├── PlaylistTracksRef.cs │ ├── Image.cs │ ├── PlaylistCreate.cs │ ├── Search │ │ ├── SearchResult.cs │ │ └── ApiSearchResult.cs │ ├── Uri │ │ ├── UriType.cs │ │ └── SpotifyUri.cs │ ├── PlaylistTrack.cs │ ├── Context.cs │ ├── LinkedFrom.cs │ ├── CurrentlyPlaying.cs │ ├── SimpleArtist.cs │ ├── CursorPaging.cs │ ├── Paging.cs │ ├── Device.cs │ ├── WebResponse.cs │ ├── PublicUser.cs │ ├── FullArtist.cs │ ├── SimpleAlbum.cs │ ├── CurrentlyPlayingContext.cs │ ├── PrivateUser.cs │ ├── SimplePlaylist.cs │ ├── SimpleTrack.cs │ ├── FullPlaylist.cs │ ├── FullAlbum.cs │ └── FullTrack.cs ├── Auth │ ├── AuthCode │ │ └── AccessTokenRequest.cs │ ├── AuthParameters.cs │ ├── ImplicitGrant.cs │ └── ClientCredentials.cs ├── Business │ ├── ApiHelper.cs │ ├── HelperExtensions.cs │ ├── Validation.cs │ └── ApiClient.cs ├── SpotifyWebApi.cs ├── ISpotifyWebApi.cs └── SpotifyWebApi.csproj ├── Test ├── Api │ ├── ArtistTest.cs │ ├── PlaylistTest.cs │ ├── BaseApiTest.cs │ └── AlbumTest.cs ├── Auth │ └── AuthCode │ │ └── AuthorizationCodeTests.cs ├── Business │ ├── WebRequestHelperTest.cs │ └── ValidationTest.cs ├── Test.csproj ├── TestBase.cs ├── TestData.cs └── Model │ └── SpotifyUriTest.cs ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── push.yml │ └── release.yml ├── LICENSE.md ├── Solution.ruleset ├── SpotifyWebApi.sln ├── .gitattributes ├── README.md └── .gitignore /ExampleConsoleApp/.gitignore: -------------------------------------------------------------------------------- 1 | appsettings.development.json -------------------------------------------------------------------------------- /SpotifyIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pimmerks/SpotifyWebApi/HEAD/SpotifyIcon.png -------------------------------------------------------------------------------- /ExampleConsoleApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Spotify":{ 3 | "ClientId": "", 4 | "ClientSecret": "", 5 | "RedirectUri": "" 6 | } 7 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Follow/IFollowApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Follow 2 | { 3 | /// 4 | /// The follow api. 5 | /// 6 | public interface IFollowApi 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Test/Api/ArtistTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SpotifyWebApiTest.Api 6 | { 7 | class ArtistTest 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/UserLibrary/IUserLibraryApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.UserLibrary 2 | { 3 | /// 4 | /// The user library api. 5 | /// 6 | public interface IUserLibraryApi 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Personalization/IPersonalizationApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Personalization 2 | { 3 | /// 4 | /// The personalization api. 5 | /// 6 | public interface IPersonalizationApi 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Internals only visible for testing purpose. 2 | 3 | using System.Runtime.CompilerServices; 4 | using Fody; 5 | 6 | [assembly: InternalsVisibleTo("SpotifyWebApiTest")] 7 | [assembly: ConfigureAwait(false)] 8 | -------------------------------------------------------------------------------- /SpotifyWebApi/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Cursor.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public class Cursor 9 | { 10 | /// 11 | /// Gets or sets the after. 12 | /// 13 | [JsonProperty("after")] 14 | public string After { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/DeviceList.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The device list object. 8 | /// 9 | internal class DeviceList 10 | { 11 | /// 12 | /// A list of devices. 13 | /// 14 | [JsonProperty("devices")] 15 | public List Devices { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/List/MultipleAlbums.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.List 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The . 8 | /// 9 | public class MultipleAlbums 10 | { 11 | /// 12 | /// Gets or sets the albums. 13 | /// 14 | [JsonProperty("albums")] 15 | public IList Albums { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/List/MultipleTracks.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.List 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The . 8 | /// 9 | public class MultipleTracks 10 | { 11 | /// 12 | /// Gets or sets the tracks. 13 | /// 14 | [JsonProperty("tracks")] 15 | public IList Tracks { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/List/MultipleArtists.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.List 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The . 8 | /// 9 | public class MultipleArtists 10 | { 11 | /// 12 | /// Gets or sets the artists. 13 | /// 14 | [JsonProperty("artists")] 15 | public IList Artists { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Enum/ContextType.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Enum 2 | { 3 | /// 4 | /// The . 5 | /// 6 | public enum ContextType 7 | { 8 | /// 9 | /// TODO 10 | /// 11 | Album, 12 | 13 | /// 14 | /// TODO 15 | /// 16 | Artist, 17 | 18 | /// 19 | /// TODO 20 | /// 21 | Playlist, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/List/MultiplePlaylists.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.List 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The . 8 | /// 9 | public class MultiplePlaylists 10 | { 11 | /// 12 | /// Gets or sets the playlists. 13 | /// 14 | [JsonProperty("playlists")] 15 | public IList Playlists { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Copyright.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// Class Copyright. 7 | /// 8 | public class Copyright 9 | { 10 | /// 11 | /// Gets or sets the text. 12 | /// 13 | [JsonProperty("text")] 14 | public string Text { get; set; } 15 | 16 | /// 17 | /// Gets or sets the type. 18 | /// 19 | [JsonProperty("type")] 20 | public string Type { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Enum/RepeatState.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Enum 2 | { 3 | /// 4 | /// The . 5 | /// 6 | public enum RepeatState 7 | { 8 | /// 9 | /// Will repeat the current track. 10 | /// 11 | Track, 12 | 13 | /// 14 | /// Will repeat the current context. 15 | /// 16 | Context, 17 | 18 | /// 19 | /// Will turn repeat off. 20 | /// 21 | Off 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Error.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The class. 7 | /// 8 | public class Error 9 | { 10 | /// 11 | /// Gets or sets the status. 12 | /// 13 | [JsonProperty("status")] 14 | public int Status { get; set; } 15 | 16 | /// 17 | /// Gets or sets the message. 18 | /// 19 | [JsonProperty("message")] 20 | public string Message { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Followers.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The class. 7 | /// 8 | public class Followers 9 | { 10 | /// 11 | /// Gets or sets the href. 12 | /// 13 | [JsonProperty("href")] 14 | public string Href { get; set; } 15 | 16 | /// 17 | /// Gets or sets the total. 18 | /// 19 | [JsonProperty("total")] 20 | public int Total { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Test/Auth/AuthCode/AuthorizationCodeTests.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest.Auth.AuthCode 2 | { 3 | using SpotifyWebApi.Auth; 4 | using SpotifyWebApi.Model.Enum; 5 | using Xunit; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class AuthorizationCodeTests 11 | { 12 | /// 13 | /// Gets the URL test. 14 | /// 15 | [Fact] 16 | public void GetUrlTest() 17 | { 18 | // TODO: Add test. 19 | Assert.True(true); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | ```csharp 25 | // The code you used while getting this bug 26 | ``` 27 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public class NotFoundException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The message that describes the error. 14 | public NotFoundException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/ForbiddenException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// The server understood the request, but is refusing to fulfill it. 7 | /// 8 | public class ForbiddenException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The exception message. 14 | public ForbiddenException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/BadRequestException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// The request could not be understood by the server due to malformed syntax. 7 | /// 8 | public class BadRequestException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The exception message. 14 | public BadRequestException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/UserNotPremiumException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public class UserNotPremiumException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The message that describes the error. 14 | public UserNotPremiumException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/InternalServerErrorException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// An error you should (according to Spotify) never recieve. 7 | /// 8 | public class InternalServerErrorException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The exception message. 14 | public InternalServerErrorException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Auth/TokenAuthenticationType.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Auth 2 | { 3 | /// 4 | /// Enum defining token types. 5 | /// 6 | public enum TokenAuthenticationType 7 | { 8 | /// 9 | /// Token generated using Authorization Code. 10 | /// 11 | AuthorizationCode = 0, 12 | 13 | /// 14 | /// Token generated using Client Credentials. 15 | /// 16 | ClientCredentials = 1, 17 | 18 | /// 19 | /// Token generated using Implicit Grant. 20 | /// 21 | ImplicitGrant = 2 22 | } 23 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/InvalidUriException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// Exception that gets thrown when creating a spotify uri from a non valid string. 7 | /// 8 | public class InvalidUriException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The message that describes the error. 14 | public InvalidUriException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Enum/AlbumType.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Enum 2 | { 3 | using System; 4 | 5 | /// 6 | /// Enum AlbumType 7 | /// 8 | [Flags] 9 | public enum AlbumType 10 | { 11 | /// 12 | /// The album 13 | /// 14 | Album = 1, 15 | 16 | /// 17 | /// The single 18 | /// 19 | Single = 2, 20 | 21 | /// 22 | /// The appears on 23 | /// 24 | AppearsOn = 4, 25 | 26 | /// 27 | /// The compilation 28 | /// 29 | Compilation = 8 30 | } 31 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/PlaylistTracksRef.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The class. 7 | /// 8 | public class PlaylistTracksRef 9 | { 10 | /// 11 | /// A link to the Web API endpoint where full details of the playlist's tracks can be retrieved. 12 | /// 13 | [JsonProperty("href")] 14 | public string Href { get; set; } 15 | 16 | /// 17 | /// Number of tracks in the playlist. 18 | /// 19 | [JsonProperty("total")] 20 | public int? Total { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature-request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/ServiceUnavailableException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// The server is currently unable to handle the request due to a temporary condition which will be alleviated after some delay. 7 | /// 8 | public class ServiceUnavailableException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The exception message. 14 | public ServiceUnavailableException(string message) 15 | : base(message) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/TooManyRequestsException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | 5 | /// 6 | /// Rate limiting has been applied. 7 | /// See here. 8 | /// 9 | public class TooManyRequestsException : Exception 10 | { 11 | /// 12 | /// Initializes a new instance of the class. 13 | /// 14 | /// The exception message. 15 | public TooManyRequestsException(string message) 16 | : base(message) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/BadGatewayException.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Exception 2 | { 3 | using System; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Converters; 6 | 7 | /// 8 | /// The server was acting as a gateway or proxy and received an invalid response from the upstream server. 9 | /// 10 | public class BadGatewayException : Exception 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The exception message. 16 | public BadGatewayException(string message) 17 | : base(message) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Image.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The class. 7 | /// 8 | public class Image 9 | { 10 | /// 11 | /// Gets or sets the height. 12 | /// 13 | [JsonProperty("height")] 14 | public int? Height { get; set; } 15 | 16 | /// 17 | /// Gets or sets the URL. 18 | /// 19 | [JsonProperty("url")] 20 | public string Url { get; set; } 21 | 22 | /// 23 | /// Gets or sets the width. 24 | /// 25 | [JsonProperty("width")] 26 | public int? Width { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Exception/ValidationException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) companyPlaceholder. All rights reserved. 3 | // 4 | 5 | namespace SpotifyWebApi.Model.Exception 6 | { 7 | using System; 8 | 9 | /// 10 | /// The . 11 | /// 12 | public class ValidationException : Exception 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The validation message. 18 | public ValidationException(string message) 19 | : base(message) 20 | { 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/PlaylistCreate.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The playlist create object 7 | /// 8 | public class PlaylistCreate 9 | { 10 | /// 11 | /// The name of the playlist 12 | /// 13 | [JsonProperty("name")] 14 | public string Name { get; set; } 15 | 16 | /// 17 | /// The playlist description. 18 | /// 19 | [JsonProperty("description")] 20 | public string Description { get; set; } 21 | 22 | /// 23 | /// The playlist visibility. 24 | /// 25 | [JsonProperty("public")] 26 | public bool Public { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SpotifyWebApi/Auth/AuthCode/AccessTokenRequest.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Auth.AuthCode 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The class. 7 | /// 8 | internal class AccessTokenRequest 9 | { 10 | /// 11 | /// The grant type. 12 | /// 13 | [JsonProperty("grant_type")] 14 | public string GrantType { get; set; } 15 | 16 | /// 17 | /// The code. 18 | /// 19 | [JsonProperty("code")] 20 | public string Code { get; set; } 21 | 22 | /// 23 | /// The redirect uri. 24 | /// 25 | [JsonProperty("redirect_uri")] 26 | public string RedirectUri { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Search/SearchResult.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Search 2 | { 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// A search result of a search query. 7 | /// 8 | public class SearchResult 9 | { 10 | /// 11 | /// The albums. 12 | /// 13 | public List Albums { get; set; } 14 | 15 | /// 16 | /// The artists. 17 | /// 18 | public List Artists { get; set; } 19 | 20 | /// 21 | /// The tracks. 22 | /// 23 | public List Tracks { get; set; } 24 | 25 | /// 26 | /// The playlists. 27 | /// 28 | public List Playlists { get; set; } 29 | } 30 | } -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: Push 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: 3.1.101 18 | 19 | - name: Restore packages 20 | uses: actions/cache@v1 21 | with: 22 | path: ~/.nuget/packages 23 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} 24 | restore-keys: | 25 | ${{ runner.os }}-nuget- 26 | 27 | - name: Restore packages 28 | run: dotnet restore --use-lock-file --locked-mode 29 | 30 | - name: Build with dotnet 31 | run: dotnet build --configuration Release 32 | 33 | - name: Test with dotnet 34 | run: dotnet test --configuration Release 35 | -------------------------------------------------------------------------------- /Test/Business/WebRequestHelperTest.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest.Business 2 | { 3 | using System; 4 | using SpotifyWebApi.Business; 5 | using SpotifyWebApi.Model; 6 | using SpotifyWebApi.Model.Auth; 7 | using Xunit; 8 | 9 | /// 10 | /// The . 11 | /// 12 | public class WebRequestHelperTest 13 | { 14 | 15 | /// 16 | /// TODO 17 | /// 18 | [Fact] 19 | public void TestGet() 20 | { 21 | // TODO: Add test. 22 | Assert.True(true); 23 | } 24 | 25 | /// 26 | /// TODO 27 | /// 28 | [Fact] 29 | public void TestCreateUpdateDeletePlaylist() 30 | { 31 | // TODO: Add test. 32 | Assert.True(true); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ExampleConsoleApp/ExampleConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | Always 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Uri/UriType.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Uri 2 | { 3 | using System.Runtime.Serialization; 4 | 5 | /// 6 | /// Enum UriType 7 | /// 8 | [DataContract] 9 | public enum UriType 10 | { 11 | /// 12 | /// The user. 13 | /// 14 | [EnumMember] 15 | User, 16 | 17 | /// 18 | /// The track. 19 | /// 20 | [EnumMember] 21 | Track, 22 | 23 | /// 24 | /// The artist. 25 | /// 26 | [EnumMember] 27 | Artist, 28 | 29 | /// 30 | /// The album. 31 | /// 32 | [EnumMember] 33 | Album, 34 | 35 | /// 36 | /// The playlist. 37 | /// 38 | [EnumMember] 39 | Playlist, 40 | 41 | /// 42 | /// A local uri. 43 | /// 44 | [EnumMember] 45 | Local, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/PlaylistTrack.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The class. 8 | /// 9 | public class PlaylistTrack 10 | { 11 | /// 12 | /// Gets or sets the added at. 13 | /// 14 | [JsonProperty("added_at")] 15 | public DateTime AddedAt { get; set; } 16 | 17 | /// 18 | /// Gets or sets the added by. 19 | /// 20 | [JsonProperty("added_by")] 21 | public PublicUser AddedBy { get; set; } 22 | 23 | /// 24 | /// Gets or sets a value indicating whether this instance is local. 25 | /// 26 | [JsonProperty("is_local")] 27 | public bool IsLocal { get; set; } 28 | 29 | /// 30 | /// Gets or sets the track. 31 | /// 32 | [JsonProperty("track")] 33 | public FullTrack Track { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pim Merks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Test/Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | SpotifyWebApiTest 7 | SpotifyWebApiTest 8 | ..\Solution.ruleset 9 | bin\Debug\$(TargetFramework)\SpotifyWebApiTest.xml 10 | 11 | 12 | 13 | 14 | NU5105 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ExampleConsoleApp/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace ExampleConsoleApp 2 | { 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | class Startup 9 | { 10 | static async Task Main(string[] args) 11 | { 12 | var serviceCollection = new ServiceCollection(); 13 | 14 | var builder = new ConfigurationBuilder() 15 | .SetBasePath(Directory.GetCurrentDirectory()) 16 | .AddJsonFile("appsettings.json", false) 17 | .AddJsonFile("appsettings.development.json", true); 18 | 19 | IConfiguration configuration = builder.Build(); 20 | 21 | serviceCollection.AddScoped(_ => configuration); 22 | serviceCollection.AddScoped(); 23 | serviceCollection.AddScoped(); 24 | 25 | var serviceProvider = serviceCollection.BuildServiceProvider(); 26 | 27 | var p = serviceProvider.GetRequiredService(); 28 | await p.Run(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Test/TestBase.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest 2 | { 3 | using SpotifyWebApi; 4 | using Xunit; 5 | using Xunit.Abstractions; 6 | 7 | /// 8 | /// The . 9 | /// 10 | public class TestBase : IClassFixture 11 | { 12 | /// 13 | /// An instance of test data. 14 | /// 15 | protected readonly TestData TestData; 16 | 17 | /// 18 | /// An instance of to provide logging while testing. 19 | /// 20 | protected readonly ITestOutputHelper Output; 21 | 22 | /// 23 | /// Gets the api from the test data. 24 | /// 25 | protected ISpotifyWebApi Api { get; } 26 | 27 | /// 28 | /// The base test class. 29 | /// 30 | public TestBase(TestData testData, ITestOutputHelper output) 31 | { 32 | this.TestData = testData; 33 | this.Output = output; 34 | this.Api = new SpotifyWebApi(this.TestData.ClientCredentialsToken); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Enum/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Enum 2 | { 3 | using System; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public static class EnumExtensions 9 | { 10 | /// 11 | /// Ases the string. 12 | /// 13 | /// Type of the album. 14 | /// System.String. 15 | /// albumType - null 16 | public static string AsString(this AlbumType albumType) 17 | { 18 | switch (albumType) 19 | { 20 | case AlbumType.Album: 21 | return "album"; 22 | case AlbumType.Single: 23 | return "single"; 24 | case AlbumType.AppearsOn: 25 | return "appears_on"; 26 | case AlbumType.Compilation: 27 | return "compilation"; 28 | default: 29 | throw new ArgumentOutOfRangeException(nameof(albumType), albumType, null); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Search/ApiSearchResult.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Search 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The internal . 7 | /// 8 | public class ApiSearchResult 9 | { 10 | /// 11 | /// The albums. 12 | /// 13 | [JsonProperty("albums", NullValueHandling = NullValueHandling.Ignore)] 14 | public Paging Albums { get; set; } 15 | 16 | /// 17 | /// The artists. 18 | /// 19 | [JsonProperty("artists", NullValueHandling = NullValueHandling.Ignore)] 20 | public Paging Artists { get; set; } 21 | 22 | /// 23 | /// The tracks. 24 | /// 25 | [JsonProperty("tracks", NullValueHandling = NullValueHandling.Ignore)] 26 | public Paging Tracks { get; set; } 27 | 28 | /// 29 | /// The playlists. 30 | /// 31 | [JsonProperty("playlists", NullValueHandling = NullValueHandling.Ignore)] 32 | public Paging Playlists { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Context.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Enum; 5 | using Newtonsoft.Json; 6 | 7 | /// 8 | /// The . 9 | /// 10 | public class Context 11 | { 12 | /// 13 | /// Gets or sets the uri of the context. 14 | /// 15 | [JsonProperty("uri")] 16 | public string Uri { get; set; } 17 | 18 | /// 19 | /// Gets or sets the href of the context, or null if not available. 20 | /// 21 | [JsonProperty("href")] 22 | public string Href { get; set; } 23 | 24 | /// 25 | /// Gets or sets the external urls of the context, or null if not available. 26 | /// 27 | [JsonProperty("external_urls")] 28 | public Dictionary ExternalUrls { get; set; } 29 | 30 | /// 31 | /// Gets or sets the object type of the item's context. Can be one of "album", "artist" or "playlist". 32 | /// 33 | [JsonProperty("type")] 34 | public ContextType Type { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SpotifyWebApi/Auth/AuthParameters.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Auth 2 | { 3 | using Model.Enum; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public class AuthParameters 9 | { 10 | /// 11 | /// Gets or sets the authentication client id. 12 | /// 13 | public string ClientId { get; set; } 14 | 15 | /// 16 | /// Gets or sets the authentication client secret. 17 | /// Note that this is only used for the flow. 18 | /// 19 | public string ClientSecret { get; set; } 20 | 21 | /// 22 | /// Gets or sets the authentication redirect uri. 23 | /// 24 | public string RedirectUri { get; set; } 25 | 26 | /// 27 | /// Gets or sets a value indicating wheter to show the login screen every time the user is requested to login. 28 | /// 29 | public bool ShowDialog { get; set; } 30 | 31 | /// 32 | /// Gets or sets the authentication scopes. 33 | /// 34 | public string Scopes { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/LinkedFrom.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class LinkedFrom 11 | { 12 | /// 13 | /// Gets or sets the external urls. 14 | /// 15 | [JsonProperty("external_urls")] 16 | public Dictionary ExternalUrls { get; set; } 17 | 18 | /// 19 | /// Gets or sets the href. 20 | /// 21 | [JsonProperty("href")] 22 | public string Href { get; set; } 23 | 24 | /// 25 | /// Gets or sets the identifier. 26 | /// 27 | [JsonProperty("id")] 28 | public string Id { get; set; } 29 | 30 | /// 31 | /// Gets or sets the type. 32 | /// 33 | [JsonProperty("type")] 34 | public string Type { get; set; } 35 | 36 | /// 37 | /// Gets or sets the URI. 38 | /// 39 | [JsonProperty("uri")] 40 | public SpotifyUri Uri { get; set; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/CurrentlyPlaying.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public class CurrentlyPlaying 9 | { 10 | /// 11 | /// Gets or sets a Context Object. Can be null. 12 | /// 13 | [JsonProperty("Context")] 14 | public Context Context { get; set; } 15 | 16 | /// 17 | /// Gets or sets unix Millisecond Timestamp when data was fetched 18 | /// 19 | [JsonProperty("timestamp")] 20 | public long Timestamp { get; set; } 21 | 22 | /// 23 | /// Gets or sets progress into the currently playing track. Can be null. 24 | /// 25 | [JsonProperty("progress_ms")] 26 | public int ProgressMs { get; set; } 27 | 28 | /// 29 | /// Gets or sets if something is currently playing. 30 | /// 31 | [JsonProperty("is_playing")] 32 | public bool IsPlaying { get; set; } 33 | 34 | /// 35 | /// Gets or sets the currently playing track. Can be null. 36 | /// 37 | [JsonProperty("item")] 38 | public FullTrack Item { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/SimpleArtist.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class SimpleArtist 11 | { 12 | /// 13 | /// Gets or sets the external urls. 14 | /// 15 | [JsonProperty("external_urls")] 16 | public Dictionary ExternalUrls { get; set; } 17 | 18 | /// 19 | /// Gets or sets the href. 20 | /// 21 | [JsonProperty("href")] 22 | public string Href { get; set; } 23 | 24 | /// 25 | /// Gets or sets the identifier. 26 | /// 27 | [JsonProperty("id")] 28 | public string Id { get; set; } 29 | 30 | /// 31 | /// Gets or sets the name. 32 | /// 33 | [JsonProperty("name")] 34 | public string Name { get; set; } 35 | 36 | /// 37 | /// Gets or sets the type. 38 | /// 39 | [JsonProperty("type")] 40 | public string Type { get; set; } 41 | 42 | /// 43 | /// Gets or sets the URI. 44 | /// 45 | [JsonProperty("uri")] 46 | public SpotifyUri Uri { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/UserProfile/IUserProfileApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.UserProfile 2 | { 3 | using System.Threading.Tasks; 4 | using Model; 5 | using Model.Enum; 6 | using Model.Uri; 7 | 8 | /// 9 | /// The user profile api. 10 | /// 11 | public interface IUserProfileApi 12 | { 13 | /// 14 | /// Get detailed profile information about the current user (including the current user’s username). 15 | /// 16 | /// A object representing the current user. 17 | /// 18 | /// A valid access token from the Spotify Accounts service: see the Web API Authorization Guide for details. 19 | /// The access token must have been issued on behalf of the current user. 20 | /// Reading the user’s email address requires the scope; 21 | /// reading country and product subscription level requires the scope. 22 | /// 23 | Task GetMe(); 24 | 25 | /// 26 | /// Get public profile information about a Spotify user. 27 | /// 28 | /// The user’s Spotify user ID. 29 | /// The requested user. 30 | Task GetUser(SpotifyUri userUri); 31 | } 32 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Search/ISearchApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Search 2 | { 3 | using System.Threading.Tasks; 4 | using Model.Enum; 5 | using Model.Search; 6 | 7 | /// 8 | /// The search api. 9 | /// 10 | public interface ISearchApi 11 | { 12 | /// 13 | /// Get Spotify catalog information about artists, albums, tracks or playlists that match a keyword string. 14 | /// Note: There is only support for 20 results per category. 15 | /// TODO: Add support for more than 20 results per category. 16 | /// 17 | /// The search query's keywords (and optional field filters and operators). 18 | /// For more info see: https://developer.spotify.com/web-api/search-item/ 19 | /// A list of item types to search across. 20 | /// Optional. An ISO 3166-1 alpha-2 country code. 21 | /// Optional. The starting offset of the search. 22 | /// The maximum results to return per category. 23 | /// A list of s. 24 | Task Search( 25 | string query, 26 | string searchType, 27 | string market = "", 28 | int offset = 0, 29 | int resultLimit = 20); 30 | } 31 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/CursorPaging.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The class. 8 | /// 9 | /// The type of 10 | public class CursorPaging 11 | { 12 | /// 13 | /// Gets or sets the href. 14 | /// 15 | [JsonProperty("href")] 16 | public string Href { get; set; } 17 | 18 | /// 19 | /// Gets or sets the items. 20 | /// 21 | [JsonProperty("items")] 22 | public List Items { get; set; } 23 | 24 | /// 25 | /// Gets or sets the limit. 26 | /// 27 | [JsonProperty("limit")] 28 | public int Limit { get; set; } 29 | 30 | /// 31 | /// Gets or sets the next. 32 | /// 33 | [JsonProperty("next")] 34 | public string Next { get; set; } 35 | 36 | /// 37 | /// Gets or sets the cursors. 38 | /// 39 | [JsonProperty("cursors")] 40 | public Cursor Cursors { get; set; } 41 | 42 | /// 43 | /// Gets or sets the total. 44 | /// 45 | [JsonProperty("total")] 46 | public int Total { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Track/ITrackApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Track 2 | { 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Model; 7 | using Model.Uri; 8 | 9 | /// 10 | /// The track api. 11 | /// 12 | public interface ITrackApi 13 | { 14 | /// 15 | /// Get Spotify catalog information for a single track identified by its 16 | /// 17 | /// The for the track. 18 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 19 | /// A object. 20 | Task GetTrack(SpotifyUri uri, string market = null); 21 | 22 | /// 23 | /// Get Spotify catalog information for multiple tracks based on their s. 24 | /// 25 | /// A list of s. Note a maximum of 50 tracks is allowed. 26 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 27 | /// A list of objects. 28 | Task> GetTracks(IList uris, string market = null); 29 | } 30 | } -------------------------------------------------------------------------------- /Solution.ruleset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SpotifyWebApi/Business/ApiHelper.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Business 2 | { 3 | using System; 4 | using System.Net; 5 | using Model.Auth; 6 | 7 | /// 8 | /// The . 9 | /// 10 | internal static class ApiHelper 11 | { 12 | /// 13 | /// The base uri for all api requests. 14 | /// 15 | public const string BaseUri = "https://api.spotify.com/v1"; 16 | 17 | /// 18 | /// Creates a new from the specified . 19 | /// 20 | /// The uri of the request. 21 | /// A new from the specified uri. 22 | public static HttpWebRequest CreateRequest(Uri uri) 23 | { 24 | var request = WebRequest.CreateHttp(uri); 25 | request.ContentType = "application/x-www-form-urlencoded"; 26 | return request; 27 | } 28 | 29 | /// 30 | /// Encodes a string to Base64. 31 | /// 32 | /// The string to encode. 33 | /// The base 64 encoded string. 34 | public static string Base64Encode(string plainText) 35 | { 36 | var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); 37 | return System.Convert.ToBase64String(plainTextBytes); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/UserProfile/UserProfileApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.UserProfile 2 | { 3 | using System.Threading.Tasks; 4 | using Business; 5 | using Model; 6 | using Model.Auth; 7 | using Model.Uri; 8 | 9 | /// 10 | /// 11 | /// The . 12 | /// 13 | public class UserProfileApi : BaseApi, IUserProfileApi 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// A valid . 19 | public UserProfileApi(Token token) 20 | : base(token) 21 | { 22 | } 23 | 24 | /// 25 | public async Task GetMe() 26 | { 27 | var r = await ApiClient.GetAsync( 28 | MakeUri("me"), this.Token); 29 | 30 | if (r.Response is PrivateUser res) 31 | { 32 | return res; 33 | } 34 | return new PrivateUser(); 35 | } 36 | 37 | /// 38 | public async Task GetUser(SpotifyUri userUri) 39 | { 40 | var r = await ApiClient.GetAsync( 41 | MakeUri($"users/{userUri.Id}"), this.Token); 42 | 43 | if (r.Response is PublicUser res) 44 | { 45 | return res; 46 | } 47 | return new PublicUser(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v[0-9]+.[0-9]+.[0-9]+ 7 | 8 | jobs: 9 | release: 10 | name: Release ${{ github.ref }} 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Setup .NET Core 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 3.1.101 19 | 20 | - name: Restore cache 21 | uses: actions/cache@v1 22 | with: 23 | path: ~/.nuget/packages 24 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} 25 | restore-keys: | 26 | ${{ runner.os }}-nuget- 27 | 28 | - name: Restore packages 29 | run: dotnet restore --use-lock-file --locked-mode 30 | 31 | - name: Build with dotnet 32 | run: dotnet build --configuration Release 33 | 34 | - name: Test with dotnet 35 | run: dotnet test --configuration Release 36 | 37 | - name: Process version of the tag 38 | id: version 39 | uses: ncipollo/semantic-version-action@v1 40 | 41 | - name: Pack package 42 | env: 43 | version: ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }} 44 | run: dotnet pack --configuration Release --output build --include-symbols --include-source -p:Version=$version -p:SymbolPackageFormat=snupkg 45 | 46 | - name: Publish package 47 | env: 48 | api_key: ${{ secrets.NUGET_API_KEY }} 49 | source: ${{ secrets.NUGET_URI }} 50 | run: dotnet nuget push build/*.nupkg --source $source --api-key $api_key 51 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Paging.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The class. 8 | /// 9 | /// The type of 10 | public class Paging 11 | { 12 | /// 13 | /// Gets or sets the href. 14 | /// 15 | [JsonProperty("href")] 16 | public string Href { get; set; } 17 | 18 | /// 19 | /// Gets or sets the items. 20 | /// 21 | [JsonProperty("items")] 22 | public List Items { get; set; } 23 | 24 | /// 25 | /// Gets or sets the limit. 26 | /// 27 | [JsonProperty("limit")] 28 | public int Limit { get; set; } 29 | 30 | /// 31 | /// Gets or sets the next. 32 | /// 33 | [JsonProperty("next")] 34 | public string Next { get; set; } 35 | 36 | /// 37 | /// Gets or sets the offset. 38 | /// 39 | [JsonProperty("offset")] 40 | public int Offset { get; set; } 41 | 42 | /// 43 | /// Gets or sets the previous. 44 | /// 45 | [JsonProperty("previous")] 46 | public string Previous { get; set; } 47 | 48 | /// 49 | /// Gets or sets the total. 50 | /// 51 | [JsonProperty("total")] 52 | public int Total { get; set; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Device.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using Newtonsoft.Json; 4 | 5 | /// 6 | /// The . 7 | /// 8 | public class Device 9 | { 10 | /// 11 | /// Gets or sets the device ID. This may be null. 12 | /// 13 | [JsonProperty("id")] 14 | public string Id { get; set; } 15 | 16 | /// 17 | /// Gets or sets a value indicating if this device is the currently active device. 18 | /// 19 | [JsonProperty("is_active")] 20 | public bool IsActive { get; set; } 21 | 22 | /// 23 | /// Gets or sets a value indicating whether controlling this device is restricted. 24 | /// At present if this is "true" then no Web API commands will be accepted by this device. 25 | /// 26 | [JsonProperty("is_restricted")] 27 | public bool IsRestricted { get; set; } 28 | 29 | /// 30 | /// Gets or sets the name of the device. 31 | /// 32 | [JsonProperty("name")] 33 | public string Name { get; set; } 34 | 35 | /// 36 | /// Gets or sets device type, such as "Computer", "Smartphone" or "Speaker". 37 | /// 38 | [JsonProperty("type")] 39 | public string Type { get; set; } 40 | 41 | /// 42 | /// Gets or sets the current volume in percent. This may be null. 43 | /// 44 | [JsonProperty("volume_percent")] 45 | public int? VolumePercent { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/WebResponse.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System; 4 | using System.Net; 5 | 6 | /// 7 | /// The . 8 | /// 9 | public class WebResponse 10 | { 11 | /// 12 | /// Initializes a new instance of the class. 13 | /// 14 | /// The response. 15 | /// The status code. 16 | private WebResponse(object response, HttpStatusCode statusCode) 17 | { 18 | this.Response = response; 19 | this.StatusCode = statusCode; 20 | } 21 | 22 | /// 23 | /// The response. 24 | /// 25 | public object Response { get; set; } 26 | 27 | /// 28 | /// Gets the type of the response. 29 | /// 30 | public Type Type => this.Response.GetType(); 31 | 32 | /// 33 | /// Gets or sets the status code of the html request. 34 | /// 35 | public HttpStatusCode StatusCode { get; set; } 36 | 37 | /// 38 | /// Makes the specified response. 39 | /// 40 | /// The response. 41 | /// The status code. 42 | /// The new webresponse. 43 | public static WebResponse Make(object response, HttpStatusCode statusCode) 44 | { 45 | return new WebResponse(response, statusCode); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/PublicUser.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class PublicUser 11 | { 12 | /// 13 | /// Gets or sets the display name. 14 | /// 15 | [JsonProperty("display_name")] 16 | public string DisplayName { get; set; } 17 | 18 | /// 19 | /// Gets or sets the external urls. 20 | /// 21 | [JsonProperty("external_urls")] 22 | public Dictionary ExternalUrls { get; set; } 23 | 24 | /// 25 | /// Gets or sets the followers. 26 | /// 27 | [JsonProperty("followers")] 28 | public Followers Followers { get; set; } 29 | 30 | /// 31 | /// Gets or sets the href. 32 | /// 33 | [JsonProperty("href")] 34 | public string Href { get; set; } 35 | 36 | /// 37 | /// Gets or sets the identifier. 38 | /// 39 | [JsonProperty("id")] 40 | public string Id { get; set; } 41 | 42 | /// 43 | /// Gets or sets the images. 44 | /// 45 | [JsonProperty("images")] 46 | public List Images { get; set; } 47 | 48 | /// 49 | /// Gets or sets the type. 50 | /// 51 | [JsonProperty("type")] 52 | public string Type { get; set; } 53 | 54 | /// 55 | /// Gets or sets the URI. 56 | /// 57 | [JsonProperty("uri")] 58 | public SpotifyUri Uri { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Browse/BrowseApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Browse 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using global::SpotifyWebApi.Business; 8 | using Model; 9 | using Model.Auth; 10 | 11 | /// 12 | /// 13 | /// The . 14 | /// 15 | public class BrowseApi : BaseApi, IBrowseApi 16 | { 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// A valid . 21 | public BrowseApi(Token token) 22 | : base(token) 23 | { 24 | } 25 | 26 | /// 27 | public async Task<(string Message, IList Playlists)> GetFeaturedPlaylists( 28 | string locale, 29 | string country, 30 | DateTime? timeStamp, 31 | int maxResults, 32 | int offset) 33 | { 34 | var r = await ApiClient.GetAsync( 35 | MakeUri( 36 | $"browse/featured-playlists", 37 | ("locale", locale), 38 | ("country", country), 39 | ("timestamp", $"{timeStamp:O}")), 40 | this.Token); 41 | 42 | if (r.Response is FeaturedPlaylistResponse res) 43 | { 44 | return (res.Message, res.Playlists); 45 | } 46 | return (string.Empty, new List()); 47 | } 48 | 49 | private class FeaturedPlaylistResponse 50 | { 51 | public string Message { get; set; } 52 | 53 | public List Playlists { get; set; } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Album/IAlbumApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Album 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Model; 6 | using Model.Uri; 7 | 8 | /// 9 | /// The album api. 10 | /// 11 | public interface IAlbumApi 12 | { 13 | /// 14 | /// Get Spotify catalog information for a single album. 15 | /// 16 | /// The for the album 17 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 18 | /// The retrieved . 19 | Task GetAlbum(SpotifyUri albumUri, string market = ""); 20 | 21 | /// 22 | /// Get Spotify catalog information for multiple albums identified by their Spotify URIs. 23 | /// 24 | /// A list of the Spotify URIs for the albums. Maximum: 20 IDs. 25 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 26 | /// The retrieved s. 27 | Task> GetAlbums(IList albumUris, string market = ""); 28 | 29 | /// 30 | /// Get Spotify catalog information about an album’s tracks. Optional parameters can be used to limit the number of tracks returned. 31 | /// 32 | /// The for the album 33 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 34 | /// The retrieved tracks. 35 | Task> GetAlbumTracks(SpotifyUri albumUri, string market = ""); 36 | } 37 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/FullArtist.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class FullArtist 11 | { 12 | /// 13 | /// Gets or sets the external urls. 14 | /// 15 | [JsonProperty("external_urls")] 16 | public Dictionary ExternalUrls { get; set; } 17 | 18 | /// 19 | /// Gets or sets the followers. 20 | /// 21 | [JsonProperty("followers")] 22 | public Followers Followers { get; set; } 23 | 24 | /// 25 | /// Gets or sets the genres. 26 | /// 27 | [JsonProperty("genres")] 28 | public List Genres { get; set; } 29 | 30 | /// 31 | /// Gets or sets the href. 32 | /// 33 | [JsonProperty("href")] 34 | public string Href { get; set; } 35 | 36 | /// 37 | /// Gets or sets the identifier. 38 | /// 39 | [JsonProperty("id")] 40 | public string Id { get; set; } 41 | 42 | /// 43 | /// Gets or sets the images. 44 | /// 45 | [JsonProperty("images")] 46 | public List Images { get; set; } 47 | 48 | /// 49 | /// Gets or sets the name. 50 | /// 51 | [JsonProperty("name")] 52 | public string Name { get; set; } 53 | 54 | /// 55 | /// Gets or sets the popularity. 56 | /// 57 | [JsonProperty("popularity")] 58 | public int Popularity { get; set; } 59 | 60 | /// 61 | /// Gets or sets the type. 62 | /// 63 | [JsonProperty("type")] 64 | public string Type { get; set; } 65 | 66 | /// 67 | /// Gets or sets the URI. 68 | /// 69 | [JsonProperty("uri")] 70 | public SpotifyUri Uri { get; set; } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Track/TrackApi.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) companyPlaceholder. All rights reserved. 3 | // 4 | 5 | namespace SpotifyWebApi.Api.Track 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using Business; 13 | using Model; 14 | using Model.Auth; 15 | using Model.Uri; 16 | 17 | /// 18 | /// The . 19 | /// 20 | public class TrackApi : BaseApi, ITrackApi 21 | { 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// A valid access token. 26 | public TrackApi(Token token) 27 | : base(token) 28 | { 29 | } 30 | 31 | /// 32 | public async Task GetTrack(SpotifyUri uri, string market) 33 | { 34 | var r = await ApiClient.GetAsync( 35 | MakeUri( 36 | $"tracks/{uri.Id}", 37 | ("market", market)), 38 | this.Token); 39 | 40 | if (r.Response is FullTrack res) 41 | { 42 | return res; 43 | } 44 | return new FullTrack(); 45 | } 46 | 47 | /// 48 | public async Task> GetTracks(IList uris, string market) 49 | { 50 | Validation.ValidateList(uris, 1, 50); 51 | var r = await ApiClient.GetAsync>( 52 | MakeUri( 53 | $"tracks", 54 | ("ids", string.Join(",", uris.Select(x => x.Id))), 55 | ("market", market)), 56 | this.Token); 57 | 58 | if (r.Response is List res) 59 | { 60 | return res; 61 | } 62 | return new List(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/SimpleAlbum.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class SimpleAlbum 11 | { 12 | /// 13 | /// Gets or sets the type of the album. 14 | /// 15 | [JsonProperty("album_type")] 16 | public string AlbumType { get; set; } 17 | 18 | /// 19 | /// Gets or sets the artists. 20 | /// 21 | [JsonProperty("artists")] 22 | public List Artists { get; set; } 23 | 24 | /// 25 | /// Gets or sets the available markets. 26 | /// 27 | [JsonProperty("available_markets")] 28 | public List AvailableMarkets { get; set; } 29 | 30 | /// 31 | /// Gets or sets the external urls. 32 | /// 33 | [JsonProperty("external_url")] 34 | public Dictionary ExternalUrls { get; set; } 35 | 36 | /// 37 | /// Gets or sets the href. 38 | /// 39 | [JsonProperty("href")] 40 | public string Href { get; set; } 41 | 42 | /// 43 | /// Gets or sets the identifier. 44 | /// 45 | [JsonProperty("id")] 46 | public string Id { get; set; } 47 | 48 | /// 49 | /// Gets or sets the images. 50 | /// 51 | [JsonProperty("images")] 52 | public List Images { get; set; } 53 | 54 | /// 55 | /// Gets or sets the name. 56 | /// 57 | [JsonProperty("name")] 58 | public string Name { get; set; } 59 | 60 | /// 61 | /// Gets or sets the type. 62 | /// 63 | [JsonProperty("type")] 64 | public string Type { get; set; } 65 | 66 | /// 67 | /// Gets or sets the URI. 68 | /// 69 | [JsonProperty("uri")] 70 | public SpotifyUri Uri { get; set; } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Test/Api/PlaylistTest.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest.Api 2 | { 3 | using System.Diagnostics; 4 | using System.Threading.Tasks; 5 | using SpotifyWebApi.Api.Playlist; 6 | using SpotifyWebApi.Model.Uri; 7 | using Xunit; 8 | using Xunit.Abstractions; 9 | 10 | /// 11 | /// The . 12 | /// 13 | public class PlaylistTest : TestBase 14 | { 15 | /// 16 | public PlaylistTest(TestData testData, ITestOutputHelper output) : base(testData, output) 17 | { 18 | } 19 | 20 | /// 21 | /// Tests the method. 22 | /// 23 | /// A playlist uri. 24 | /// The expected track count. 25 | [Theory(Skip = "Unsupported")] 26 | [InlineData("spotify:user:1141820105:playlist:26JXqOmOpnO3hJ4Ij1EEKL", 20)] 27 | [InlineData("spotify:user:1141820105:playlist:4ReouBjlGOwe1btf1CAvIh", 100)] 28 | [InlineData("spotify:user:1141820105:playlist:2Iiwx1mspBbv9eV3gah2B9", 200)] 29 | [InlineData("spotify:user:1141820105:playlist:2vIxq3ZWHDDgrVfAzmIbDf", 1000)] 30 | // [InlineData("spotify:user:1141820105:playlist:4y6G7PQjyw48ha4SdbLF0A", 10000)] 31 | public async Task GetPlaylistTracksTest(string uri, int expectedTrackCount) 32 | { 33 | var st = Stopwatch.StartNew(); 34 | var tracks = await this.Api.Playlist.GetPlaylistTracks(SpotifyUri.Make(uri)); 35 | st.Stop(); 36 | this.Output.WriteLine($"Getting {expectedTrackCount} tracks took {st.Elapsed:c}"); 37 | Assert.Equal(expectedTrackCount, tracks.Count); 38 | } 39 | 40 | /// 41 | /// A test for correct retrieval of a playlist. 42 | /// 43 | [Fact(Skip = "Unsupported")] 44 | public async Task GetPlaylistTest() 45 | { 46 | var pl = await this.Api.Playlist.GetPlaylist( 47 | "spotify:user:1141820105:playlist:26JXqOmOpnO3hJ4Ij1EEKL"); 48 | Assert.NotNull(pl); 49 | Assert.Equal("Test Playlist (20 songs)", pl.Name); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Test/TestData.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using SpotifyWebApi; 6 | using SpotifyWebApi.Auth; 7 | using SpotifyWebApi.Model.Auth; 8 | using SpotifyWebApi.Model.Enum; 9 | using Xunit.Abstractions; 10 | 11 | /// 12 | /// A class containing some test data used for all tests. 13 | /// 14 | public class TestData 15 | { 16 | /// 17 | /// An instance of a without access to personal information. 18 | /// 19 | public Token ClientCredentialsToken { get; } 20 | 21 | /// 22 | /// An instance of a with access to personal information. 23 | /// 24 | public Token PersonalToken { get; } 25 | 26 | /// 27 | /// Initializes the test data. 28 | /// 29 | public TestData() 30 | { 31 | // So to make the tests work, we need to get a Client Id and Secret. 32 | // I have chosen to save that data in environment variables. 33 | // Key: 'SpotifyWebApiTestData' Value: 'ClientId:ClientSecret' 34 | var variables = Environment.GetEnvironmentVariable("SpotifyWebApiTestData")?.Split(':'); 35 | if (variables == null) 36 | { 37 | throw new Exception("Did you set the environment variables before testing?"); 38 | } 39 | 40 | var clientId = variables[0]; 41 | var clientSecret = variables[1]; 42 | 43 | this.ClientCredentialsToken = this.GetClientCredentialsToken(clientId, clientSecret); 44 | // this.PersonalToken = this.GetImplicitGrantToken(clientId); 45 | } 46 | 47 | private Token GetClientCredentialsToken(string clientId, string clientSecret) 48 | { 49 | return ClientCredentials.GetToken(new AuthParameters 50 | { 51 | ClientId = clientId, 52 | ClientSecret = clientSecret, 53 | Scopes = Scopes.All 54 | }); 55 | } 56 | 57 | private Token GetImplicitGrantToken(string clientId) 58 | { 59 | throw new NotImplementedException(); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/CurrentlyPlayingContext.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System; 4 | using Newtonsoft.Json; 5 | 6 | /// 7 | /// The . 8 | /// 9 | public class CurrentlyPlayingContext 10 | { 11 | /// 12 | /// Gets or sets the device that is currently active. 13 | /// 14 | [JsonProperty("device")] 15 | public Device Device { get; set; } 16 | 17 | /// 18 | /// Gets or sets the repeat state: off, track, context. 19 | /// 20 | [JsonProperty("repeat_state")] 21 | public string RepeatState { get; set; } 22 | 23 | /// 24 | /// Gets or sets a value indicating whether shuffle is on or off 25 | /// 26 | [JsonProperty("shuffle_state")] 27 | public bool ShuffleState { get; set; } 28 | 29 | /// 30 | /// Gets or sets the context. 31 | /// 32 | [JsonProperty("context")] 33 | public Context Context { get; set; } 34 | 35 | /// 36 | /// Gets or sets the Unix Millisecond Timestamp when data was fetched 37 | /// 38 | [JsonProperty("timestamp")] 39 | public long TimestampMs { get; set; } 40 | 41 | /// 42 | /// Gets the object of the 43 | /// 44 | public DateTime Timestamp => DateTimeOffset.FromUnixTimeMilliseconds(this.TimestampMs).LocalDateTime; 45 | 46 | /// 47 | /// Gets or sets the progress into the currently playing track. Can be null. 48 | /// 49 | [JsonProperty("progress_ms")] 50 | public int? ProgressMs { get; set; } 51 | 52 | /// 53 | /// Gets or sets a value indicating whether if something is currently playing. 54 | /// 55 | [JsonProperty("is_playing")] 56 | public bool IsPlaying { get; set; } 57 | 58 | /// 59 | /// Gets or sets the currently playing track. Can be null. 60 | /// 61 | [JsonProperty("item")] 62 | public FullTrack Item { get; set; } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SpotifyWebApi/Auth/ImplicitGrant.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) companyPlaceholder. All rights reserved. 3 | // 4 | 5 | namespace SpotifyWebApi.Auth 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using Model.Auth; 13 | using Model.Enum; 14 | 15 | /// 16 | /// The . 17 | /// 18 | public static class ImplicitGrant 19 | { 20 | /// 21 | /// Retrieves the authentication url for authenticating with the spotify web api. 22 | /// 23 | /// The to use while creating the url. 24 | /// The state to use while creating the url. 25 | /// For more info see 'https://developer.spotify.com/web-api/authorization-guide/#implicit_grant_flow' 26 | /// The url that the user can use to authenticate this application. 27 | public static string GetUrl(AuthParameters parameters, string state) 28 | { 29 | return $"https://accounts.spotify.com/authorize/?" + 30 | $"client_id={parameters.ClientId}" + 31 | $"&response_type=token" + 32 | $"&redirect_uri={parameters.RedirectUri}" + 33 | $"&scope={parameters.Scopes}" + 34 | $"&state={state}" + 35 | $"&show_dialog={(parameters.ShowDialog ? "true" : "false")}"; 36 | } 37 | 38 | /// 39 | /// Creates a token from the callback url. 40 | /// 41 | /// The accesstoken. 42 | /// The tokentype. 43 | /// The expires in. 44 | /// A valid . 45 | public static Token Callback(string accessToken, string tokenType, int expiresIn) 46 | { 47 | return Token.Make( 48 | accessToken, 49 | null, 50 | tokenType, 51 | expiresIn, 52 | authenticationType: TokenAuthenticationType.ImplicitGrant); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SpotifyWebApi/SpotifyWebApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi 2 | { 3 | using System; 4 | using Api; 5 | using Api.Album; 6 | using Api.Artist; 7 | using Api.Browse; 8 | using Api.Follow; 9 | using Api.Personalization; 10 | using Api.Player; 11 | using Api.Playlist; 12 | using Api.Search; 13 | using Api.Track; 14 | using Api.UserLibrary; 15 | using Api.UserProfile; 16 | using Model.Auth; 17 | 18 | /// 19 | /// The main Api class. 20 | /// 21 | public class SpotifyWebApi : BaseApi, ISpotifyWebApi 22 | { 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// A valid . 27 | public SpotifyWebApi(Token token) 28 | : base(token) 29 | { 30 | } 31 | 32 | /// 33 | public IAlbumApi Album => new AlbumApi(this.Token); 34 | 35 | /// 36 | public IArtistApi Artist => new ArtistApi(this.Token); 37 | 38 | /// 39 | public IBrowseApi Browse => throw new NotImplementedException("This api is not yet implemented!"); 40 | 41 | /// 42 | public IFollowApi Follow => throw new NotImplementedException("This api is not yet implemented!"); 43 | 44 | /// 45 | public IPersonalizationApi Personalization => throw new NotImplementedException("This api is not yet implemented!"); 46 | 47 | /// 48 | public IPlayerApi Player => new PlayerApi(this.Token); 49 | 50 | /// 51 | public IPlaylistApi Playlist => new PlaylistApi(this.Token); 52 | 53 | /// 54 | public ISearchApi Search => throw new NotImplementedException("This api is not yet implemented!"); 55 | 56 | /// 57 | public ITrackApi Track => new TrackApi(this.Token); 58 | 59 | /// 60 | public IUserLibraryApi UserLibrary => throw new NotImplementedException("This api is not yet implemented!"); 61 | 62 | /// 63 | public IUserProfileApi UserProfile => new UserProfileApi(this.Token); 64 | 65 | /// 66 | public void SetToken(Token token) 67 | { 68 | this.UpdateToken(token); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ExampleConsoleApp/Program.cs: -------------------------------------------------------------------------------- 1 | namespace ExampleConsoleApp 2 | { 3 | using System; 4 | using System.Collections.Specialized; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | using Microsoft.Extensions.Configuration; 10 | using SpotifyWebApi; 11 | using SpotifyWebApi.Auth; 12 | using SpotifyWebApi.Model.Auth; 13 | using SpotifyWebApi.Model.Enum; 14 | 15 | public class Program 16 | { 17 | private readonly IConfiguration configuration; 18 | private readonly SpotifyAuthentication spotifyAuthentication; 19 | 20 | public Program(IConfiguration configuration, SpotifyAuthentication spotifyAuthentication) 21 | { 22 | this.configuration = configuration; 23 | this.spotifyAuthentication = spotifyAuthentication; 24 | } 25 | 26 | public async Task Run() 27 | { 28 | var token = await this.spotifyAuthentication.GetToken(); 29 | 30 | Console.Clear(); 31 | Console.WriteLine("Successfully logged in!"); 32 | 33 | // Use the api with access to personal data. 34 | var api = new SpotifyWebApi(token); 35 | var me = await api.UserProfile.GetMe(); 36 | 37 | Console.WriteLine($"Hello {me.DisplayName}"); 38 | 39 | var playlists = await api.Playlist.GetMyPlaylists(); 40 | Console.WriteLine("Your playlists:"); 41 | Console.WriteLine(string.Join(", ", playlists.Select(x => x.Name))); 42 | 43 | Console.WriteLine(); 44 | 45 | var devices = await api.Player.GetAvailableDevices(); 46 | Console.WriteLine("Your devices:"); 47 | foreach (var device in devices) 48 | { 49 | Console.Write($"{device.Name}, "); 50 | } 51 | 52 | // Console.WriteLine("Refreshing token..."); 53 | // var newToken = await AuthorizationCode.RefreshTokenAsync(this.parameters, token); 54 | // Console.WriteLine($"old: {token.AccessToken}"); 55 | // Console.WriteLine($"new: {newToken.AccessToken}"); 56 | // 57 | // api.SetToken(newToken); 58 | // 59 | // me = await api.UserProfile.GetMe(); 60 | // 61 | // Console.WriteLine($"Hello {me.DisplayName}"); 62 | 63 | Console.ReadLine(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Test/Model/SpotifyUriTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SpotifyWebApiTest.Model 6 | { 7 | using SpotifyWebApi.Model.Exception; 8 | using SpotifyWebApi.Model.Uri; 9 | using Xunit; 10 | 11 | /// 12 | /// A class testing the correct functioning of . 13 | /// 14 | public class SpotifyUriTest 15 | { 16 | /// 17 | /// Tests the SpotifyUri.Make() method. 18 | /// 19 | /// The uri to convert. 20 | /// The . 21 | [Theory] 22 | [InlineData("spotify:album:1JG04tRkcORA9RP3p06oGp", UriType.Album)] 23 | [InlineData("spotify:artist:2JPBDCZC133viR1884BIEi", UriType.Artist)] 24 | [InlineData("spotify:user:1141820105:playlist:2vIxq3ZWHDDgrVfAzmIbDf", UriType.Playlist)] 25 | [InlineData("spotify:track:0ZGZTut99seZYeJTLy7QZG", UriType.Track)] 26 | [InlineData("spotify:user:1141820105", UriType.User)] 27 | public void UriTypeTest(string u, UriType type) 28 | { 29 | var uri = SpotifyUri.Make(u); 30 | Assert.Equal(u, uri.FullUri); 31 | Assert.Equal(type, uri.Type); 32 | } 33 | 34 | /// 35 | /// Tests the SpotifyUri.Make method for correct exceptions. 36 | /// 37 | [Fact] 38 | public void UriExceptionTest() 39 | { 40 | Assert.Throws(() => SpotifyUri.Make("")); 41 | Assert.Throws(() => SpotifyUri.Make("", UriType.Album)); 42 | Assert.Throws(() => SpotifyUri.Make("a:a:a:a:a")); 43 | Assert.Throws(() => SpotifyUri.Make("spotify:::")); 44 | } 45 | 46 | /// 47 | /// Tests the SpotifyUri.Make method for correct functioning. 48 | /// 49 | [Fact] 50 | public void UriTest() 51 | { 52 | var u1 = SpotifyUri.Make("0ZGZTut99seZYeJTLy7QZG", UriType.Track); 53 | var u2 = SpotifyUri.Make("2vIxq3ZWHDDgrVfAzmIbDf", UriType.Playlist); 54 | 55 | Assert.Equal(UriType.Track, u1.Type); 56 | Assert.Equal("spotify:track:0ZGZTut99seZYeJTLy7QZG", u1.FullUri); 57 | Assert.Equal(UriType.Playlist, u2.Type); 58 | Assert.Equal("spotify:playlist:2vIxq3ZWHDDgrVfAzmIbDf", u2.FullUri); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /SpotifyWebApi/Auth/ClientCredentials.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Auth 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.Specialized; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http.Headers; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using Business; 13 | using Model.Auth; 14 | using Newtonsoft.Json; 15 | 16 | /// 17 | /// The . 18 | /// 19 | public static class ClientCredentials 20 | { 21 | /// 22 | /// Retrieves a valid token from the Spotify web api. 23 | /// 24 | /// The used for this token. 25 | /// A valid . 26 | public static Token GetToken(AuthParameters parameters) 27 | { 28 | var req = ApiHelper.CreateRequest(new Uri("https://accounts.spotify.com/api/token")); 29 | 30 | var headers = new NameValueCollection 31 | { 32 | ["Authorization"] = "Basic " + ApiHelper.Base64Encode($"{parameters.ClientId}:{parameters.ClientSecret}"), 33 | }; 34 | 35 | req.Headers = new WebHeaderCollection 36 | { 37 | headers 38 | }; 39 | 40 | var data = Encoding.ASCII.GetBytes("grant_type=client_credentials"); 41 | 42 | req.Method = "POST"; 43 | req.ContentType = "application/x-www-form-urlencoded"; 44 | req.ContentLength = data.Length; 45 | 46 | using (var stream = req.GetRequestStream()) 47 | { 48 | stream.Write(data, 0, data.Length); 49 | } 50 | 51 | var response = (HttpWebResponse)req.GetResponse(); 52 | 53 | // Get the stream containing content returned by the server. 54 | var dataStream = response.GetResponseStream(); 55 | 56 | // Open the stream using a StreamReader for easy access. 57 | var reader = new StreamReader(dataStream ?? throw new InvalidOperationException()); 58 | 59 | // Read the content. 60 | var json = reader.ReadToEnd(); 61 | 62 | var token = JsonConvert.DeserializeObject(json); 63 | token.CanAccessPersonalData = false; 64 | token.AuthenticationType = TokenAuthenticationType.ClientCredentials; 65 | 66 | return token; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/PrivateUser.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class PrivateUser 11 | { 12 | /// 13 | /// Gets or sets the birthdate. 14 | /// 15 | [JsonProperty("birthdate")] 16 | public string Birthdate { get; set; } 17 | 18 | /// 19 | /// Gets or sets the country. 20 | /// 21 | [JsonProperty("country")] 22 | public string Country { get; set; } 23 | 24 | /// 25 | /// Gets or sets the display name. 26 | /// 27 | [JsonProperty("display_name")] 28 | public string DisplayName { get; set; } 29 | 30 | /// 31 | /// Gets or sets the email. 32 | /// 33 | [JsonProperty("email")] 34 | public string Email { get; set; } 35 | 36 | /// 37 | /// Gets or sets the external urls. 38 | /// 39 | [JsonProperty("external_urls")] 40 | public Dictionary ExternalUrls { get; set; } 41 | 42 | /// 43 | /// Gets or sets the followers. 44 | /// 45 | [JsonProperty("followers")] 46 | public Followers Followers { get; set; } 47 | 48 | /// 49 | /// Gets or sets the href. 50 | /// 51 | [JsonProperty("href")] 52 | public string Href { get; set; } 53 | 54 | /// 55 | /// Gets or sets the identifier. 56 | /// 57 | [JsonProperty("id")] 58 | public string Id { get; set; } 59 | 60 | /// 61 | /// Gets or sets the images. 62 | /// 63 | [JsonProperty("images")] 64 | public List Images { get; set; } 65 | 66 | /// 67 | /// Gets or sets the product. 68 | /// 69 | [JsonProperty("product")] 70 | public string Product { get; set; } 71 | 72 | /// 73 | /// Gets or sets the type. 74 | /// 75 | [JsonProperty("type")] 76 | public string Type { get; set; } 77 | 78 | /// 79 | /// Gets or sets the URI. 80 | /// 81 | [JsonProperty("uri")] 82 | public SpotifyUri Uri { get; set; } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /SpotifyWebApi.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpotifyWebApi", "SpotifyWebApi\SpotifyWebApi.csproj", "{CCD1CAF9-1E3E-42C7-8BBA-A9522C56942D}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{588425FE-3536-40E5-9C61-0CC44598BF42}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution Folder", "{EBCC8FE0-7CF5-4D01-9C51-4283CFFAAA1D}" 11 | ProjectSection(SolutionItems) = preProject 12 | .gitattributes = .gitattributes 13 | .gitignore = .gitignore 14 | README.md = README.md 15 | Solution.ruleset = Solution.ruleset 16 | SpotifyIcon.png = SpotifyIcon.png 17 | EndProjectSection 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleConsoleApp", "ExampleConsoleApp\ExampleConsoleApp.csproj", "{2B3BE9FE-CAAC-4EFB-8EBB-2AEA23229592}" 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Release|Any CPU = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {CCD1CAF9-1E3E-42C7-8BBA-A9522C56942D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {CCD1CAF9-1E3E-42C7-8BBA-A9522C56942D}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {CCD1CAF9-1E3E-42C7-8BBA-A9522C56942D}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {CCD1CAF9-1E3E-42C7-8BBA-A9522C56942D}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {588425FE-3536-40E5-9C61-0CC44598BF42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {588425FE-3536-40E5-9C61-0CC44598BF42}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {588425FE-3536-40E5-9C61-0CC44598BF42}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {588425FE-3536-40E5-9C61-0CC44598BF42}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {2B3BE9FE-CAAC-4EFB-8EBB-2AEA23229592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {2B3BE9FE-CAAC-4EFB-8EBB-2AEA23229592}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {2B3BE9FE-CAAC-4EFB-8EBB-2AEA23229592}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {2B3BE9FE-CAAC-4EFB-8EBB-2AEA23229592}.Release|Any CPU.Build.0 = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(ExtensibilityGlobals) = postSolution 44 | SolutionGuid = {16D90C67-7447-4F44-A250-6CA7BD2358F9} 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /Test/Business/ValidationTest.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest.Business 2 | { 3 | using System.Collections.Generic; 4 | using SpotifyWebApi.Business; 5 | using SpotifyWebApi.Model.Auth; 6 | using SpotifyWebApi.Model.Exception; 7 | using Xunit; 8 | 9 | /// 10 | /// The . 11 | /// 12 | public class ValidationTest 13 | { 14 | /// 15 | /// Test method for . 16 | /// 17 | [Fact] 18 | public void ValidateValidListTest() 19 | { 20 | var validList = new List {0, 1, 2}; 21 | Validation.ValidateList(validList, 0, 3); 22 | Validation.ValidateList(validList, 1, 3); 23 | Validation.ValidateList(validList, 2, 3); 24 | } 25 | 26 | /// 27 | /// Test method for . 28 | /// 29 | [Fact] 30 | public void ValidateNonValidListTest() 31 | { 32 | var nonvalidList = new List(); 33 | Assert.Throws(() => Validation.ValidateList(nonvalidList, 0, 3)); 34 | Assert.Throws(() => Validation.ValidateList(nonvalidList, 1, 3)); 35 | Assert.Throws(() => Validation.ValidateList(nonvalidList, 2, 3)); 36 | } 37 | 38 | /// 39 | /// Test method for . 40 | /// 41 | [Fact] 42 | public void ValidateIntegerTest() 43 | { 44 | Validation.ValidateInteger(0, 0, 2); 45 | Validation.ValidateInteger(1, 0, 2); 46 | Validation.ValidateInteger(2, 0, 2); 47 | 48 | Assert.Throws(() => Validation.ValidateInteger(5, 0, 2)); 49 | Assert.Throws(() => Validation.ValidateInteger(5, 0, 2)); 50 | Assert.Throws(() => Validation.ValidateInteger(5, 0, 2)); 51 | Assert.Throws(() => Validation.ValidateInteger(5, 0, 2)); 52 | } 53 | 54 | /// 55 | /// Test method for . 56 | /// 57 | [Fact] 58 | public void ValidateTokenTest() 59 | { 60 | Assert.Throws(() => Validation.ValidateToken(null)); 61 | Assert.Throws(() => Validation.ValidateToken(new Token())); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SpotifyWebApi/ISpotifyWebApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi 2 | { 3 | using Api.Album; 4 | using Api.Artist; 5 | using Api.Browse; 6 | using Api.Follow; 7 | using Api.Personalization; 8 | using Api.Player; 9 | using Api.Playlist; 10 | using Api.Search; 11 | using Api.Track; 12 | using Api.UserLibrary; 13 | using Api.UserProfile; 14 | using Model.Auth; 15 | 16 | /// 17 | /// The . 18 | /// 19 | public interface ISpotifyWebApi 20 | { 21 | /// 22 | /// Gets the implementation of . 23 | /// 24 | IAlbumApi Album { get; } 25 | 26 | /// 27 | /// Gets the implementation of . 28 | /// 29 | IArtistApi Artist { get; } 30 | 31 | /// 32 | /// Gets the implementation of . 33 | /// 34 | IBrowseApi Browse { get; } 35 | 36 | /// 37 | /// Gets the implementation of . 38 | /// 39 | IFollowApi Follow { get; } 40 | 41 | /// 42 | /// Gets the implementation of . 43 | /// 44 | IPersonalizationApi Personalization { get; } 45 | 46 | /// 47 | /// Gets the implementation of . 48 | /// 49 | IPlayerApi Player { get; } 50 | 51 | /// 52 | /// Gets the implementation of . 53 | /// 54 | IPlaylistApi Playlist { get; } 55 | 56 | /// 57 | /// Gets the implementation of . 58 | /// 59 | ISearchApi Search { get; } 60 | 61 | /// 62 | /// Gets the implementation of . 63 | /// 64 | ITrackApi Track { get; } 65 | 66 | /// 67 | /// Gets the implementation of . 68 | /// 69 | IUserLibraryApi UserLibrary { get; } 70 | 71 | /// 72 | /// Gets the implementation of . 73 | /// 74 | IUserProfileApi UserProfile { get; } 75 | 76 | /// 77 | /// Allows for setting a new token after refreshing it. 78 | /// 79 | /// The refreshed . 80 | void SetToken(Token token); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/SimplePlaylist.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class SimplePlaylist 11 | { 12 | /// 13 | /// Gets or sets a value indicating whether this is collaborative. 14 | /// 15 | [JsonProperty("collaborative")] 16 | public bool Collaborative { get; set; } 17 | 18 | /// 19 | /// Gets or sets the external urls. 20 | /// 21 | [JsonProperty("external_urls")] 22 | public Dictionary ExternalUrls { get; set; } 23 | 24 | /// 25 | /// Gets or sets the href. 26 | /// 27 | [JsonProperty("href")] 28 | public string Href { get; set; } 29 | 30 | /// 31 | /// Gets or sets the identifier. 32 | /// 33 | [JsonProperty("id")] 34 | public string Id { get; set; } 35 | 36 | /// 37 | /// Gets or sets the images. 38 | /// 39 | [JsonProperty("images")] 40 | public List Images { get; set; } 41 | 42 | /// 43 | /// Gets or sets the name. 44 | /// 45 | [JsonProperty("name")] 46 | public string Name { get; set; } 47 | 48 | /// 49 | /// Gets or sets the owner. 50 | /// 51 | [JsonProperty("owner")] 52 | public PublicUser Owner { get; set; } 53 | 54 | /// 55 | /// Gets or sets a value indicating whether this is public. 56 | /// 57 | [JsonProperty("public")] 58 | public bool? Public { get; set; } 59 | 60 | /// 61 | /// Gets or sets the snapshot identifier. 62 | /// 63 | [JsonProperty("snapshot_id")] 64 | public string SnapshotId { get; set; } 65 | 66 | /// 67 | /// Gets or sets the tracks. 68 | /// 69 | [JsonProperty("tracks")] 70 | public PlaylistTracksRef Tracks { get; set; } 71 | 72 | /// 73 | /// Gets or sets the type. 74 | /// 75 | [JsonProperty("type")] 76 | public string Type { get; set; } 77 | 78 | /// 79 | /// Gets or sets the URI. 80 | /// 81 | [JsonProperty("uri")] 82 | public SpotifyUri Uri { get; set; } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /SpotifyWebApi/SpotifyWebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | ..\Solution.ruleset 6 | bin\$(Configuration)\$(TargetFramework)\SpotifyWebApiTest.xml 7 | 8 | SpotifyWebApi 9 | SpotifyWebApi 10 | 11 | 12 | SpotifyWebApi-Core 13 | Spotify Web Api 14 | Pim Merks 15 | Pim Merks 16 | A Spotify Web API wrapper for C#. View the changelog here: https://github.com/pimmerks/SpotifyWebApi/releases 17 | Pim Merks 18 | ICON.png 19 | LICENSE.md 20 | 21 | https://github.com/pimmerks/SpotifyWebApi 22 | https://github.com/pimmerks/SpotifyWebApi 23 | true 24 | Spotify;SpotifyWebApi;Web;Api;REST;Spotify;WebApi;Net;Core;Standard;Framework 25 | true 26 | https://github.com/pimmerks/SpotifyWebApi 27 | master 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | all 44 | runtime; build; native; contentfiles; analyzers 45 | 46 | 47 | all 48 | 49 | 50 | all 51 | 52 | 53 | 54 | 55 | 56 | NU5105 57 | true 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/SimpleTrack.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class SimpleTrack 11 | { 12 | /// 13 | /// Gets or sets the artists. 14 | /// 15 | [JsonProperty("artists")] 16 | public List Artists { get; set; } 17 | 18 | /// 19 | /// Gets or sets the available markets. 20 | /// 21 | [JsonProperty("available_markets")] 22 | public List AvailableMarkets { get; set; } 23 | 24 | /// 25 | /// Gets or sets the disc number. 26 | /// 27 | [JsonProperty("disc_number")] 28 | public int DiscNumber { get; set; } 29 | 30 | /// 31 | /// Gets or sets the duration ms. 32 | /// 33 | [JsonProperty("duration_ms")] 34 | public int DurationMs { get; set; } 35 | 36 | /// 37 | /// Gets or sets a value indicating whether this is explicit. 38 | /// 39 | [JsonProperty("explicit")] 40 | public bool Explicit { get; set; } 41 | 42 | /// 43 | /// Gets or sets the external urls. 44 | /// 45 | [JsonProperty("external_urls")] 46 | public Dictionary ExternalUrls { get; set; } 47 | 48 | /// 49 | /// Gets or sets the href. 50 | /// 51 | [JsonProperty("href")] 52 | public string Href { get; set; } 53 | 54 | /// 55 | /// Gets or sets the identifier. 56 | /// 57 | [JsonProperty("id")] 58 | public string Id { get; set; } 59 | 60 | /// 61 | /// Gets or sets the name. 62 | /// 63 | [JsonProperty("name")] 64 | public string Name { get; set; } 65 | 66 | /// 67 | /// Gets or sets the preview URL. 68 | /// 69 | [JsonProperty("preview_url")] 70 | public string PreviewUrl { get; set; } 71 | 72 | /// 73 | /// Gets or sets the track number. 74 | /// 75 | [JsonProperty("track_number")] 76 | public int TrackNumber { get; set; } 77 | 78 | /// 79 | /// Gets or sets the type. 80 | /// 81 | [JsonProperty("type")] 82 | public string Type { get; set; } 83 | 84 | /// 85 | /// Gets or sets the URI. 86 | /// 87 | [JsonProperty("uri")] 88 | public SpotifyUri Uri { get; set; } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /SpotifyWebApi/Business/HelperExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Business 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Model; 8 | using Model.Auth; 9 | 10 | /// 11 | /// The . 12 | /// 13 | internal static class HelperExtensions 14 | { 15 | /// 16 | /// Loads to list. 17 | /// 18 | /// The type of the paging object. 19 | /// The paging object. 20 | /// The token. 21 | /// The maximum items to return. 22 | /// The final list{T} 23 | public static async Task> LoadToList(this Paging paging, Token token, int maxItems = -1) 24 | { 25 | if (paging == null) 26 | { 27 | return new List(); 28 | } 29 | var curPage = paging; 30 | var result = curPage.Items; 31 | 32 | while (curPage.Next != null) 33 | { 34 | var next = await ApiClient.GetAsync>(new Uri(curPage.Next), token); 35 | 36 | if (next.Response is Paging nextPage) 37 | { 38 | result.AddRange(nextPage.Items); 39 | curPage = nextPage; 40 | } 41 | } 42 | 43 | return result; 44 | } 45 | 46 | /// 47 | /// Chunk a list by . 48 | /// 49 | /// The type of the list. 50 | /// The source list. 51 | /// Size of the chunks. 52 | /// A List of lists containing the chunked lists. 53 | public static List> ChunkBy(this IEnumerable source, int chunkSize) 54 | { 55 | return source 56 | .Select((x, i) => new { Index = i, Value = x }) 57 | .GroupBy(x => x.Index / chunkSize) 58 | .Select(x => x.Select(v => v.Value).ToList()) 59 | .ToList(); 60 | } 61 | 62 | /// 63 | /// Converts a list of strings to a single string with a separator. 64 | /// 65 | /// The list to convert. 66 | /// The separator to put between the list items. 67 | /// The created string. 68 | public static string AsSingleString(this List list, string separator = ",") 69 | { 70 | return string.Join(separator, list); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Test/Api/BaseApiTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SpotifyWebApiTest.Api 6 | { 7 | using SpotifyWebApi.Api; 8 | using SpotifyWebApi.Model.Auth; 9 | using Xunit; 10 | 11 | /// 12 | /// A test for the . 13 | /// 14 | public class BaseApiTest : BaseApi 15 | { 16 | /// 17 | /// 18 | /// Initializes this test class. 19 | /// 20 | public BaseApiTest() 21 | : base(null) 22 | { 23 | } 24 | 25 | /// 26 | /// A test for the make uri method. 27 | /// 28 | [Fact(Skip = "Unsupported")] 29 | public void MakeUriTest() 30 | { 31 | var uri1 = MakeUri("test"); 32 | Assert.Equal("https://api.spotify.com/v1/test", uri1.AbsoluteUri); 33 | } 34 | 35 | /// 36 | /// A test for the make uri method. 37 | /// 38 | /// The expected absolute uri. 39 | /// The relative uri. 40 | /// The query parameter 41 | /// The query value 42 | [Theory(Skip = "Unsupported")] 43 | [InlineData("https://api.spotify.com/v1/test", "test", "", "")] 44 | [InlineData("https://api.spotify.com/v1/test", "test", "q", "")] 45 | [InlineData("https://api.spotify.com/v1/test", "test", "", "v")] 46 | [InlineData("https://api.spotify.com/v1/test?q=v", "test", "q", "v")] 47 | public void MakeUriTests(string expected, string r, string q, string v) 48 | { 49 | var uri = MakeUri(r, (queryParameter: q, value: v)); 50 | Assert.Equal(expected, uri.AbsoluteUri); 51 | } 52 | 53 | /// 54 | /// A test for the make uri method. 55 | /// 56 | /// The expected absolute uri. 57 | /// The relative uri. 58 | /// The query parameter 59 | /// The query value 60 | /// The query parameter 61 | /// The query value 62 | [Theory(Skip = "Unsupported")] 63 | [InlineData("https://api.spotify.com/v1/test", "test", "", "", "", "")] 64 | [InlineData("https://api.spotify.com/v1/test?q=v&q=v", "test", "q", "v", "q", "v")] 65 | [InlineData("https://api.spotify.com/v1/test", "test", "q", "", "", "v")] 66 | [InlineData("https://api.spotify.com/v1/test?q=v", "test", "q", "", "q", "v")] 67 | public void MakeUriTest2(string expected, string r, string q1, string v1, string q2, string v2) 68 | { 69 | var uri = MakeUri(r, (q1, v1), (q2, v2)); 70 | Assert.Equal(expected, uri.AbsoluteUri); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Browse/IBrowseApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Browse 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Model; 7 | 8 | /// 9 | /// The browse api interface. 10 | /// 11 | public interface IBrowseApi 12 | { 13 | /// 14 | /// Get a list of Spotify featured playlists (shown, for example, on a Spotify player’s ‘Browse’ tab). 15 | /// 16 | /// 17 | /// Optional. The desired language, consisting of a lowercase ISO 639-1 language code and an uppercase ISO 3166-1 alpha-2 country code, joined by an underscore. 18 | /// For example: es_MX, meaning “Spanish (Mexico)”. Provide this parameter if you want the results returned in a particular language (where available). 19 | /// Note that, if locale is not supplied, or if the specified language is not available, all strings will be returned in the Spotify default language (American English). 20 | /// The locale parameter, combined with the country parameter, may give odd results if not carefully matched. 21 | /// For example country=SE&locale=de_DE will return a list of categories relevant to Sweden but as German language strings. 22 | /// 23 | /// 24 | /// Optional. A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want the list of returned items to be relevant to a particular country. If omitted, the returned items will be relevant to all countries. 25 | /// 26 | /// 27 | /// Optional. Use this parameter to specify the user’s local time to get results tailored for that specific date and time in the day. If not provided, the response defaults to the current UTC time. Example: “2014-10-23T09:00:00” for a user whose local time is 9AM. If there were no featured playlists (or there is no data) at the specified time, the response will revert to the current UTC time. 28 | /// 29 | /// Optional. The maximum results to return, or -1 to retrieve all items. 30 | /// 31 | /// Optional. The index of the first item to return. Default: 0 (the first object). Use with limit to get the next set of items. 32 | /// 33 | /// 34 | /// A and a list of objects. 35 | /// The conveys a message like "Monday morning music, coming right up!". 36 | /// 37 | #pragma warning disable SA1009 // Closing parenthesis must be spaced correctly 38 | Task<(string Message, IList Playlists)> GetFeaturedPlaylists( 39 | string locale = null, 40 | string country = null, 41 | DateTime? timeStamp = null, 42 | int maxResults = -1, 43 | int offset = 0); 44 | #pragma warning restore SA1009 // Closing parenthesis must be spaced correctly 45 | } 46 | } -------------------------------------------------------------------------------- /ExampleConsoleApp/SpotifyAuthentication.cs: -------------------------------------------------------------------------------- 1 | namespace ExampleConsoleApp 2 | { 3 | using System; 4 | using System.Collections.Specialized; 5 | using System.Diagnostics; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | using Microsoft.Extensions.Configuration; 9 | using SpotifyWebApi.Auth; 10 | using SpotifyWebApi.Model.Auth; 11 | using SpotifyWebApi.Model.Enum; 12 | 13 | public class SpotifyAuthentication 14 | { 15 | private string clientSecret; 16 | private string clientId; 17 | private string redirectUri; 18 | private AuthParameters parameters; 19 | 20 | public SpotifyAuthentication(IConfiguration configuration) 21 | { 22 | this.clientId = configuration["Spotify:ClientId"]; 23 | this.clientSecret = configuration["Spotify:ClientSecret"]; 24 | this.redirectUri = configuration["Spotify:RedirectUri"]; 25 | 26 | this.parameters = new AuthParameters 27 | { 28 | ClientId = this.clientId, 29 | ClientSecret = this.clientSecret, 30 | RedirectUri = this.redirectUri, 31 | Scopes = Scopes.All, 32 | ShowDialog = true 33 | }; 34 | } 35 | 36 | public async Task GetToken() 37 | { 38 | var state = Guid.NewGuid().ToString(); // Save this state because you must check it later 39 | 40 | var url = AuthorizationCode.GetUrl(this.parameters, state); 41 | 42 | Console.WriteLine("Opening url..."); 43 | Console.WriteLine(url); 44 | 45 | var ps = new ProcessStartInfo(url) 46 | { 47 | UseShellExecute = true, 48 | Verb = "open" 49 | }; 50 | Process.Start(ps); 51 | 52 | var queryString = await this.StartServerAndRetrieveAuthCode(); 53 | 54 | // The retrieved callback: 55 | var retrievedState = queryString["state"]; 56 | var retrievedCode = queryString["code"]; 57 | var retrievedError = queryString["error"]; 58 | 59 | if (retrievedError != null) 60 | { 61 | throw new Exception(retrievedError); 62 | } 63 | 64 | if (state != retrievedState) 65 | { 66 | throw new Exception("State did not match!"); 67 | } 68 | 69 | var token = await AuthorizationCode.ProcessCallbackAsync(this.parameters, retrievedCode); 70 | 71 | return token; 72 | } 73 | 74 | public async Task StartServerAndRetrieveAuthCode(string url = "http://localhost:8080/") 75 | { 76 | var listener = new HttpListener(); 77 | listener.Prefixes.Add("http://localhost:8080/"); 78 | listener.Start(); 79 | 80 | var context = await listener.GetContextAsync(); 81 | 82 | listener.Stop(); 83 | 84 | return context.Request.QueryString; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Test/Api/AlbumTest.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApiTest.Api 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Threading.Tasks; 6 | using SpotifyWebApi.Api.Album; 7 | using SpotifyWebApi.Model.Exception; 8 | using SpotifyWebApi.Model.Uri; 9 | using Xunit; 10 | using Xunit.Abstractions; 11 | 12 | /// 13 | /// The . 14 | /// 15 | public class AlbumTest : TestBase 16 | { 17 | /// 18 | public AlbumTest(TestData testData, ITestOutputHelper output) : base(testData, output) 19 | { 20 | } 21 | 22 | /// 23 | /// The album test. 24 | /// 25 | [Theory(Skip = "Unsupported")] 26 | [InlineData("spotify:album:44tJAQ21VUkgwjRDbNeJtB", "Funky Freddy")] 27 | [InlineData("spotify:album:1JG04tRkcORA9RP3p06oGp", "Palmas")] 28 | public async Task GetAlbumTest(string uri, string expectedName) 29 | { 30 | var album = await this.Api.Album.GetAlbum(uri); 31 | Assert.True(album.Name == expectedName); 32 | Assert.NotNull(album); 33 | } 34 | 35 | /// 36 | /// The albums test. 37 | /// 38 | [Fact(Skip = "Unsupported")] 39 | public async Task GetAlbumsTest() 40 | { 41 | var albums = await this.Api.Album.GetAlbums( 42 | SpotifyUri.MakeList( 43 | "spotify:album:44tJAQ21VUkgwjRDbNeJtB", 44 | "spotify:album:1JG04tRkcORA9RP3p06oGp")); 45 | Assert.Equal(2, albums.Count); 46 | Assert.True(albums[0].Name == "Funky Freddy"); 47 | Assert.True(albums[1].Name == "Palmas"); 48 | } 49 | 50 | /// 51 | /// The album tracks test. 52 | /// 53 | [Theory(Skip = "Unsupported")] 54 | [InlineData("spotify:album:44tJAQ21VUkgwjRDbNeJtB", 1)] 55 | [InlineData("spotify:album:1JG04tRkcORA9RP3p06oGp", 21)] 56 | public async Task GetAlbumTracksTest(string uri, int expectedTrackCount) 57 | { 58 | var tracks = await this.Api.Album.GetAlbumTracks(SpotifyUri.Make(uri)); 59 | Assert.Equal(expectedTrackCount, tracks.Count); 60 | } 61 | 62 | /// 63 | /// Tests to see if the validation works. 64 | /// 65 | [Fact(Skip = "Unsupported")] 66 | public async Task GetAlbumTrackExceptionTest() 67 | { 68 | await Assert.ThrowsAsync( 69 | async () => await this.Api.Album.GetAlbum("spotify:album:fdsajkfdsjkha")); 70 | } 71 | 72 | /// 73 | /// Tests to see if the validation works. 74 | /// 75 | [Fact(Skip = "Unsupported")] 76 | public async Task UnauthorizedExceptionTest() 77 | { 78 | await Assert.ThrowsAsync( 79 | async () => await new AlbumApi(null).GetAlbum("spotify:album:fdsajkfdsjkha", "")); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Push](https://github.com/pimmerks/SpotifyWebApi/workflows/Push/badge.svg?branch=master&event=push) 2 | [![NuGet version (SpotifyWebApi-Core)](https://img.shields.io/nuget/v/SpotifyWebApi-Core.svg)](https://www.nuget.org/packages/SpotifyWebApi-Core/) 3 | [![NuGet](https://img.shields.io/nuget/dt/SpotifyWebApi-Core.svg)](https://www.nuget.org/packages/SpotifyWebApi-Core/) 4 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/pimmerks/SpotifyWebApi/blob/master/LICENSE) 5 | 6 | 7 | # SpotifyWebApi - Core 8 | A .net Core wrapper for the Spotify Web Api. 9 | 10 | # Nuget package 11 | Installing the package is easy. 12 | 13 | Using the package manager console: 14 | > `PM> Install-Package SpotifyWebApi-Core` 15 | 16 | 17 | Using the dotnet CLI: 18 | > `> dotnet add package SpotifyWebApi-Core` 19 | 20 | ## Getting an authentication token 21 | 22 | ### Client credentials: 23 | 24 | ```csharp 25 | var token = ClientCredentials.GetToken(new AuthParameters 26 | { 27 | ClientId = "clientId", 28 | ClientSecret = "clientSecret", 29 | Scopes = Scope.All, 30 | }); 31 | ``` 32 | Note: A client credentials token cannot access any personal data. 33 | The `Token` class has a usefull property to check this: `token.CanAccessPersonalData` 34 | 35 | ### Authorization code: 36 | Start the authorization code process by retrieving the authentication url: 37 | ```csharp 38 | var state = Guid.NewGuid().ToString(); // Save this state because you must check it later 39 | 40 | var parameters = new AuthParameters 41 | { 42 | ClientId = "clientId", 43 | ClientSecret = "clientSecret", 44 | RedirectUri = "https://.../callback", 45 | Scopes = Scope.All, 46 | ShowDialog = true 47 | }; 48 | 49 | var url = AuthorizationCode.GetUrl(parameters, state); 50 | ``` 51 | At this point you should start a webserver listening on the `RedirectUri` and the the client/user should login to Spotify. 52 | The webserver should expect the following query parameters: `?code=code&state=state&error=error` 53 | ```csharp 54 | // The retreived callback: 55 | var retrievedState = "retrievedState"; 56 | var retrievedCode = "code"; 57 | var retreivedError = ""; 58 | 59 | if (state != retrievedState) 60 | { 61 | throw new Exception("State did not match!"); 62 | } 63 | 64 | var token = AuthorizationCode.ProcessCallback(parameters, retrievedCode, retreivedError); 65 | 66 | // Use the api with access to personal data. 67 | var api = new SpotifyWebApi(token); 68 | var me = await api.UserProfile.GetMe(); 69 | ``` 70 | 71 | A token received with the `AuthorizationCode` can also be refreshed: 72 | ```csharp 73 | // Refresh a token when its expired 74 | if (token.IsExpired) 75 | { 76 | token = AuthorizationCode.RefreshToken(parameters, token); 77 | } 78 | ``` 79 | 80 | ## Using the api 81 | After a token has been retrieved, we can use the api: 82 | 83 | Getting a playlist and the playlist tracks: 84 | ```csharp 85 | ISpotifyWebApi api = new SpotifyWebApi(token); 86 | 87 | var playlist = await spotify.Playlist.GetPlaylist(id); 88 | var tracks = await spotify.Playlist.GetPlaylistTracks(id); 89 | ``` 90 | 91 | ## TODO: 92 | - [ ] Add documentation 93 | - [ ] Add api examples 94 | - [ ] Implement more endpoints 95 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Album/AlbumApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Album 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Business; 7 | using Model; 8 | using Model.Auth; 9 | using Model.List; 10 | using Model.Uri; 11 | 12 | /// 13 | /// 14 | /// The . 15 | /// 16 | public class AlbumApi : BaseApi, IAlbumApi 17 | { 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// A valid . 22 | public AlbumApi(Token token) 23 | : base(token) 24 | { 25 | } 26 | 27 | /// 28 | public async Task GetAlbum(SpotifyUri albumUri, string market) 29 | { 30 | var r = await ApiClient.GetAsync( 31 | MakeUri( 32 | $"albums/{albumUri.Id}", 33 | ("market", market)), 34 | this.Token); 35 | 36 | if (r.Response is FullAlbum album) 37 | { 38 | return album; 39 | } 40 | return new FullAlbum(); 41 | } 42 | 43 | /// 44 | public async Task> GetAlbums(IList albumUris, string market) 45 | { 46 | // Because we support more then 50 albums uris as input. 47 | // but the spotify api only supports a maximum of 50, 48 | // lets fix that by creating multiple lists of 50. 49 | var lists = albumUris.ChunkBy(50); 50 | 51 | var res = new List(); 52 | 53 | foreach (var l in lists) 54 | { 55 | var s = string.Join(",", l.Select(x => x.Id).ToArray()); 56 | 57 | var r = await ApiClient.GetAsync( 58 | MakeUri( 59 | $"albums", 60 | ("ids", s), 61 | ("market", market)), 62 | this.Token); 63 | 64 | if (r.Response is MultipleAlbums albums) 65 | { 66 | res.AddRange(albums.Albums); 67 | } 68 | } 69 | 70 | return res; 71 | } 72 | 73 | /// 74 | public async Task> GetAlbumTracks(SpotifyUri albumUri, string market) 75 | { 76 | var r = await ApiClient.GetAsync>( 77 | MakeUri( 78 | $"albums/{albumUri.Id}/tracks", 79 | ("limit", "50"), 80 | ("offset", "0"), // TODO: Offset 81 | ("market", market)), 82 | this.Token); 83 | 84 | if (r.Response is Paging tracks) 85 | { 86 | return await tracks.LoadToList(this.Token); 87 | } 88 | return new List(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/FullPlaylist.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class FullPlaylist 11 | { 12 | /// 13 | /// Gets or sets a value indicating whether this is collaborative. 14 | /// 15 | [JsonProperty("collaborative")] 16 | public bool Collaborative { get; set; } 17 | 18 | /// 19 | /// Gets or sets the description. 20 | /// 21 | [JsonProperty("description")] 22 | public string Description { get; set; } 23 | 24 | /// 25 | /// Gets or sets the external urls. 26 | /// 27 | [JsonProperty("external_urls")] 28 | public Dictionary ExternalUrls { get; set; } 29 | 30 | /// 31 | /// Gets or sets the followers. 32 | /// 33 | [JsonProperty("followers")] 34 | public Followers Followers { get; set; } 35 | 36 | /// 37 | /// Gets or sets the href. 38 | /// 39 | [JsonProperty("href")] 40 | public string Href { get; set; } 41 | 42 | /// 43 | /// Gets or sets the identifier. 44 | /// 45 | [JsonProperty("id")] 46 | public string Id { get; set; } 47 | 48 | /// 49 | /// Gets or sets the images. 50 | /// 51 | [JsonProperty("images")] 52 | public List Images { get; set; } 53 | 54 | /// 55 | /// Gets or sets the name. 56 | /// 57 | [JsonProperty("name")] 58 | public string Name { get; set; } 59 | 60 | /// 61 | /// Gets or sets the owner. 62 | /// 63 | [JsonProperty("owner")] 64 | public PublicUser Owner { get; set; } 65 | 66 | /// 67 | /// Gets or sets a value indicating whether this is public. 68 | /// 69 | [JsonProperty("public")] 70 | public bool? Public { get; set; } 71 | 72 | /// 73 | /// Gets or sets the snapshot identifier. 74 | /// 75 | [JsonProperty("snapshot_id")] 76 | public string SnapshotId { get; set; } 77 | 78 | /// 79 | /// Gets or sets the tracks. 80 | /// 81 | [JsonProperty("tracks")] 82 | public Paging Tracks { get; set; } 83 | 84 | /// 85 | /// Gets or sets the type. 86 | /// 87 | [JsonProperty("type")] 88 | public string Type { get; set; } 89 | 90 | /// 91 | /// Gets or sets the URI. 92 | /// 93 | [JsonProperty("uri")] 94 | public SpotifyUri Uri { get; set; } 95 | 96 | /// 97 | /// Gets or sets the track list. 98 | /// 99 | public List TrackList { get; set; } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Artist/IArtistApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Artist 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Model; 6 | using Model.Enum; 7 | using Model.Uri; 8 | 9 | /// 10 | /// The artist api. 11 | /// 12 | public interface IArtistApi 13 | { 14 | /// 15 | /// Get Spotify catalog information for a single artist identified by their unique Spotify Uri. 16 | /// 17 | /// The for the artist. 18 | /// The retrieved Artist. 19 | Task GetArtist(SpotifyUri artistUri); 20 | 21 | /// 22 | /// Get Spotify catalog information for several artists based on their Spotify Uris. 23 | /// 24 | /// A list of the for the artists. Maximum: 50 IDs. 25 | /// The retrieved artists. 26 | Task> GetArtists(IList artistUris); 27 | 28 | /// 29 | /// Get Spotify catalog information about an artist’s albums. Optional parameters can be specified in the parameters to filter the response. 30 | /// 31 | /// The for the artist. 32 | /// Optional. A list of s that will be used to filter the response. If not supplied, all album types will be returned. 33 | /// Note multiple types can be selected like so: 'AlbumType.Album | AlbumType.AppearsOn'. 34 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 35 | /// Optional. The maximum results to return, or -1 to retrieve all items. 36 | /// Optional. The index of the first album to return. Default: 0 (i.e., the first album). 37 | /// The artists albums. 38 | Task> GetArtistAlbums( 39 | SpotifyUri artistUri, 40 | AlbumType albumTypes = AlbumType.Album | AlbumType.AppearsOn | AlbumType.Compilation | AlbumType.Single, 41 | string market = "", 42 | int maxResults = -1, 43 | int offset = 0); 44 | 45 | /// 46 | /// Get Spotify catalog information about an artist’s top tracks by country. 47 | /// 48 | /// The for the artist. 49 | /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 50 | /// The artists top track by country. 51 | Task> GetArtistsTopTracks(SpotifyUri artistUri, string market); 52 | 53 | /// 54 | /// Get Spotify catalog information about artists similar to a given artist. Similarity is based on analysis of the Spotify community’s listening history. 55 | /// 56 | /// The for the artist. 57 | /// The related artists for the specified . 58 | Task> GetArtistsRelatedArtists(SpotifyUri artistUri); 59 | } 60 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Api/BaseApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Dynamic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using Business; 9 | using Model.Auth; 10 | 11 | /// 12 | /// The used for all the other APIs. 13 | /// 14 | public abstract class BaseApi 15 | { 16 | /// 17 | /// Gets the base spotify URI 18 | /// 19 | protected const string BaseUri = "https://api.spotify.com/v1/"; 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// A valid . 25 | protected BaseApi(Token token) 26 | { 27 | if (token == null) 28 | { 29 | return; 30 | } 31 | 32 | // Validate token 33 | Validation.ValidateToken(token); 34 | this.Token = token; 35 | } 36 | 37 | /// 38 | /// Gets the . 39 | /// 40 | protected Token Token { get; private set; } 41 | 42 | #region Static methods 43 | 44 | /// 45 | /// Makes a Spotify url from the and the provided . 46 | /// 47 | /// The relative URL. 48 | /// The full request uri. 49 | protected static Uri MakeUri(string relativeUrl) 50 | { 51 | if (relativeUrl.StartsWith("/")) { relativeUrl = relativeUrl.Substring(1); } 52 | return new Uri(BaseUri + relativeUrl); 53 | } 54 | 55 | /// 56 | /// Makes a Spotify uri from the and the provided . 57 | /// Also adding any query parameters. 58 | /// 59 | /// The relative URL. 60 | /// A list of query parameters. 61 | /// Note: When a parameter value is null or empty, the query will not contain the query parameter. 62 | /// The full request uri. 63 | protected static Uri MakeUri(string relativeUrl, params(string queryParameter, string value)[] parameters) 64 | { 65 | if (relativeUrl.StartsWith("/")) { relativeUrl = relativeUrl.Substring(1); } 66 | 67 | var p = parameters.Where(x => !string.IsNullOrEmpty(x.queryParameter) && !string.IsNullOrEmpty(x.value)).ToList(); 68 | 69 | var queryParameters = string.Empty; 70 | if (p.Any()) 71 | { 72 | queryParameters += "?" + string.Join("&", p.Select(x => $"{x.queryParameter}={x.value}")); 73 | } 74 | 75 | return new Uri(BaseUri + relativeUrl + queryParameters); 76 | } 77 | 78 | #endregion Static methods 79 | 80 | /// 81 | /// Updates the token when you refreshed it. 82 | /// 83 | /// The updated token. 84 | protected void UpdateToken(Token token) 85 | { 86 | // Validate token 87 | Validation.ValidateToken(token); 88 | this.Token = token; 89 | } 90 | 91 | // TODO: Add Api GET,POST,ETC methods here. 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/FullAlbum.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class FullAlbum 11 | { 12 | /// 13 | /// Gets or sets the type of the album. 14 | /// 15 | [JsonProperty("album_type")] 16 | public string AlbumType { get; set; } 17 | 18 | /// 19 | /// Gets or sets the artists. 20 | /// 21 | [JsonProperty("artists")] 22 | public List Artists { get; set; } 23 | 24 | /// 25 | /// Gets or sets the available markets. 26 | /// 27 | [JsonProperty("available_markets")] 28 | public List AvailableMarkets { get; set; } 29 | 30 | /// 31 | /// Gets or sets the copyrights. 32 | /// 33 | [JsonProperty("copyrights")] 34 | public List Copyrights { get; set; } 35 | 36 | /// 37 | /// Gets or sets the external ids. 38 | /// 39 | [JsonProperty("external_ids")] 40 | public Dictionary ExternalIds { get; set; } 41 | 42 | /// 43 | /// Gets or sets the external urls. 44 | /// 45 | [JsonProperty("external_url")] 46 | public Dictionary ExternalUrls { get; set; } 47 | 48 | /// 49 | /// Gets or sets the genres. 50 | /// 51 | [JsonProperty("genres")] 52 | public List Genres { get; set; } 53 | 54 | /// 55 | /// Gets or sets the href. 56 | /// 57 | [JsonProperty("href")] 58 | public string Href { get; set; } 59 | 60 | /// 61 | /// Gets or sets the identifier. 62 | /// 63 | [JsonProperty("id")] 64 | public string Id { get; set; } 65 | 66 | /// 67 | /// Gets or sets the images. 68 | /// 69 | [JsonProperty("images")] 70 | public List Images { get; set; } 71 | 72 | /// 73 | /// Gets or sets the label. 74 | /// 75 | [JsonProperty("label")] 76 | public string Label { get; set; } 77 | 78 | /// 79 | /// Gets or sets the name. 80 | /// 81 | [JsonProperty("name")] 82 | public string Name { get; set; } 83 | 84 | /// 85 | /// Gets or sets the popularity. 86 | /// 87 | [JsonProperty("popularity")] 88 | public int Popularity { get; set; } 89 | 90 | /// 91 | /// Gets or sets the release date. 92 | /// 93 | [JsonProperty("release_date")] 94 | public string ReleaseDate { get; set; } 95 | 96 | /// 97 | /// Gets or sets the release date precision. 98 | /// 99 | [JsonProperty("release_date_precision")] 100 | public string ReleaseDatePrecision { get; set; } 101 | 102 | /// 103 | /// Gets or sets the tracks. 104 | /// 105 | [JsonProperty("tracks")] 106 | public Paging Tracks { get; set; } 107 | 108 | /// 109 | /// Gets or sets the type. 110 | /// 111 | [JsonProperty("type")] 112 | public string Type { get; set; } 113 | 114 | /// 115 | /// Gets or sets the URI. 116 | /// 117 | [JsonProperty("uri")] 118 | public SpotifyUri Uri { get; set; } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Player/PlayerApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Player 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Business; 7 | using Model; 8 | using Model.Auth; 9 | using Model.Enum; 10 | using Model.Uri; 11 | 12 | /// 13 | /// The . 14 | /// 15 | public class PlayerApi : BaseApi, IPlayerApi 16 | { 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// A valid . 21 | public PlayerApi(Token token) 22 | : base(token) 23 | { 24 | } 25 | 26 | /// 27 | public async Task> GetAvailableDevices() 28 | { 29 | var r = await ApiClient.GetAsync( 30 | MakeUri("me/player/devices"), this.Token); 31 | 32 | if (r.Response is DeviceList res) 33 | { 34 | return res.Devices; 35 | } 36 | return new List(); 37 | } 38 | 39 | /// 40 | public async Task GetCurrentlyPlayingContext(string market = "") 41 | { 42 | var r = await ApiClient.GetAsync( 43 | MakeUri( 44 | $"me/player", 45 | ("market", market)), 46 | this.Token); 47 | 48 | if (r.Response is CurrentlyPlayingContext res) 49 | { 50 | return res; 51 | } 52 | return new CurrentlyPlayingContext(); 53 | } 54 | 55 | /// 56 | public async Task GetCurrentlyPlaying(string market = "") 57 | { 58 | var r = await ApiClient.GetAsync( 59 | MakeUri( 60 | $"me/player/currently-playing", 61 | ("market", market)), 62 | this.Token); 63 | 64 | if (r.Response is CurrentlyPlaying res) 65 | { 66 | return res; 67 | } 68 | return new CurrentlyPlaying(); 69 | } 70 | 71 | /// 72 | public Task TransferPlayback(List devices, bool? play = null) 73 | { 74 | throw new NotImplementedException(); 75 | } 76 | 77 | /// 78 | public Task StartPlayback(Device device = null, SpotifyUri contextUri = null, List uris = null) 79 | { 80 | throw new NotImplementedException(); 81 | } 82 | 83 | /// 84 | public Task PausePlayback(Device device = null) 85 | { 86 | throw new NotImplementedException(); 87 | } 88 | 89 | /// 90 | public Task Next(Device device = null) 91 | { 92 | throw new NotImplementedException(); 93 | } 94 | 95 | /// 96 | public Task Previous(Device device = null) 97 | { 98 | throw new NotImplementedException(); 99 | } 100 | 101 | /// 102 | public Task Seek(int positionMs, Device device = null) 103 | { 104 | throw new NotImplementedException(); 105 | } 106 | 107 | /// 108 | public Task SetRepeat(RepeatState state, Device device = null) 109 | { 110 | throw new NotImplementedException(); 111 | } 112 | 113 | /// 114 | public Task SetVolume(int volumePercent, Device device = null) 115 | { 116 | throw new NotImplementedException(); 117 | } 118 | 119 | /// 120 | public Task SetShuffle(bool state, Device device = null) 121 | { 122 | throw new NotImplementedException(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Playlist/PlaylistApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Playlist 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Business; 7 | using Model; 8 | using Model.Auth; 9 | using Model.Uri; 10 | 11 | /// 12 | /// The . 13 | /// 14 | public class PlaylistApi : BaseApi, IPlaylistApi 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// A valid . 20 | public PlaylistApi(Token token) 21 | : base(token) 22 | { 23 | } 24 | 25 | /// 26 | public async Task> GetUsersPlaylist(SpotifyUri user, int maxResults, int offset = 0) 27 | { 28 | var r = await ApiClient.GetAsync>( 29 | MakeUri($"users/{user.Id}/playlists?limit=50&offset={offset}"), this.Token); 30 | 31 | if (r.Response is Paging res) 32 | { 33 | return await res.LoadToList(this.Token, maxResults); 34 | } 35 | return new List(); 36 | } 37 | 38 | /// 39 | public async Task> GetMyPlaylists(int maxResults, int offset = 0) 40 | { 41 | var r = await ApiClient.GetAsync>( 42 | MakeUri($"me/playlists?limit=50&offset={offset}"), this.Token); 43 | 44 | if (r.Response is Paging res) 45 | { 46 | return await res.LoadToList(this.Token, maxResults); 47 | } 48 | return new List(); 49 | } 50 | 51 | /// 52 | public async Task GetPlaylist(SpotifyUri playlistUri, string market) 53 | { 54 | var r = await ApiClient.GetAsync( 55 | MakeUri( 56 | $"playlists/{playlistUri.Id}", 57 | ("market", market)), 58 | this.Token); 59 | 60 | if (r.Response is FullPlaylist res) 61 | { 62 | return res; 63 | } 64 | 65 | return new FullPlaylist(); 66 | } 67 | 68 | /// 69 | public async Task> GetPlaylistTracks( 70 | SpotifyUri playlistUri, int maxResults, int offset, string market) 71 | { 72 | var r = await ApiClient.GetAsync>( 73 | MakeUri( 74 | $"playlists/{playlistUri.Id}/tracks?limit=100&offset={offset}", 75 | ("market", market)), 76 | this.Token); 77 | 78 | if (r.Response is Paging res) 79 | { 80 | return await res.LoadToList(this.Token, maxResults); 81 | } 82 | return new List(); 83 | } 84 | 85 | /// 86 | public async Task CreatePlaylist( 87 | SpotifyUri user, string name, bool @public, string description) 88 | { 89 | var r = await ApiClient.PostAsync( 90 | MakeUri($"users/{user.Id}/playlists"), 91 | new PlaylistCreate 92 | { 93 | Name = name, 94 | Public = @public, 95 | Description = description, 96 | }, this.Token); 97 | 98 | if (r.Response is FullPlaylist playlist) 99 | { 100 | return playlist; 101 | } 102 | return new FullPlaylist(); 103 | } 104 | 105 | /// 106 | public Task AddTracksToPlaylist(SpotifyUri playlistUri, IList tracks, int? position = null) 107 | { 108 | throw new NotImplementedException(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Artist/ArtistApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Artist 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Business; 7 | using Model; 8 | using Model.Auth; 9 | using Model.Enum; 10 | using Model.Uri; 11 | 12 | /// 13 | /// 14 | /// The . 15 | /// 16 | public class ArtistApi : BaseApi, IArtistApi 17 | { 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// A valid . 22 | public ArtistApi(Token token) 23 | : base(token) 24 | { 25 | } 26 | 27 | /// 28 | public async Task GetArtist(SpotifyUri artistUri) 29 | { 30 | var r = await ApiClient.GetAsync( 31 | MakeUri($"artists/{artistUri.Id}"), 32 | this.Token); 33 | 34 | if (r.Response is FullArtist res) 35 | { 36 | return res; 37 | } 38 | return new FullArtist(); 39 | } 40 | 41 | /// 42 | public async Task> GetArtists(IList artistUris) 43 | { 44 | Validation.ValidateList(artistUris, 0, 50); 45 | var ids = artistUris.Select(x => x.Id).ToList().AsSingleString(); 46 | var r = await ApiClient.GetAsync( 47 | MakeUri($"artists?ids={ids}"), 48 | this.Token); 49 | 50 | if (r.Response is List res) 51 | { 52 | return res; 53 | } 54 | return new List(); 55 | } 56 | 57 | /// 58 | public async Task> GetArtistAlbums(SpotifyUri artistUri, AlbumType albumTypes, string market, int maxResults, int offset) 59 | { 60 | var albumTypeString = string.Empty; 61 | if (albumTypes.HasFlag(AlbumType.Album)) albumTypeString += "album,"; 62 | if (albumTypes.HasFlag(AlbumType.AppearsOn)) albumTypeString += "appears_on,"; 63 | if (albumTypes.HasFlag(AlbumType.Compilation)) albumTypeString += "compilation,"; 64 | if (albumTypes.HasFlag(AlbumType.Single)) albumTypeString += "compilation,"; 65 | albumTypeString = albumTypeString.Remove(albumTypeString.Length - 1); 66 | 67 | var r = await ApiClient.GetAsync>( 68 | MakeUri( 69 | $"artists/{artistUri.Id}/albums?{albumTypeString}", 70 | ("album_type", albumTypeString), 71 | ("limit", "50"), 72 | ("offset", offset.ToString()), 73 | ("market", market)), 74 | this.Token); 75 | 76 | if (r.Response is Paging res) 77 | { 78 | return await res.LoadToList(this.Token); 79 | } 80 | return new List(); 81 | } 82 | 83 | /// 84 | public async Task> GetArtistsTopTracks(SpotifyUri artistUri, string market) 85 | { 86 | var r = await ApiClient.GetAsync>( 87 | MakeUri( 88 | $"artists/{artistUri.Id}/top-tracks", 89 | ("market", market)), 90 | this.Token); 91 | 92 | if (r.Response is List res) 93 | { 94 | return res; 95 | } 96 | return new List(); 97 | } 98 | 99 | /// 100 | public async Task> GetArtistsRelatedArtists(SpotifyUri artistUri) 101 | { 102 | var r = await ApiClient.GetAsync>( 103 | MakeUri($"artists/{artistUri.Id}/related-artists"), 104 | this.Token); 105 | 106 | if (r.Response is List res) 107 | { 108 | return res; 109 | } 110 | return new List(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /SpotifyWebApi/Business/Validation.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Business 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net; 6 | using System.Net.Http; 7 | using Model.Auth; 8 | using Model.Exception; 9 | 10 | /// 11 | /// A static class used for validation a variance of objects. 12 | /// 13 | internal static class Validation 14 | { 15 | /// 16 | /// Validates a list and throws a when failed. 17 | /// 18 | /// The list to validate 19 | /// The minimum number of items in the list. 20 | /// The maximum number of items in the list. 21 | /// Type of the list to validate. 22 | /// Throws a validation exception when the list is null or invalid. 23 | public static void ValidateList(ICollection list, int min = -1, int max = -1) 24 | { 25 | if (list == null) { throw new ValidationException("The list is null."); } 26 | 27 | var c = list.Count; 28 | if (c == 0) { throw new ValidationException("The list is empty."); } 29 | if (c < min) { throw new ValidationException("The list is too small."); } 30 | if (c > max) { throw new ValidationException("The list is too big."); } 31 | } 32 | 33 | /// 34 | /// Validates a integer and throws a when failed. 35 | /// 36 | /// The value to validate 37 | /// The minimum of the value. 38 | /// The maximum of the value. 39 | /// Throws a validation exception when the value is invalid. 40 | public static void ValidateInteger(int value, int min = int.MinValue, int max = int.MaxValue) 41 | { 42 | if (value < min) { throw new ValidationException("The value is too small."); } 43 | if (value > max) { throw new ValidationException("The value is too big."); } 44 | } 45 | 46 | /// 47 | /// Validates a . 48 | /// Note this function does not check for expired tokens! 49 | /// 50 | /// The token to validate. 51 | /// Throws a validation exception when the value is invalid. 52 | public static void ValidateToken(Token token) 53 | { 54 | if (token == null) { throw new ValidationException("The token is null."); } 55 | 56 | if (token.Type != "Bearer") throw new ValidationException("The token type is not \"Bearer\"."); 57 | 58 | if (string.IsNullOrWhiteSpace(token.AccessToken)) 59 | throw new ValidationException("The token's 'AccessToken' is empty or null"); 60 | 61 | // if (string.IsNullOrWhiteSpace(token.RefreshToken)) 62 | // throw new ValidationException("The token's 'RefreshToken' is empty or null"); 63 | } 64 | 65 | /// 66 | /// Validates a and throws errors based on the code. 67 | /// 68 | /// The status code to validate. 69 | /// The response of the http request. 70 | public static void ValidateResponseCode(HttpStatusCode statusCode, string response) 71 | { 72 | switch (statusCode) 73 | { 74 | case HttpStatusCode.BadRequest: 75 | throw new BadRequestException(response); 76 | case HttpStatusCode.Unauthorized: 77 | throw new UnauthorizedAccessException(response); 78 | case HttpStatusCode.Forbidden: 79 | throw new ForbiddenException(response); 80 | case HttpStatusCode.NotFound: 81 | throw new NotFoundException(response); 82 | case HttpStatusCode.InternalServerError: 83 | throw new InternalServerErrorException(response); 84 | case HttpStatusCode.BadGateway: 85 | throw new BadGatewayException(response); 86 | case HttpStatusCode.ServiceUnavailable: 87 | throw new ServiceUnavailableException(response); 88 | } 89 | 90 | // This status code is not in the HttpStatusCode Enum 91 | if ((int)statusCode == 429) 92 | { 93 | throw new TooManyRequestsException(response); 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Auth/Token.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | [assembly: InternalsVisibleTo("SpotifyWebApi.Auth")] 3 | 4 | namespace SpotifyWebApi.Model.Auth 5 | { 6 | using System; 7 | using Newtonsoft.Json; 8 | using Uri; 9 | 10 | /// 11 | /// The class. 12 | /// 13 | public class Token 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public Token() 19 | { 20 | } 21 | 22 | /// 23 | /// Gets or sets the access token. 24 | /// 25 | [JsonProperty("access_token")] 26 | public string AccessToken { get; set; } 27 | 28 | /// 29 | /// Gets or sets the type. 30 | /// 31 | [JsonProperty("token_type")] 32 | public string Type { get; set; } 33 | 34 | /// 35 | /// Gets or sets the scope. 36 | /// 37 | [JsonProperty("scope")] 38 | public string Scope { get; set; } 39 | 40 | /// 41 | /// Gets or sets the expires in. 42 | /// 43 | [JsonProperty("expires_in")] 44 | public int ExpiresIn { get; set; } 45 | 46 | /// 47 | /// Gets or sets the refresh token. 48 | /// 49 | [JsonProperty("refresh_token")] 50 | public string RefreshToken { get; set; } 51 | 52 | /// 53 | /// Gets the token generated. 54 | /// 55 | [JsonProperty("token_generated")] 56 | public DateTime TokenGenerated { get; set; } = DateTime.UtcNow; 57 | 58 | /// 59 | /// Gets a value indicating whether this instance is expired. 60 | /// 61 | [JsonIgnore] 62 | public bool IsExpired => DateTime.UtcNow > this.TokenGenerated.AddSeconds(this.ExpiresIn); 63 | 64 | /// 65 | /// Gets a value indicating wheter this token can be used to access personal data. 66 | /// 67 | [JsonProperty("can_access_personal_data")] 68 | public bool CanAccessPersonalData { get; internal set; } = true; 69 | 70 | /// 71 | /// Gets a value indicating the authentication type of this token. 72 | /// 73 | [JsonProperty("token_authentication_type")] 74 | public TokenAuthenticationType AuthenticationType { get; internal set; } 75 | 76 | /// 77 | /// Creates a token. 78 | /// 79 | /// The access token. 80 | /// The refresh token. 81 | /// The token type. 82 | /// The expires in value. 83 | /// The datetime the token was generated. 84 | /// The scope of the token. 85 | /// If the token can access personal data. 86 | /// The authentication type of the token. 87 | /// The newly made . 88 | public static Token Make( 89 | string accessToken, 90 | string refreshToken, 91 | string tokenType = "Bearer", 92 | int expiresIn = 3600, 93 | DateTime? tokenGenerated = null, 94 | string scope = null, 95 | bool canAccessPersonalData = true, 96 | TokenAuthenticationType authenticationType = TokenAuthenticationType.AuthorizationCode) 97 | { 98 | return new Token 99 | { 100 | AccessToken = accessToken, 101 | RefreshToken = refreshToken, 102 | Type = tokenType, 103 | ExpiresIn = expiresIn, 104 | TokenGenerated = tokenGenerated ?? DateTime.UtcNow, 105 | Scope = scope ?? string.Empty, 106 | CanAccessPersonalData = canAccessPersonalData, 107 | AuthenticationType = authenticationType 108 | }; 109 | } 110 | 111 | /// 112 | /// Creates a header string from this instance. 113 | /// 114 | /// The web header string. 115 | public string ToHeaderString() 116 | { 117 | return this.Type + " " + this.AccessToken; 118 | } 119 | 120 | /// 121 | public override string ToString() 122 | { 123 | return $"AccessToken: {this.AccessToken}, RefreshToken: {this.RefreshToken}"; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/FullTrack.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model 2 | { 3 | using System.Collections.Generic; 4 | using Newtonsoft.Json; 5 | using Uri; 6 | 7 | /// 8 | /// The class. 9 | /// 10 | public class FullTrack 11 | { 12 | /// 13 | /// Gets or sets the album. 14 | /// 15 | [JsonProperty("album")] 16 | public SimpleAlbum Album { get; set; } 17 | 18 | /// 19 | /// Gets or sets the artists. 20 | /// 21 | [JsonProperty("artists")] 22 | public List Artists { get; set; } 23 | 24 | /// 25 | /// Gets or sets the available markets. 26 | /// 27 | [JsonProperty("available_markets")] 28 | public List AvailableMarkets { get; set; } 29 | 30 | /// 31 | /// Gets or sets the disc number. 32 | /// 33 | [JsonProperty("disc_number")] 34 | public int DiscNumber { get; set; } 35 | 36 | /// 37 | /// Gets or sets the duration ms. 38 | /// 39 | [JsonProperty("duration_ms")] 40 | public int DurationMs { get; set; } 41 | 42 | /// 43 | /// Gets or sets the explicit. 44 | /// 45 | [JsonProperty("explicit")] 46 | public string Explicit { get; set; } 47 | 48 | /// 49 | /// Gets or sets the external ids. 50 | /// 51 | [JsonProperty("external_ids")] 52 | public Dictionary ExternalIds { get; set; } 53 | 54 | /// 55 | /// Gets or sets the external urls. 56 | /// 57 | [JsonProperty("external_urls")] 58 | public Dictionary ExternalUrls { get; set; } 59 | 60 | /// 61 | /// Gets or sets the href. 62 | /// 63 | [JsonProperty("href")] 64 | public string Href { get; set; } 65 | 66 | /// 67 | /// Gets or sets the identifier. 68 | /// 69 | [JsonProperty("id")] 70 | public string Id { get; set; } 71 | 72 | /// 73 | /// Gets or sets a value indicating whether this instance is playable. 74 | /// 75 | [JsonProperty("is_playable")] 76 | public bool IsPlayable { get; set; } 77 | 78 | /// 79 | /// Gets or sets the linked from. 80 | /// 81 | [JsonProperty("linked_from")] 82 | public LinkedFrom LinkedFrom { get; set; } 83 | 84 | /// 85 | /// Gets or sets the name. 86 | /// 87 | [JsonProperty("name")] 88 | public string Name { get; set; } 89 | 90 | /// 91 | /// Gets or sets the popularity. 92 | /// 93 | [JsonProperty("popularity")] 94 | public int Popularity { get; set; } 95 | 96 | /// 97 | /// Gets or sets the preview URL. 98 | /// 99 | [JsonProperty("preview_url")] 100 | public string PreviewUrl { get; set; } 101 | 102 | /// 103 | /// Gets or sets the track number. 104 | /// 105 | [JsonProperty("track_number")] 106 | public int TrackNumber { get; set; } 107 | 108 | /// 109 | /// Gets or sets the type. 110 | /// 111 | [JsonProperty("type")] 112 | public string Type { get; set; } 113 | 114 | /// 115 | /// Gets or sets the URI. 116 | /// 117 | [JsonProperty("uri")] 118 | public SpotifyUri Uri { get; set; } 119 | 120 | /// 121 | /// Returns a hash code for this instance. 122 | /// 123 | /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 124 | public override int GetHashCode() 125 | { 126 | if (this.Id != null) 127 | return this.Id.GetHashCode(); 128 | else 129 | return 0; 130 | } 131 | 132 | /// 133 | /// Determines whether the specified is equal to this instance. 134 | /// 135 | /// The object to compare with the current object. 136 | /// true if the specified is equal to this instance; otherwise, false. 137 | public override bool Equals(object obj) 138 | { 139 | return this.Id.Equals(obj); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Playlist/IPlaylistApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Playlist 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Model; 6 | using Model.Enum; 7 | using Model.Uri; 8 | 9 | /// 10 | /// The playlist api. 11 | /// 12 | public interface IPlaylistApi 13 | { 14 | /// 15 | /// Get a list of the playlists owned or followed by a Spotify user. 16 | /// 17 | /// The of the user. 18 | /// Optional. The maximum results to return, or -1 to retrieve all items. 19 | /// Optional. The index of the first playlist to return. 20 | /// Default: 0 (the first object). 21 | /// Maximum offset: 100.000. Use with limit to get the next set of playlists. 22 | /// A list of s. 23 | Task> GetUsersPlaylist(SpotifyUri user, int maxResults = -1, int offset = 0); 24 | 25 | /// 26 | /// Get a list of the playlists owned or followed by the current Spotify user. 27 | /// 28 | /// Optional. The maximum results to return, or -1 to retrieve all items. 29 | /// Optional. The index of the first playlist to return. 30 | /// Default: 0 (the first object). 31 | /// Maximum offset: 100.000. Use with limit to get the next set of playlists. 32 | /// A list of s. 33 | Task> GetMyPlaylists(int maxResults = -1, int offset = 0); 34 | 35 | /// 36 | /// Get a playlist owned by a Spotify user. 37 | /// 38 | /// The for the playlist. 39 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 40 | /// A object. 41 | Task GetPlaylist(SpotifyUri playlistUri, string market = null); 42 | 43 | /// 44 | /// Get full details of the tracks of a playlist owned by a Spotify user. 45 | /// 46 | /// The for the playlist. 47 | /// Optional. The maximum results to return, or -1 to retrieve all items. 48 | /// Optional. The index of the first track to return. Default: 0 (the first object). 49 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 50 | /// A list of objects. 51 | Task> GetPlaylistTracks( 52 | SpotifyUri playlistUri, int maxResults = -1, int offset = 0, string market = null); 53 | 54 | /// 55 | /// Create a playlist for a Spotify user. 56 | /// 57 | /// The of the user. 58 | /// The name for the new playlist, for example "Your Coolest Playlist". This name does not need to be unique; a user may have several playlists with the same name. 59 | /// Optional. If true the playlist will be public, if false it will be private. 60 | /// Optional. Value for playlist description as displayed in Spotify Clients and in the Web API. 61 | /// The created . 62 | /// 63 | /// Note: that the playlist will be empty until you add tracks. 64 | /// Note: To be able to create private playlists, the user must have granted the scope. 65 | /// 66 | Task CreatePlaylist( 67 | SpotifyUri user, string name, bool @public = true, string description = ""); 68 | 69 | /// 70 | /// Add one or more tracks to a user’s playlist. 71 | /// 72 | /// The for the playlist. 73 | /// A list of 's to add to the playlist. 74 | /// Optional. The position to insert the tracks, a zero-based index. 75 | /// Tha add tracks to playlist task. 76 | Task AddTracksToPlaylist(SpotifyUri playlistUri, IList tracks, int? position = null); 77 | 78 | // TODO: More functions: 79 | // https://developer.spotify.com/web-api/remove-tracks-playlist/ 80 | // https://developer.spotify.com/web-api/reorder-playlists-tracks/ 81 | // https://developer.spotify.com/web-api/replace-playlists-tracks/ 82 | // https://developer.spotify.com/web-api/change-playlist-details/ 83 | } 84 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | build/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | 257 | # CodeRush 258 | .cr/ 259 | 260 | # Python Tools for Visual Studio (PTVS) 261 | __pycache__/ 262 | *.pyc 263 | 264 | # Custom project ignore 265 | Examples/ExampleNet47/App.config 266 | coverage.json 267 | SpotifyWebApi/FodyWeavers.xsd 268 | -------------------------------------------------------------------------------- /SpotifyWebApi/Business/ApiClient.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Business 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Model; 9 | using Model.Auth; 10 | using Newtonsoft.Json; 11 | using Newtonsoft.Json.Converters; 12 | 13 | /// 14 | /// The class used for communicating with a REST service. 15 | /// 16 | internal static class ApiClient 17 | { 18 | /// 19 | /// Gets from an uri asynchronously. 20 | /// 21 | /// The expected return object. 22 | /// The request URI. 23 | /// Optional. A valid . 24 | /// The response of the HTTP GET. 25 | public static async Task GetAsync(Uri uri, Token token) 26 | { 27 | using var client = MakeHttpClient(token); 28 | using var response = await client.GetAsync(uri); 29 | var responseString = await response.Content.ReadAsStringAsync(); 30 | 31 | Validation.ValidateResponseCode(response.StatusCode, responseString); 32 | 33 | return WebResponse.Make(DeserializeObject(responseString), response.StatusCode); 34 | } 35 | 36 | /// 37 | /// Posts to an uri asynchronously. 38 | /// 39 | /// The expected return object. 40 | /// The request URI. 41 | /// The body of the request. 42 | /// Optional. A valid . 43 | /// The response of the HTTP POST. 44 | public static async Task PostAsync(Uri uri, object body, Token token = null) 45 | { 46 | using var client = MakeHttpClient(token); 47 | using var content = new StringContent(SerializeObject(body), Encoding.UTF8, "application/json"); 48 | 49 | using var response = await client.PostAsync(uri, content); 50 | var responseString = await response.Content.ReadAsStringAsync(); 51 | 52 | Validation.ValidateResponseCode(response.StatusCode, responseString); 53 | 54 | return WebResponse.Make(DeserializeObject(responseString), response.StatusCode); 55 | } 56 | 57 | /// 58 | /// Puts to an uri asynchronously. 59 | /// 60 | /// The expected return object. 61 | /// The request URI. 62 | /// The body of the request. 63 | /// Optional. A valid . 64 | /// The response of the HTTP PUT. 65 | public static async Task PutAsync(Uri uri, object body, Token token = null) 66 | { 67 | using var client = MakeHttpClient(token); 68 | using var content = new StringContent(SerializeObject(body), Encoding.UTF8, "application/json"); 69 | 70 | using var response = await client.PutAsync(uri, content); 71 | var responseString = await response.Content.ReadAsStringAsync(); 72 | 73 | Validation.ValidateResponseCode(response.StatusCode, responseString); 74 | 75 | return WebResponse.Make(DeserializeObject(responseString), response.StatusCode); 76 | } 77 | 78 | /// 79 | /// Deletes from an uri asynchronously. 80 | /// 81 | /// The expected return object. 82 | /// The request URI. 83 | /// Optional. A valid . 84 | /// The response of the HTTP DELETE. 85 | public static async Task DeleteAsync(Uri uri, Token token = null) 86 | { 87 | using var client = MakeHttpClient(token); 88 | using var response = await client.DeleteAsync(uri); 89 | var responseString = await response.Content.ReadAsStringAsync(); 90 | 91 | Validation.ValidateResponseCode(response.StatusCode, responseString); 92 | 93 | return WebResponse.Make(DeserializeObject(responseString), response.StatusCode); 94 | } 95 | 96 | /// 97 | /// Makes the HTTP client. 98 | /// 99 | /// Optional. A valid . 100 | /// A newly created containing the authentication provided by the token. 101 | private static HttpClient MakeHttpClient(Token token = null) 102 | { 103 | var client = new HttpClient(); 104 | client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/x-www-form-urlencoded")); 105 | 106 | if (token != null) 107 | client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(token.ToHeaderString()); 108 | 109 | return client; 110 | } 111 | 112 | /// 113 | /// Custom deserialization with StringEnumConverter. 114 | /// 115 | /// The type of the expected object. 116 | /// The json to deserialize. 117 | /// The deserialized object. 118 | private static T DeserializeObject(string json) 119 | { 120 | return JsonConvert.DeserializeObject(json, new StringEnumConverter(true)); 121 | } 122 | 123 | /// 124 | /// Custom serialization with StringEnumConverter. 125 | /// 126 | /// The object to serialize. 127 | /// The serialized JSON. 128 | private static string SerializeObject(object obj) 129 | { 130 | return JsonConvert.SerializeObject(obj, new StringEnumConverter(true)); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Enum/Scopes.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Enum 2 | { 3 | /// 4 | /// Spotify scopes, for more information visit https://developer.spotify.com/documentation/general/guides/scopes/ 5 | /// 6 | public static class Scopes 7 | { 8 | /// 9 | /// The default scope separator. 10 | /// 11 | public const string ScopeSeparator = " "; 12 | 13 | #region Images 14 | 15 | /// 16 | /// Write access to user-provided images. 17 | /// 18 | public const string UgcImageUpload = "ugc-image-upload"; 19 | 20 | /// 21 | /// All scopes in the image category. 22 | /// 23 | public const string AllImages = UgcImageUpload; 24 | 25 | #endregion 26 | 27 | #region Spotify connect 28 | 29 | /// 30 | /// UserReadPlaybackState 31 | /// 32 | public const string UserReadPlaybackState = "user-read-playback-state"; 33 | 34 | /// 35 | /// UserModifyPlaybackState 36 | /// 37 | public const string UserModifyPlaybackState = "user-modify-playback-state"; 38 | 39 | /// 40 | /// UserReadCurrentlyPlaying 41 | /// 42 | public const string UserReadCurrentlyPlaying = "user-read-currently-playing"; 43 | 44 | /// 45 | /// All scopes from the Spotify Connect category. 46 | /// 47 | public const string AllSpotifyConnect = 48 | UserReadPlaybackState + Sep + UserModifyPlaybackState + Sep + UserReadCurrentlyPlaying; 49 | 50 | #endregion 51 | 52 | #region Playback 53 | 54 | /// 55 | /// Streaming 56 | /// 57 | public const string Streaming = "streaming"; 58 | 59 | /// 60 | /// AppRemoteControl 61 | /// 62 | public const string AppRemoteControl = "app-remote-control"; 63 | 64 | /// 65 | /// All scopes from the Playback category. 66 | /// 67 | public const string AllPlayback = Streaming + Sep + AppRemoteControl; 68 | 69 | #endregion 70 | 71 | #region Users 72 | 73 | /// 74 | /// UserReadEmail 75 | /// 76 | public const string UserReadEmail = "user-read-email"; 77 | 78 | /// 79 | /// UserReadPrivate 80 | /// 81 | public const string UserReadPrivate = "user-read-private"; 82 | 83 | /// 84 | /// All scopes from the Users category. 85 | /// 86 | public const string AllUser = UserReadEmail + Sep + UserReadPrivate; 87 | 88 | #endregion 89 | 90 | #region Playlists 91 | 92 | /// 93 | /// PlaylistReadCollaborative 94 | /// 95 | public const string PlaylistReadCollaborative = "playlist-read-collaborative"; 96 | 97 | /// 98 | /// PlaylistModifyPublic 99 | /// 100 | public const string PlaylistModifyPublic = "playlist-modify-public"; 101 | 102 | /// 103 | /// PlaylistReadPrivate 104 | /// 105 | public const string PlaylistReadPrivate = "playlist-read-private"; 106 | 107 | /// 108 | /// PlaylistModifyPrivate 109 | /// 110 | public const string PlaylistModifyPrivate = "playlist-modify-private"; 111 | 112 | /// 113 | /// All scopes from the Playlist category. 114 | /// 115 | public const string AllPlaylist = PlaylistReadCollaborative + Sep + PlaylistModifyPublic + Sep + 116 | PlaylistReadPrivate + Sep + PlaylistModifyPrivate; 117 | #endregion 118 | 119 | #region Library 120 | 121 | /// 122 | /// UserLibraryModify 123 | /// 124 | public const string UserLibraryModify = "user-library-modify"; 125 | 126 | /// 127 | /// UserLibraryRead 128 | /// 129 | public const string UserLibraryRead = "user-library-read"; 130 | 131 | /// 132 | /// All scopes from the Library category. 133 | /// 134 | public const string AllLibrary = UserLibraryModify + Sep + UserLibraryRead; 135 | 136 | #endregion 137 | 138 | #region Listening history 139 | 140 | /// 141 | /// UserTopRead 142 | /// 143 | public const string UserTopRead = "user-top-read"; 144 | 145 | /// 146 | /// UserReadRecentlyPlayed 147 | /// 148 | public const string UserReadRecentlyPlayed = "user-read-recently-played"; 149 | 150 | /// 151 | /// All scopes from the Listening history category. 152 | /// 153 | public const string AllListeningHistory = UserTopRead + Sep + UserReadRecentlyPlayed; 154 | #endregion 155 | 156 | #region Follow 157 | 158 | /// 159 | /// UserFollowRead 160 | /// 161 | public const string UserFollowRead = "user-follow-read"; 162 | 163 | /// 164 | /// UserFollowModify 165 | /// 166 | public const string UserFollowModify = "user-follow-modify"; 167 | 168 | /// 169 | /// All scopes from the Follow category. 170 | /// 171 | public const string AllFollow = UserFollowRead + Sep + UserFollowModify; 172 | 173 | #endregion 174 | 175 | /// 176 | /// All scopes. 177 | /// 178 | public const string All = AllImages + Sep + AllSpotifyConnect + Sep + AllPlayback + Sep + AllUser + Sep + 179 | AllPlaylist + Sep + AllLibrary + Sep + AllListeningHistory + Sep + AllFollow; 180 | 181 | /// 182 | /// Scope separator for ease of use. 183 | /// 184 | private const string Sep = ScopeSeparator; 185 | } 186 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Api/Player/IPlayerApi.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Api.Player 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Model; 6 | using Model.Enum; 7 | using Model.Uri; 8 | 9 | /// 10 | /// The player api. 11 | /// 12 | /// These endpoints are in Beta. 13 | public interface IPlayerApi 14 | { 15 | /// 16 | /// Get information about a user’s available devices. 17 | /// 18 | /// A list of available devices. 19 | Task> GetAvailableDevices(); 20 | 21 | /// 22 | /// Get information about the user’s current playback state, including track, track progress, and active device. 23 | /// 24 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 25 | /// The users instance. 26 | Task GetCurrentlyPlayingContext(string market = ""); 27 | 28 | /// 29 | /// Get the object currently being played on the user’s Spotify account. 30 | /// 31 | /// Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. 32 | /// The users object. 33 | Task GetCurrentlyPlaying(string market = ""); 34 | 35 | /// 36 | /// Transfer playback to a new device and determine if it should start playing. 37 | /// 38 | /// Required. A list containing the devices on which playback should be started/transferred. 39 | /// NOTE: Although an list is accepted, only a single is currently supported. 40 | /// Optional. 41 | /// True: ensure playback happens on new device. 42 | /// False or not provided: keep the current playback state. 43 | /// A representing the asynchronous operation. 44 | Task TransferPlayback(List devices, bool? play = null); 45 | 46 | /// 47 | /// Start a new context or resume current playback on the user’s active device. 48 | /// TODO: Add offset parameter. 49 | /// 50 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 51 | /// Optional. Spotify URI of the context to play. Valid contexts are albums, artists & playlists. 52 | /// Optional. A list of the Spotify track URIs to play. 53 | /// A representing the asynchronous operation. 54 | Task StartPlayback(Device device = null, SpotifyUri contextUri = null, List uris = null); 55 | 56 | /// 57 | /// Pause playback on the user’s account. 58 | /// 59 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 60 | /// A representing the asynchronous operation. 61 | Task PausePlayback(Device device = null); 62 | 63 | /// 64 | /// Skips to next track in the user’s queue. 65 | /// 66 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 67 | /// A representing the asynchronous operation. 68 | Task Next(Device device = null); 69 | 70 | /// 71 | /// Skips to previous track in the user’s queue. 72 | /// Note that this will ALWAYS skip to the previous track, regardless of the current track’s progress. 73 | /// Returning to the start of the current track should be performed using the function. 74 | /// 75 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 76 | /// A representing the asynchronous operation. 77 | Task Previous(Device device = null); 78 | 79 | /// 80 | /// Seeks to the given position in the user’s currently playing track. 81 | /// 82 | /// The position in milliseconds to seek to. Must be a positive number. Passing in a position that is greater than the length of the track will cause the player to start playing the next song. 83 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 84 | /// A representing the asynchronous operation. 85 | Task Seek(int positionMs, Device device = null); 86 | 87 | /// 88 | /// Set the repeat mode for the user’s playback. Options are repeat-track, repeat-context, and off. 89 | /// 90 | /// The state of repeat. 91 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 92 | /// A representing the asynchronous operation. 93 | Task SetRepeat(RepeatState state, Device device = null); 94 | 95 | /// 96 | /// Set the volume for the user’s current playback device. 97 | /// 98 | /// The volume to set. Must be a value from 0 to 100 inclusive. 99 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 100 | /// A representing the asynchronous operation. 101 | Task SetVolume(int volumePercent, Device device = null); 102 | 103 | /// 104 | /// Toggle shuffle on or off for user’s playback. 105 | /// 106 | /// True: Shuffle user's playback. False: Do not shuffle user's playback 107 | /// Optional. The device this command is targeting. If not supplied, the user's currently active device is the target. 108 | /// A representing the asynchronous operation. 109 | Task SetShuffle(bool state, Device device = null); 110 | } 111 | } -------------------------------------------------------------------------------- /SpotifyWebApi/Model/Uri/SpotifyUri.cs: -------------------------------------------------------------------------------- 1 | namespace SpotifyWebApi.Model.Uri 2 | { 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Runtime.Serialization; 8 | using Exception; 9 | 10 | /// 11 | /// The class. 12 | /// 13 | [DataContract] 14 | public class SpotifyUri 15 | { 16 | #region Constructor 17 | 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// The URI. 22 | /// 23 | /// Uri was not correct! 24 | /// or 25 | /// Uri was not a spotify uri! 26 | /// 27 | private SpotifyUri(string uri) 28 | { 29 | this.FullUri = uri; 30 | 31 | var split = uri.Split(':'); 32 | this.Domain = split[0]; 33 | 34 | if (this.Domain.Equals("spotify")) 35 | { 36 | if (split.Length == 5) 37 | { 38 | this.Type = UriType.Playlist; 39 | } 40 | else 41 | { 42 | try 43 | { 44 | this.Type = (UriType)Enum.Parse(typeof(UriType), split[1], true); 45 | } 46 | catch (Exception) 47 | { 48 | throw new InvalidUriException("Uri was not correct!"); 49 | } 50 | } 51 | 52 | switch (this.Type) 53 | { 54 | case UriType.User: 55 | this.Id = split[2]; 56 | break; 57 | case UriType.Track: 58 | this.Id = split[2]; 59 | break; 60 | case UriType.Artist: 61 | this.Id = split[2]; 62 | break; 63 | case UriType.Album: 64 | this.Id = split[2]; 65 | break; 66 | case UriType.Playlist: 67 | if (split.Length == 5) 68 | { 69 | this.Id = split[4]; 70 | } 71 | else 72 | { 73 | this.Id = split[2]; 74 | } 75 | break; 76 | case UriType.Local: 77 | break; 78 | } 79 | } 80 | else 81 | { 82 | throw new InvalidUriException($"The provided uri is not a spotify uri (uri: {uri})."); 83 | } 84 | } 85 | 86 | #endregion Constructor 87 | 88 | #region Properties 89 | 90 | /// 91 | /// Gets the domain. 92 | /// 93 | [DataMember] 94 | public string Domain { get; private set; } 95 | 96 | /// 97 | /// Gets the type. 98 | /// 99 | [DataMember] 100 | public UriType Type { get; private set; } 101 | 102 | /// 103 | /// Gets the user identifier. 104 | /// 105 | [DataMember] 106 | [Obsolete("The userId property isn't needed anymore for creating a Playlist Uri.")] 107 | public string UserId { get; private set; } 108 | 109 | /// 110 | /// Gets the identifier. 111 | /// 112 | [DataMember] 113 | public string Id { get; private set; } 114 | 115 | /// 116 | /// Gets the full URI. 117 | /// 118 | [DataMember] 119 | public string FullUri { get; private set; } 120 | 121 | #endregion Properties 122 | 123 | #region Conversion 124 | 125 | /// 126 | /// Implicit operator to convert a string to a . 127 | /// 128 | /// The uri to convert. 129 | public static implicit operator SpotifyUri(string uri) 130 | { 131 | return SpotifyUri.Make(uri); 132 | } 133 | 134 | #endregion Conversion 135 | 136 | #region Methods 137 | 138 | /// 139 | /// Creates a from the given id and type. 140 | /// 141 | /// The id. 142 | /// The . 143 | /// A new instance of . 144 | public static SpotifyUri Make(string id, UriType type) 145 | { 146 | if (string.IsNullOrEmpty(id)) throw new ArgumentException("id is null or empty."); 147 | 148 | switch (type) 149 | { 150 | case UriType.User: 151 | return new SpotifyUri($"spotify:user:{id}"); 152 | case UriType.Track: 153 | return new SpotifyUri($"spotify:track:{id}"); 154 | case UriType.Artist: 155 | return new SpotifyUri($"spotify:artist:{id}"); 156 | case UriType.Album: 157 | return new SpotifyUri($"spotify:album:{id}"); 158 | case UriType.Playlist: 159 | return new SpotifyUri($"spotify:playlist:{id}"); 160 | case UriType.Local: 161 | throw new NotSupportedException("UriType Local is not supported."); 162 | default: 163 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 164 | } 165 | } 166 | 167 | /// 168 | /// Creates a playlist from the given uri. 169 | /// 170 | /// The user id. 171 | /// The playlist id. 172 | /// A new instance of . 173 | /// Note: this will always be of . 174 | [Obsolete("The userId property isn't needed anymore for creating a Playlist Uri. Users should now just use `Make(string id, UriType type)` or Make(string uri).")] 175 | public static SpotifyUri Make(string userId, string playlistId) 176 | { 177 | if (string.IsNullOrEmpty(userId)) throw new ArgumentException("userId is null or empty."); 178 | if (string.IsNullOrEmpty(playlistId)) throw new ArgumentException("playlistId is null or empty."); 179 | 180 | return new SpotifyUri($"spotify:playlist:{playlistId}"); 181 | } 182 | 183 | /// 184 | /// Creates a from the given uri. 185 | /// 186 | /// The spotify uri. 187 | /// A new instance of . 188 | public static SpotifyUri Make(string uri) 189 | { 190 | if (string.IsNullOrEmpty(uri)) throw new ArgumentException("uri is null or empty."); 191 | return new SpotifyUri(uri); 192 | } 193 | 194 | /// 195 | /// Creates a list of s for the given uris. 196 | /// 197 | /// A list of uris. 198 | /// A list of . 199 | public static IList MakeList(params string[] uri) 200 | { 201 | return uri.Select(SpotifyUri.Make).ToList(); 202 | } 203 | 204 | /// 205 | public override string ToString() => this.FullUri; 206 | 207 | /// 208 | /// This method is called after the object is completely deserialized. Use it instead of the constructror. 209 | /// 210 | /// The streaming context. 211 | [OnDeserialized] 212 | private void OnDeserialized(StreamingContext context) 213 | { 214 | var i = new SpotifyUri(this.FullUri); 215 | this.Domain = i.Domain; 216 | this.FullUri = i.FullUri; 217 | this.Id = i.Id; 218 | this.Type = i.Type; 219 | #pragma warning disable 220 | this.UserId = i.UserId; 221 | #pragma warning restore 222 | } 223 | 224 | #endregion Methods 225 | } 226 | } --------------------------------------------------------------------------------