/// <summary> /// HttpWebRequest POST request to send to YouTube to check if the user's is authenticated (signed in) by determining /// if a generic request is successful given the current authentication cookie value we have stored. /// /// In this case, we're actually perform a request for personally uploaded music files /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <returns>True if successfully authenticated, false otherwise</returns> public static bool IsAuthenticated(string cookieValue) { try { var request = (HttpWebRequest)WebRequest.Create(Global.YTMusicBaseUrl + "browse" + Global.YTMusicParams); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); byte[] postBytes = GetPostBytes( SafeFileStream.ReadAllText( Path.Combine(Global.WorkingDirectory, @"AppData\check_auth_context.json"))); request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } object json = JsonConvert.DeserializeObject(result); } } catch (Exception ex) { Console.Out.WriteLine(ex.Message); return(false); } return(true); }
/// <summary> /// HttpWebRequest POST request to send to YTM which fetches a list (collection) of Playlists (without playlist tracks). /// Use the 'Requests.Playlists.GetPlaylist (singular)' method to get an individual playlist complete with track listing. /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <returns>OnlinePlaylistCollection object (list of playlists without tracks)</returns> public static OnlinePlaylistCollection GetPlaylists( string cookieValue) { var playListCol = new OnlinePlaylistCollection(); try { var request = (HttpWebRequest)WebRequest.Create(Global.YTMusicBaseUrl + "browse?" + Global.YTMusicParams); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); byte[] postBytes = GetPostBytes( SafeFileStream.ReadAllText( Path.Combine(Global.WorkingDirectory, @"AppData\get_playlists_context.json"))); request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); var playListsResultContext = JsonConvert.DeserializeObject <BrowsePlaylistsResultsContext>(result); var playListResults = playListsResultContext.contents .singleColumnBrowseResultsRenderer .tabs[0] .tabRenderer .content .sectionListRenderer .contents[1] .itemSectionRenderer .contents[0] .gridRenderer .items; foreach (var item in playListResults) { if (item.musicTwoRowItemRenderer.title.runs[0].text != "New playlist" && item.musicTwoRowItemRenderer.title.runs[0].text != "Your likes") { try { playListCol.Add(new OnlinePlaylist { Title = item.musicTwoRowItemRenderer.title.runs[0].text, Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text + item.musicTwoRowItemRenderer.subtitle.runs[1].text + item.musicTwoRowItemRenderer.subtitle.runs[2].text, BrowseId = item.musicTwoRowItemRenderer.navigationEndpoint.browseEndpoint.browseId, CoverArtUrl = item.musicTwoRowItemRenderer.thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails[0].url }); } catch (Exception e) { Logger.Log(e, "GetPlaylists - Error fetching a playlist", Log.LogTypeEnum.Error); } } } } } } catch (Exception e) { var _ = e; #if DEBUG Console.Out.WriteLine("GetPlaylists: " + e.Message); #endif } return(playListCol); }
/// <summary> /// HttpWebRequest POST request - Recursively fetches all the songs of an artist from YouTube Music's 'Upload' section /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <param name="browseId">YouTube Music's navigation ID for an individual artist, retreived from 'GetArtists' request</param> /// <param name="songs">Input ArtistCache 'Songs' object (should be empty when initialising - this is a recursive method)</param> /// <param name="continuationToken">Token from YouTube Music indicated more results to fetch, and the token to get them /// (shoult be empty when initialising - this is a recursive method)</param> /// <returns>ArtistCache object</returns> public static AlbumSongCollection GetArtistSongs( string cookieValue, string browseId, AlbumSongCollection albumSongCollection = null, string continuationToken = null) { if (albumSongCollection == null) { albumSongCollection = new AlbumSongCollection(); } if (albumSongCollection.Songs == null) { albumSongCollection.Songs = new SongCollection(); } if (albumSongCollection.Albums == null) { albumSongCollection.Albums = new AlbumCollection(); } try { var request = (HttpWebRequest)WebRequest.Create(Global.YTMusicBaseUrl + "browse" + (string.IsNullOrEmpty(continuationToken) ? "" : "?ctoken=" + continuationToken + "&continuation=" + continuationToken) + (string.IsNullOrEmpty(continuationToken) ? Global.YTMusicParams : Global.YTMusicParams.Replace('?', '&'))); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); var context = JsonConvert.DeserializeObject <BrowseArtistRequestContext>( SafeFileStream.ReadAllText( Path.Combine( Global.WorkingDirectory, @"AppData\get_artist_context.json"))); context.browseId = string.Format("{0}", browseId); byte[] postBytes = GetPostBytes(JsonConvert.SerializeObject(context)); request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } if (string.IsNullOrEmpty(continuationToken)) { albumSongCollection = GetInitalArtistSongs(albumSongCollection, result, out string continuation); if (!string.IsNullOrEmpty(continuation)) { return(GetArtistSongs(cookieValue, browseId, albumSongCollection, continuation)); } } else { albumSongCollection = GetContinuationArtistSongs(albumSongCollection, result, out string continuation); if (!string.IsNullOrEmpty(continuation)) { return(GetArtistSongs(cookieValue, browseId, albumSongCollection, continuation)); } } } } catch (Exception e) { var _ = e; #if DEBUG Console.Out.WriteLine("GetArtistSongs: " + e.Message); #endif } return(albumSongCollection); }
/// <summary> /// HttpWebRequest POST request to send to add a playlist item (track) to an existing YouTube Music playlist /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <param name="playlistId">YT Music playlistid (or browseId) to add to</param> /// <param name="videoId">This is the unique track id to add</param> /// <returns>True if successfully, false otherwise</returns> public static bool AddPlaylistItem(string cookieValue, string playlistId, string videoId, out Exception ex) { ex = null; string originalRequest = string.Empty; try { var request = (HttpWebRequest)WebRequest.Create( Global.YTMusicBaseUrl + "browse/edit_playlist" + Global.YTMusicParams); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); var context = JsonConvert.DeserializeObject <AddPlaylistItemContext>( SafeFileStream.ReadAllText( Path.Combine( Global.WorkingDirectory, @"AppData\add_playlist_item_context.json"))); if (playlistId.StartsWith("VL")) { playlistId = playlistId.Substring(2, playlistId.Length - 2); } context.playlistId = playlistId; context.actions[0].addedVideoId = videoId; var delta = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now); double utcMinuteOffset = delta.TotalMinutes; context.context.client.utcOffsetMinutes = (int)utcMinuteOffset; byte[] postBytes = GetPostBytes(JsonConvert.SerializeObject(context)); try { originalRequest = JsonConvert.SerializeObject(context); } catch { } request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } if (result.ToLower().Contains("error")) { throw new Exception("Error: " + result); } } } catch (Exception e) { Logger.LogError("AddPlaylistItem", "Error adding to playlist: " + playlistId, false, originalRequest); ex = e; return(false); } return(true); }
/// <summary> /// HttpWebRequest POST request to send to YTM to create a new playlist /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <param name="title">Title to call the playlist</param> /// <param name="description">Description of the playlist (does not allow HTML tags)</param> /// <param name="videoIds">List of video ids (track ids) to create the playlist with</param> /// <param name="privacyStatus">PRIVATE, PUBLIC or UNLISTED - Default it PUBLIC</param> /// <param name="playlistId">(Output) playlist id once created</param> /// <param name="browseId">(Output) browse id once create (It's just the playlist ID prefixed with 'VL')</param> /// <param name="errorMessage">(Output)Any error message encountered during the request</param> /// <returns>True if request is successful, false otherwise</returns> public static bool CreatePlaylist( string cookieValue, string title, string description, List <string> videoIds, PrivacyStatusEmum privacyStatus, out string playlistId, out string browseId, out Exception ex) { ex = null; string originalRequest = string.Empty; browseId = string.Empty; playlistId = string.Empty; if (description == null) { description = string.Empty; } try { var request = (HttpWebRequest)WebRequest.Create( Global.YTMusicBaseUrl + "playlist/create" + Global.YTMusicParams); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); var context = JsonConvert.DeserializeObject <CreatePlaylistRequestContext>( SafeFileStream.ReadAllText( Path.Combine( Global.WorkingDirectory, @"AppData\create_playlist_context.json"))); context.title = title; context.description = description; context.privacyStatus = privacyStatus.ToString().ToUpper(); context.videoIds = videoIds.ToArray(); var delta = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now); double utcMinuteOffset = delta.TotalMinutes; context.context.client.utcOffsetMinutes = (int)utcMinuteOffset; try { originalRequest = JsonConvert.SerializeObject(context); } catch { } byte[] postBytes = GetPostBytes(JsonConvert.SerializeObject(context)); request.ContentLength = postBytes.Length; request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } if (result.ToLower().Contains("error")) { throw new Exception("Error: " + result + ": Original Http Request: " + originalRequest); } else { var runObject = JObject.Parse(result); var browseIds = runObject.Descendants() .Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "browseId") .Select(p => ((JProperty)p).Value).ToList(); if (browseIds.Count > 0) { browseId = browseIds[0].ToString(); } var playlistIds = runObject.Descendants() .Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "playlistId") .Select(p => ((JProperty)p).Value).ToList(); if (playlistIds.Count > 0) { playlistId = playlistIds[0].ToString(); } } } } catch (Exception e) { Logger.LogError("CreatePlaylist", "Error creating playlist: " + title, originalRequest); ex = e; return(false); } return(true); }
/// <summary> /// HttpWebRequest POST request to send to YouTube delete a YT music track fro the users uploads /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <param name="entityId">YT Music entity ID to delete</param> /// <returns>True if successfully authenticated, false otherwise</returns> public static bool DeleteAlbumOrTrackFromYTMusic(string cookieValue, string entityId, out string errorMessage) { errorMessage = string.Empty; try { var request = (HttpWebRequest)WebRequest.Create( Global.YouTubeMusicBaseUrl + "music/delete_privately_owned_entity" + Global.YouTubeMusicParams); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); var context = JsonConvert.DeserializeObject <DeleteFromYTMusicRequestContext>( SafeFileStream.ReadAllText( Path.Combine( Global.WorkingDirectory, @"AppData\delete_song_context.json"))); context.entityId = entityId; byte[] postBytes = GetPostBytes(JsonConvert.SerializeObject(context)); request.ContentLength = postBytes.Length; request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } if (result.ToLower().Contains("error")) { errorMessage = "Error: " + result; return(false); } } } catch (Exception e) { errorMessage = "Error: " + e.Message; return(false); } return(true); }
/// <summary> /// HttpWebRequest POST request to send to YTM to remove a playlist item (track) from an existing playlist /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <param name="playlistId">Playlist id to remove a track from</param> /// <param name="videoEntityId">VideoId (unique track entityId) of the track to remove</param> /// <param name="setVideoEntityId">SetVideoId (alt unbique track entityId) of the track to remove</param> /// <param name="errorMessage">(Output) Error message if error encountered while sending the request</param> /// <returns>True if the request was successful, false otherwise</returns> public static bool RemovePlaylistItems( string cookieValue, string playlistId, string videoEntityId, string setVideoEntityId, out string errorMessage) { errorMessage = string.Empty; if (playlistId.StartsWith("VL")) { playlistId = playlistId.Substring(2, playlistId.Length - 2); } try { var request = (HttpWebRequest)WebRequest.Create( Global.YTMusicBaseUrl + "browse/edit_playlist" + Global.YTMusicParams); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); var context = JsonConvert.DeserializeObject <DeletePlaylistItemRequestContext>( SafeFileStream.ReadAllText( Path.Combine( Global.WorkingDirectory, @"AppData\delete_playlist_item_context.json"))); context.playlistId = playlistId; context.actions[0].setVideoId = setVideoEntityId; context.actions[0].removedVideoId = videoEntityId; byte[] postBytes = GetPostBytes(JsonConvert.SerializeObject(context)); request.ContentLength = postBytes.Length; request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } if (result.ToLower().Contains("error")) { errorMessage = "Error: " + result; return(false); } } } catch (Exception e) { errorMessage = "Error: " + e.Message; return(false); } return(true); }
/// <summary> /// HttpWebRequest POST request to send to YTM, which gets a playlist given a playlist or browse id /// (You can get this from the 'Requests.Playlists.GetPlaylists (plural)') request method. And then recurisvely /// fetches all tracks listed in the playlist /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <param name="browseId">Playlist id or browse id to retreive the playlist for</param> /// <param name="playlist">Shuuld be null initially. This object is used for recursive purposes</param> /// <param name="continuationToken">Should be null initially. This object is used for recursive purposes.</param> /// <returns>OnlinePlaylist object</returns> public static OnlinePlaylist GetPlaylist( string cookieValue, string browseId, OnlinePlaylist playlist = null, string continuationToken = null) { if (playlist == null) { playlist = new OnlinePlaylist(); } if (playlist.Songs == null) { playlist.Songs = new PlaylistSongCollection(); } try { var request = (HttpWebRequest)WebRequest.Create(Global.YTMusicBaseUrl + "browse" + (string.IsNullOrEmpty(continuationToken) ? "" : "?ctoken=" + continuationToken + "&continuation=" + continuationToken) + (string.IsNullOrEmpty(continuationToken) ? Global.YTMusicParams : Global.YTMusicParams.Replace('?', '&'))); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); var context = JsonConvert.DeserializeObject <BrowseArtistRequestContext>( SafeFileStream.ReadAllText( Path.Combine( Global.WorkingDirectory, @"AppData\get_playlist_context.json"))); context.browseId = string.Format("{0}", browseId); byte[] postBytes = GetPostBytes(JsonConvert.SerializeObject(context)); request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); } if (string.IsNullOrEmpty(continuationToken)) { var playListData = JsonConvert.DeserializeObject <BrowsePlaylistResultsContext>(result); playlist.BrowseId = browseId; playlist.Title = playListData.header .musicEditablePlaylistDetailHeaderRenderer .header .musicDetailHeaderRenderer .title .runs[0] .text; playlist.Subtitle = playListData.header .musicEditablePlaylistDetailHeaderRenderer .header .musicDetailHeaderRenderer .subtitle.runs[0].text + playListData.header .musicEditablePlaylistDetailHeaderRenderer .header .musicDetailHeaderRenderer .subtitle.runs[1].text + playListData.header .musicEditablePlaylistDetailHeaderRenderer .header .musicDetailHeaderRenderer .subtitle.runs[2].text; playlist.Description = playListData.header .musicEditablePlaylistDetailHeaderRenderer .editHeader .musicPlaylistEditHeaderRenderer .description != null ? playListData.header .musicEditablePlaylistDetailHeaderRenderer .editHeader .musicPlaylistEditHeaderRenderer .description .runs[0] .text : ""; playlist.Duration = playListData.header .musicEditablePlaylistDetailHeaderRenderer .header .musicDetailHeaderRenderer .secondSubtitle .runs[2] .text; playlist.CoverArtUrl = playListData.header .musicEditablePlaylistDetailHeaderRenderer .header .musicDetailHeaderRenderer .thumbnail .croppedSquareThumbnailRenderer .thumbnail .thumbnails[0].url; try { playlist.PrivacyStatus = (OnlinePlaylist.PrivacyStatusEmum)Enum.Parse( typeof(OnlinePlaylist.PrivacyStatusEmum), playListData.header .musicEditablePlaylistDetailHeaderRenderer .editHeader .musicPlaylistEditHeaderRenderer .privacy, true); } catch { playlist.PrivacyStatus = OnlinePlaylist.PrivacyStatusEmum.Private; } playlist.Songs = GetInitalPlaylistSongs(playlist.Songs, result, out string continuation); if (!string.IsNullOrEmpty(continuation)) { return(GetPlaylist(cookieValue, browseId, playlist, continuation)); } } else { playlist.Songs = GetContinuationPlaylistSongs(playlist.Songs, result, out string continuation); if (!string.IsNullOrEmpty(continuation)) { return(GetPlaylist(cookieValue, browseId, playlist, continuation)); } } } } catch (Exception e) { var _ = e; #if DEBUG Console.Out.WriteLine("GetArtistSongs: " + e.Message); #endif } return(playlist); }
/// <summary> /// HttpWebRequest POST request to send to YTM which fetches a list (collection) of Playlists (without playlist tracks). /// Use the 'Requests.Playlists.GetPlaylist (singular)' method to get an individual playlist complete with track listing. /// </summary> /// <param name="cookieValue">Cookie from a previous YouTube Music sign in via this application (stored in the database)</param> /// <returns>OnlinePlaylistCollection object (list of playlists without tracks)</returns> public static OnlinePlaylistCollection GetPlaylists( string cookieValue, string continuationToken = null, OnlinePlaylistCollection playListCol = null) { if (playListCol == null) { playListCol = new OnlinePlaylistCollection(); } try { var request = (HttpWebRequest)WebRequest.Create(Global.YTMusicBaseUrl + "browse" + (string.IsNullOrEmpty(continuationToken) ? "" : "?ctoken=" + continuationToken + "&continuation=" + continuationToken) + (string.IsNullOrEmpty(continuationToken) ? Global.YTMusicParams : Global.YTMusicParams.Replace('?', '&'))); request = AddStandardHeaders(request, cookieValue); request.ContentType = "application/json; charset=UTF-8"; request.Headers["X-Goog-AuthUser"] = "******"; request.Headers["x-origin"] = "https://music.youtube.com"; request.Headers["X-Goog-Visitor-Id"] = Global.GoogleVisitorId; request.Headers["Authorization"] = GetAuthorisation(GetSAPISIDFromCookie(cookieValue)); byte[] postBytes = GetPostBytes( SafeFileStream.ReadAllText( Path.Combine(Global.WorkingDirectory, @"AppData\get_playlists_context.json"))); request.ContentLength = postBytes.Length; using (var requestStream = request.GetRequestStream()) { requestStream.Write(postBytes, 0, postBytes.Length); requestStream.Close(); } postBytes = null; using (var response = (HttpWebResponse)request.GetResponse()) { string result; using (var brotli = new Brotli.BrotliStream(response.GetResponseStream(), System.IO.Compression.CompressionMode.Decompress, true)) { var streamReader = new StreamReader(brotli); result = streamReader.ReadToEnd(); if (string.IsNullOrEmpty(continuationToken)) { var playListsResultContext = JsonConvert.DeserializeObject <BrowsePlaylistsResultsContext>(result); var playListResults = playListsResultContext.contents .singleColumnBrowseResultsRenderer .tabs[0] .tabRenderer .content .sectionListRenderer .contents[1] .itemSectionRenderer .contents[0] .gridRenderer .items; foreach (var item in playListResults) { if (item.musicTwoRowItemRenderer.title.runs[0].text != "New playlist" && item.musicTwoRowItemRenderer.title.runs[0].text != "Your likes") { try { var pl = new OnlinePlaylist { Title = item.musicTwoRowItemRenderer.title.runs[0].text }; try { pl.Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text + item.musicTwoRowItemRenderer.subtitle.runs[1].text + item.musicTwoRowItemRenderer.subtitle.runs[2].text; } catch { try { pl.Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text + item.musicTwoRowItemRenderer.subtitle.runs[1].text; } catch { try { pl.Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text; } catch { } } } pl.BrowseId = item.musicTwoRowItemRenderer.navigationEndpoint.browseEndpoint.browseId; try { pl.CoverArtUrl = item.musicTwoRowItemRenderer.thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails[0].url; } catch { } playListCol.Add(pl); } catch { } } } string continuation = string.Empty; var jo = JObject.Parse(result); var musicShelfRendererTokens = jo.Descendants().Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "itemSectionRenderer") .Select(p => ((JProperty)p).Value) .ToList(); foreach (var token in musicShelfRendererTokens) { Console.Out.WriteLine(token); var msr = token.ToObject <BrowsePlaylistsResultsContext.Itemsectionrenderer>(); if (msr != null && msr.contents[0].gridRenderer.continuations != null && msr.contents[0].gridRenderer.continuations.Length > 0 && msr.contents[0].gridRenderer.continuations[0].nextContinuationData != null && msr.contents[0].gridRenderer.continuations[0].nextContinuationData.continuation != null) { continuation = msr.contents[0].gridRenderer.continuations[0].nextContinuationData.continuation; } } if (!string.IsNullOrEmpty(continuation)) { return(GetPlaylists(cookieValue, continuation, playListCol)); } } else { var playListsResultContext = JsonConvert.DeserializeObject <BrowsePlaylistsResultsContinuationContext>(result); var playListResults = playListsResultContext.continuationContents .gridContinuation .items; foreach (var item in playListResults) { if (item.musicTwoRowItemRenderer.title.runs[0].text != "New playlist" && item.musicTwoRowItemRenderer.title.runs[0].text != "Your likes") { try { var pl = new OnlinePlaylist { Title = item.musicTwoRowItemRenderer.title.runs[0].text }; try { pl.Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text + item.musicTwoRowItemRenderer.subtitle.runs[1].text + item.musicTwoRowItemRenderer.subtitle.runs[2].text; } catch { try { pl.Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text + item.musicTwoRowItemRenderer.subtitle.runs[1].text; } catch { try { pl.Subtitle = item.musicTwoRowItemRenderer.subtitle.runs[0].text; } catch { } } } pl.BrowseId = item.musicTwoRowItemRenderer.navigationEndpoint.browseEndpoint.browseId; try { pl.CoverArtUrl = item.musicTwoRowItemRenderer.thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails[0].url; } catch { } playListCol.Add(pl); } catch (Exception e) { Logger.Log(e, "GetPlaylists - Error fetching a playlist", Log.LogTypeEnum.Error); } } } string continuation = string.Empty; var jo = JObject.Parse(result); var musicShelfRendererTokens = jo.Descendants().Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "continuationContents") .Select(p => ((JProperty)p).Value) .ToList(); foreach (var token in musicShelfRendererTokens) { Console.Out.WriteLine(token); var msr = token.ToObject <BrowsePlaylistsResultsContinuationContext.Continuationcontents>(); if (msr != null && msr.gridContinuation != null && msr.gridContinuation.continuations != null && msr.gridContinuation.continuations.Length > 0 && msr.gridContinuation.continuations[0].nextContinuationData != null && msr.gridContinuation.continuations[0].nextContinuationData.continuation != null) { continuation = msr.gridContinuation.continuations[0].nextContinuationData.continuation; } } if (!string.IsNullOrEmpty(continuation)) { return(GetPlaylists(cookieValue, continuation, playListCol)); } } } } } catch (Exception e) { var _ = e; #if DEBUG Console.Out.WriteLine("GetPlaylists: " + e.Message); #endif } return(playListCol); }