diff --git a/SpotifyAPI/SpotifyAPI.csproj b/SpotifyAPI/SpotifyAPI.csproj index 4adb37d7..63fd5480 100644 --- a/SpotifyAPI/SpotifyAPI.csproj +++ b/SpotifyAPI/SpotifyAPI.csproj @@ -63,8 +63,13 @@ + + + + + diff --git a/SpotifyAPI/SpotifyWebAPI/AutorizationCodeAuth.cs b/SpotifyAPI/SpotifyWebAPI/AutorizationCodeAuth.cs index 7f07275e..f11ff32f 100644 --- a/SpotifyAPI/SpotifyWebAPI/AutorizationCodeAuth.cs +++ b/SpotifyAPI/SpotifyWebAPI/AutorizationCodeAuth.cs @@ -67,7 +67,7 @@ namespace SpotifyAPI.SpotifyWebAPI builder.Append("&response_type=code"); builder.Append("&redirect_uri=" + RedirectUri); builder.Append("&state=" + State); - builder.Append("&scope=" + Scope.GetScopeValue(" ")); + builder.Append("&scope=" + Scope.GetStringAttribute(" ")); builder.Append("&show_dialog=" + ShowDialog.ToString()); return builder.ToString(); } diff --git a/SpotifyAPI/SpotifyWebAPI/ClientCredentialsAuth.cs b/SpotifyAPI/SpotifyWebAPI/ClientCredentialsAuth.cs index 192ac137..a6975f79 100644 --- a/SpotifyAPI/SpotifyWebAPI/ClientCredentialsAuth.cs +++ b/SpotifyAPI/SpotifyWebAPI/ClientCredentialsAuth.cs @@ -26,7 +26,7 @@ namespace SpotifyAPI.SpotifyWebAPI NameValueCollection col = new NameValueCollection(); col.Add("grant_type","client_credentials"); - col.Add("scope", Scope.GetScopeValue(" ")); + col.Add("scope", Scope.GetStringAttribute(" ")); byte[] data = null; try diff --git a/SpotifyAPI/SpotifyWebAPI/FollowType.cs b/SpotifyAPI/SpotifyWebAPI/FollowType.cs new file mode 100644 index 00000000..75819674 --- /dev/null +++ b/SpotifyAPI/SpotifyWebAPI/FollowType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpotifyAPI.SpotifyWebAPI +{ + [Flags] + public enum FollowType + { + [StringAttribute("artist")] + ARTIST = 1, + [StringAttribute("user")] + USER = 2 + } +} diff --git a/SpotifyAPI/SpotifyWebAPI/ImplicitGrantAuth.cs b/SpotifyAPI/SpotifyWebAPI/ImplicitGrantAuth.cs index ab92529b..241f7400 100644 --- a/SpotifyAPI/SpotifyWebAPI/ImplicitGrantAuth.cs +++ b/SpotifyAPI/SpotifyWebAPI/ImplicitGrantAuth.cs @@ -36,7 +36,7 @@ namespace SpotifyAPI.SpotifyWebAPI builder.Append("&response_type=token"); builder.Append("&redirect_uri=" + RedirectUri); builder.Append("&state=" + State); - builder.Append("&scope=" + Scope.GetScopeValue(" ")); + builder.Append("&scope=" + Scope.GetStringAttribute(" ")); builder.Append("&show_dialog=" + ShowDialog.ToString()); return builder.ToString(); } diff --git a/SpotifyAPI/SpotifyWebAPI/Models/ArrayResponse.cs b/SpotifyAPI/SpotifyWebAPI/Models/ArrayResponse.cs new file mode 100644 index 00000000..6245eb7a --- /dev/null +++ b/SpotifyAPI/SpotifyWebAPI/Models/ArrayResponse.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +namespace SpotifyAPI.SpotifyWebAPI.Models +{ + public class ListResponse : BasicModel + { + public List List { get; set; } + } +} diff --git a/SpotifyAPI/SpotifyWebAPI/Models/Category.cs b/SpotifyAPI/SpotifyWebAPI/Models/Category.cs new file mode 100644 index 00000000..2291f806 --- /dev/null +++ b/SpotifyAPI/SpotifyWebAPI/Models/Category.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace SpotifyAPI.SpotifyWebAPI.Models +{ + public class Category : BasicModel + { + [JsonProperty("href")] + public String Href { get; set; } + [JsonProperty("icons")] + public List Icons { get; set; } + [JsonProperty("id")] + public String Id { get; set; } + [JsonProperty("name")] + public String Name { get; set; } + } +} diff --git a/SpotifyAPI/SpotifyWebAPI/Models/CategoryList.cs b/SpotifyAPI/SpotifyWebAPI/Models/CategoryList.cs new file mode 100644 index 00000000..8d6e88d6 --- /dev/null +++ b/SpotifyAPI/SpotifyWebAPI/Models/CategoryList.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace SpotifyAPI.SpotifyWebAPI.Models +{ + public class CategoryList : BasicModel + { + [JsonProperty("categories")] + public Paging Categories { get; set; } + } +} diff --git a/SpotifyAPI/SpotifyWebAPI/Models/CategoryPlaylist.cs b/SpotifyAPI/SpotifyWebAPI/Models/CategoryPlaylist.cs new file mode 100644 index 00000000..5e14682e --- /dev/null +++ b/SpotifyAPI/SpotifyWebAPI/Models/CategoryPlaylist.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace SpotifyAPI.SpotifyWebAPI.Models +{ + public class CategoryPlaylist : BasicModel + { + [JsonProperty("playlists")] + public Paging Playlists { get; set; } + } +} diff --git a/SpotifyAPI/SpotifyWebAPI/Models/FullTrack.cs b/SpotifyAPI/SpotifyWebAPI/Models/FullTrack.cs index b5b21584..a1b57b78 100644 --- a/SpotifyAPI/SpotifyWebAPI/Models/FullTrack.cs +++ b/SpotifyAPI/SpotifyWebAPI/Models/FullTrack.cs @@ -44,5 +44,17 @@ namespace SpotifyAPI.SpotifyWebAPI.Models public String Type { get; set; } [JsonProperty("uri")] public String Uri { get; set; } + + /// + /// Only filled when the "market"-parameter was supplied! + /// + [JsonProperty("is_playable")] + public Boolean? IsPlayable { get; set; } + + /// + /// Only filled when the "market"-parameter was supplied! + /// + [JsonProperty("linked_from")] + public LinkedFrom LinkedFrom { get; set; } } } diff --git a/SpotifyAPI/SpotifyWebAPI/Models/GeneralModels.cs b/SpotifyAPI/SpotifyWebAPI/Models/GeneralModels.cs index 7eeb9ff9..cc9db476 100644 --- a/SpotifyAPI/SpotifyWebAPI/Models/GeneralModels.cs +++ b/SpotifyAPI/SpotifyWebAPI/Models/GeneralModels.cs @@ -100,8 +100,17 @@ namespace SpotifyAPI.SpotifyWebAPI.Models public String Type { get; set; } } - public class CheckUserTracks : BasicModel + public class LinkedFrom { - public List Checked { get; set; } + [JsonProperty("external_urls")] + public Dictionary ExternalUrls { get; set; } + [JsonProperty("href")] + public String Href { get; set; } + [JsonProperty("id")] + public String Id { get; set; } + [JsonProperty("type")] + public String Type { get; set; } + [JsonProperty("uri")] + public String Uri { get; set; } } } diff --git a/SpotifyAPI/SpotifyWebAPI/Models/PrivateProfile.cs b/SpotifyAPI/SpotifyWebAPI/Models/PrivateProfile.cs index 7a88f9c9..72c347ed 100644 --- a/SpotifyAPI/SpotifyWebAPI/Models/PrivateProfile.cs +++ b/SpotifyAPI/SpotifyWebAPI/Models/PrivateProfile.cs @@ -9,6 +9,8 @@ namespace SpotifyAPI.SpotifyWebAPI.Models { public class PrivateProfile : BasicModel { + [JsonProperty("birthdate")] + public String Birthdate { get; set; } [JsonProperty("country")] public String Country { get; set; } [JsonProperty("display_name")] diff --git a/SpotifyAPI/SpotifyWebAPI/Scope.cs b/SpotifyAPI/SpotifyWebAPI/Scope.cs index 0322d014..3b3373b0 100644 --- a/SpotifyAPI/SpotifyWebAPI/Scope.cs +++ b/SpotifyAPI/SpotifyWebAPI/Scope.cs @@ -26,6 +26,12 @@ namespace SpotifyAPI.SpotifyWebAPI [StringAttribute("user-library-read")] USER_LIBRARAY_READ = 128, [StringAttribute("user-library-modify")] - USER_LIBRARY_MODIFY = 256 + USER_LIBRARY_MODIFY = 256, + [StringAttribute("user-follow-modify")] + USER_FOLLOW_MODIFY = 512, + [StringAttribute("user-follow-read")] + USER_FOLLOW_READ = 1024, + [StringAttribute("user-read-birthdate")] + USER_READ_BIRTHDATE = 2048 } } diff --git a/SpotifyAPI/SpotifyWebAPI/SpotifyWebAPIClass.cs b/SpotifyAPI/SpotifyWebAPI/SpotifyWebAPIClass.cs index cc34708e..118e33bc 100644 --- a/SpotifyAPI/SpotifyWebAPI/SpotifyWebAPIClass.cs +++ b/SpotifyAPI/SpotifyWebAPI/SpotifyWebAPIClass.cs @@ -46,6 +46,36 @@ namespace SpotifyAPI.SpotifyWebAPI return DownloadData("https://api.spotify.com/v1/users/" + userId); } + public ErrorResponse Follow(FollowType followType, List ids) + { + JObject ob = new JObject(); + ob.Add("ids", new JArray(ids.ToArray())); + return UploadData("https://api.spotify.com/v1/me/following?type=" + followType.GetStringAttribute(""), ob.ToString(Formatting.None), "PUT"); + } + + public ErrorResponse Unfollow(FollowType followType, List ids) + { + JObject ob = new JObject(); + ob.Add("ids", new JArray(ids.ToArray())); + return UploadData("https://api.spotify.com/v1/me/following?type=" + followType.GetStringAttribute(""), ob.ToString(Formatting.None), "DELETE"); + } + + /// + /// + /// + public ListResponse IsFollowing(FollowType followType, List ids) + { + JToken res = DownloadData("https://api.spotify.com/v1/me/following/contains?type=" + followType.GetStringAttribute("") + "&ids=" + string.Join(",", ids)); + if (res is JArray) + { + return new ListResponse { List = res.ToObject>(), ErrorResponse = null }; + } + else + { + return new ListResponse { List = null, ErrorResponse = res.ToObject() }; + } + } + #endregion #region User-Library @@ -54,26 +84,29 @@ namespace SpotifyAPI.SpotifyWebAPI JArray array = new JArray(ids.ToArray()); return UploadData("https://api.spotify.com/v1/me/tracks/", array.ToString(Formatting.None), "PUT"); } - public Paging GetSavedTracks() + public Paging GetSavedTracks(String market = "") { - return DownloadData>("https://api.spotify.com/v1/me/tracks"); + if(market == "") + return DownloadData>("https://api.spotify.com/v1/me/tracks"); + else + return DownloadData>("https://api.spotify.com/v1/me/tracks?market=" + market); } public ErrorResponse RemoveSavedTracks(List ids) { JArray array = new JArray(ids.ToArray()); return UploadData("https://api.spotify.com/v1/me/tracks/", array.ToString(Formatting.None), "DELETE"); } - public CheckUserTracks CheckSavedTracks(List ids) + public ListResponse CheckSavedTracks(List ids) { String resp = DownloadString("https://api.spotify.com/v1/me/tracks/contains?ids=" + string.Join(",", ids)); JToken res = JToken.Parse(resp); if (res is JArray) { - return new CheckUserTracks { Checked = res.ToObject>(), ErrorResponse = null }; + return new ListResponse { List = res.ToObject>(), ErrorResponse = null }; } else { - return new CheckUserTracks { Checked = null, ErrorResponse = res.ToObject() }; + return new ListResponse { List = null, ErrorResponse = res.ToObject() }; } } #endregion @@ -87,9 +120,12 @@ namespace SpotifyAPI.SpotifyWebAPI { return DownloadData("https://api.spotify.com/v1/users/" + userId + "/playlists/" + playlistId); } - public Paging GetPlaylistTracks(String userId, String playlistId) + public Paging GetPlaylistTracks(String userId, String playlistId, String market = "") { - return DownloadData>("https://api.spotify.com/v1/users/" + userId + "/playlists/" + playlistId + "/tracks"); + if(market == "") + return DownloadData>("https://api.spotify.com/v1/users/" + userId + "/playlists/" + playlistId + "/tracks"); + else + return DownloadData>("https://api.spotify.com/v1/users/" + userId + "/playlists/" + playlistId + "/tracks?market=" + market); } public FullPlaylist CreatePlaylist(String userId, String playlistName, Boolean isPublic = true) { @@ -147,6 +183,28 @@ namespace SpotifyAPI.SpotifyWebAPI builder.Append("×tamp=" + timestamp.ToString("yyyy-MM-ddTHH:mm:ss")); return DownloadData(builder.ToString()); } + + public ErrorResponse FollowPlaylist(String ownerId, String playlistId, bool showPublic) + { + JObject ob = new JObject(); + ob.Add("public", showPublic); + return UploadData("https://api.spotify.com/v1/users/" + ownerId + "/playlists/" + playlistId + "/followers", ob.ToString(Formatting.None), "PUT"); + } + + public ErrorResponse UnfollowPlaylist(String ownerId, String playlistId) + { + return UploadData("https://api.spotify.com/v1/users/" + ownerId + "/playlists/" + playlistId + "/followers", "", "DELETE"); + } + + public ListResponse IsFollowingPlaylist(String ownerId, String playlistId, List ids) + { + JToken res = DownloadData("https://api.spotify.com/v1/users/" + ownerId + "/playlists/" + playlistId + "/followers/contains?ids=" + string.Join(",", ids)); + if (res is JArray) + return new ListResponse() { List = res.ToObject>(), ErrorResponse = null }; + else + return new ListResponse() { List = null, ErrorResponse = res.ToObject() }; + } + #endregion #region Search and Fetch @@ -166,27 +224,36 @@ namespace SpotifyAPI.SpotifyWebAPI limit = Math.Min(50, limit); StringBuilder builder = new StringBuilder("https://api.spotify.com/v1/search"); builder.Append("?q=" + q); - builder.Append("&type=" + type.GetSearchValue(",")); + builder.Append("&type=" + type.GetStringAttribute(",")); builder.Append("&limit=" + limit); builder.Append("&offset=" + offset); return DownloadData(builder.ToString()); } - public SeveralTracks GetSeveralTracks(List ids) + public SeveralTracks GetSeveralTracks(List ids, String market = "") { - return DownloadData("https://api.spotify.com/v1/tracks?ids=" + string.Join(",", ids)); + if(market == "") + return DownloadData("https://api.spotify.com/v1/tracks?ids=" + string.Join(",", ids)); + else + return DownloadData("https://api.spotify.com/v1/tracks?market=" + market + "&ids=" + string.Join(",", ids)); } - public SeveralAlbums GetSeveralAlbums(List ids) + public SeveralAlbums GetSeveralAlbums(List ids, String market = "") { - return DownloadData("https://api.spotify.com/v1/albums?ids=" + string.Join(",", ids)); + if(market == "") + return DownloadData("https://api.spotify.com/v1/albums?ids=" + string.Join(",", ids)); + else + return DownloadData("https://api.spotify.com/v1/albums?market=" + market + "&ids=" + string.Join(",", ids)); } public SeveralArtists GetSeveralArtists(List ids) { return DownloadData("https://api.spotify.com/v1/artists?ids=" + string.Join(",", ids)); } - public FullTrack GetTrack(String id) + public FullTrack GetTrack(String id, String market = "") { - return DownloadData("https://api.spotify.com/v1/tracks/" + id); + if(market == "") + return DownloadData("https://api.spotify.com/v1/tracks/" + id); + else + return DownloadData("https://api.spotify.com/v1/tracks/" + id + "?market=" + market); } public SeveralArtists GetRelatedArtists(String id) { @@ -200,7 +267,7 @@ namespace SpotifyAPI.SpotifyWebAPI { limit = Math.Min(50, limit); StringBuilder builder = new StringBuilder("https://api.spotify.com/v1/artists/" + id + "/albums"); - builder.Append("?type=" + type.GetAlbumValue(",")); + builder.Append("?type=" + type.GetStringAttribute(",")); builder.Append("&limit=" + limit); builder.Append("&offset=" + offset); if (market != "") @@ -211,20 +278,68 @@ namespace SpotifyAPI.SpotifyWebAPI { return DownloadData("https://api.spotify.com/v1/artists/" + id); } - public Paging GetAlbumTracks(String id, int limit = 20, int offset = 0) + public Paging GetAlbumTracks(String id, String market, int limit = 20, int offset = 0) { limit = Math.Min(50, limit); StringBuilder builder = new StringBuilder("https://api.spotify.com/v1/albums/" + id + "/tracks"); builder.Append("?limit=" + limit); builder.Append("&offset=" + offset); + if (market != "") + builder.Append("&market=" + market); return DownloadData>(builder.ToString()); } - public FullAlbum GetAlbum(String id) + public FullAlbum GetAlbum(String id, String market = "") { - return DownloadData("https://api.spotify.com/v1/albums/" + id); + if(market == "") + return DownloadData("https://api.spotify.com/v1/albums/" + id); + else + return DownloadData("https://api.spotify.com/v1/albums/" + id + "?market=" + market); } #endregion + #region Category + + public CategoryList GetCategories(String country = "", String locale = "", int limit = 20, int offset = 0) + { + limit = Math.Min(50, limit); + StringBuilder builder = new StringBuilder("https://api.spotify.com/v1/browse/categories"); + builder.Append("?limit=" + limit); + builder.Append("&offset=" + offset); + if (country != "") + builder.Append("&country=" + country); + if (locale != "") + builder.Append("&locale=" + locale); + return DownloadData(builder.ToString()); + } + + public Category GetCategory(String categoryId, String country = "", String locale = "") + { + StringBuilder builder = new StringBuilder("https://api.spotify.com/v1/browse/categories/" + categoryId); + if(country != "") + builder.Append("?country=" + country); + if(locale != "") + { + if (country != "") + builder.Append("&locale=" + locale); + else + builder.Append("?locale=" + locale); + } + return DownloadData(builder.ToString()); + } + + public CategoryPlaylist GetCategoryPlaylists(String categoryId, String country = "", int limit = 20, int offset = 0) + { + limit = Math.Min(50, limit); + StringBuilder builder = new StringBuilder("https://api.spotify.com/v1/browse/categories/" + categoryId + "/playlists"); + builder.Append("?limit=" + limit); + builder.Append("&offset=" + offset); + if (country != "") + builder.Append("&country=" + country); + return DownloadData(builder.ToString()); + } + + #endregion + #region Util public T UploadData(String url, String uploadData, String method = "POST") { @@ -263,7 +378,6 @@ namespace SpotifyAPI.SpotifyWebAPI { response = new StreamReader(e.Response.GetResponseStream()).ReadToEnd(); } - Debug.WriteLine(response); return response; } #endregion diff --git a/SpotifyAPI/SpotifyWebAPI/Util.cs b/SpotifyAPI/SpotifyWebAPI/Util.cs index 8d0e2978..27ef2ae3 100644 --- a/SpotifyAPI/SpotifyWebAPI/Util.cs +++ b/SpotifyAPI/SpotifyWebAPI/Util.cs @@ -10,41 +10,14 @@ namespace SpotifyAPI.SpotifyWebAPI { public static class Util { - public static string GetScopeValue(this Scope en,String separator) + public static string GetStringAttribute(this T en, String separator) where T : struct, IConvertible { + Enum e = (Enum)(object)en; IEnumerable attributes = - Enum.GetValues(typeof(Scope)) - .Cast() - .Where(v => en.HasFlag(v)) - .Select(v => typeof(Scope).GetField(v.ToString())) - .Select(f => f.GetCustomAttributes(typeof(StringAttribute), false)[0]) - .Cast(); - - List list = new List(); - attributes.ToList().ForEach((element) => list.Add(element.Text)); - return string.Join(" ", list); - } - public static string GetSearchValue(this SearchType en, String separator) - { - IEnumerable attributes = - Enum.GetValues(typeof(SearchType)) - .Cast() - .Where(v => en.HasFlag(v)) - .Select(v => typeof(SearchType).GetField(v.ToString())) - .Select(f => f.GetCustomAttributes(typeof(StringAttribute), false)[0]) - .Cast(); - - List list = new List(); - attributes.ToList().ForEach((element) => list.Add(element.Text)); - return string.Join(" ", list); - } - public static string GetAlbumValue(this AlbumType en, String separator) - { - IEnumerable attributes = - Enum.GetValues(typeof(AlbumType)) - .Cast() - .Where(v => en.HasFlag(v)) - .Select(v => typeof(AlbumType).GetField(v.ToString())) + Enum.GetValues(typeof(T)) + .Cast() + .Where(v => e.HasFlag((Enum)(object)v)) + .Select(v => typeof(T).GetField(v.ToString())) .Select(f => f.GetCustomAttributes(typeof(StringAttribute), false)[0]) .Cast(); diff --git a/SpotifyWebAPIExample/Program.cs b/SpotifyWebAPIExample/Program.cs index 7e0bce1e..c8d6b7e8 100644 --- a/SpotifyWebAPIExample/Program.cs +++ b/SpotifyWebAPIExample/Program.cs @@ -25,6 +25,7 @@ namespace SpotifyWebAPIExample RedirectUri = "http://localhost", //How many permissions we need? Scope = Scope.USER_READ_PRIVATE | Scope.USER_READ_EMAIL | Scope.PLAYLIST_READ_PRIVATE | Scope.USER_LIBRARAY_READ | Scope.USER_LIBRARY_MODIFY | Scope.USER_READ_PRIVATE + | Scope.USER_FOLLOW_MODIFY | Scope.USER_FOLLOW_READ | Scope.PLAYLIST_MODIFY_PRIVATE | Scope.USER_READ_BIRTHDATE }; //Start the internal http server auth.StartHttpServer();