/// <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);
        }
        private void BindAlbumNodesFromSelect(
            TreeNode artistNode,
            AlbumSongCollection albumSongCollection,
            bool expand             = true,
            bool showFetchedMessage = true,
            bool isDeleting         = false)
        {
            if (tvUploads.InvokeRequired)
            {
                var d = new BindAlbumNodesFromSelectDelegate(BindAlbumNodesFromSelect);
                Invoke(d, new object[] { artistNode, albumSongCollection, expand, showFetchedMessage, isDeleting });
            }
            else
            {
                SetTreeViewEnabled(false);
                if (artistNode != null)
                {
                    var albumNodes = new List <TreeNode>();
                    foreach (var album in albumSongCollection.Albums)
                    {
                        var    songNodes   = new List <TreeNode>();
                        string releaseMbId = string.Empty;

                        foreach (var song in album.Songs)
                        {
                            var    musicFile             = MainForm.MusicFileRepo.LoadFromEntityId(song.EntityId).Result;
                            string databaseExistenceText = "Not found or not mapped";

                            if (musicFile != null && musicFile.Id != 0 && musicFile.Id != -1)
                            {
                                databaseExistenceText = $"Exists ({musicFile.Id})";
                                releaseMbId           = string.IsNullOrEmpty(musicFile.ReleaseMbId) ? releaseMbId : musicFile.ReleaseMbId;
                            }

                            songNodes.Add(new TreeNode
                            {
                                Name = song.EntityId,
                                Text = song.Title,
                                Tag  = Tag = new MusicManageTreeNodeModel
                                {
                                    NodeType               = MusicManageTreeNodeModel.NodeTypeEnum.Song,
                                    ArtistTitle            = ((MusicManageTreeNodeModel)artistNode.Tag).ArtistTitle,
                                    AlbumTitle             = album.Title,
                                    SongTitleOrDescription = song.Title,
                                    Duration               = song.Duration,
                                    CovertArtUrl           = song.CoverArtUrl,
                                    DatabaseExistence      = databaseExistenceText,
                                    MbId             = musicFile == null || string.IsNullOrEmpty(musicFile.MbId) ? "-" : musicFile.MbId,
                                    EntityOrBrowseId = song.EntityId,
                                    Uploaded         = musicFile == null ? "-" : musicFile.LastUpload.ToString("dd/MM/yyyy HH:mm")
                                }
                            });
                        }

                        var albumNode = new TreeNode
                        {
                            Name = Guid.NewGuid().ToString(),
                            Text = album.Title,
                            Tag  = Tag = new MusicManageTreeNodeModel
                            {
                                NodeType          = MusicManageTreeNodeModel.NodeTypeEnum.Album,
                                ArtistTitle       = ((MusicManageTreeNodeModel)artistNode.Tag).ArtistTitle,
                                AlbumTitle        = album.Title,
                                CovertArtUrl      = album.CoverArtUrl,
                                DatabaseExistence = string.IsNullOrEmpty(releaseMbId) ? "Not found or not mapped" : "Tracks exists for this album",
                                MbId             = string.IsNullOrEmpty(releaseMbId) ? "-" : releaseMbId,
                                EntityOrBrowseId = album.EntityId,
                                Uploaded         = "-"
                            }
                        };

                        albumNode.Nodes.AddRange(songNodes.ToArray());
                        albumNode.Text = albumNode.Text + " (" + songNodes.Count + ")";
                        albumNodes.Add(albumNode);
                    }
                    ;

                    AddChildNodes(artistNode, albumNodes);

                    int albumCount = albumSongCollection.Albums.Count;
                    int songCount  = albumSongCollection.Songs.Count;

                    string albumText = "albums";
                    string songText  = "tracks";

                    if (albumCount == 1)
                    {
                        albumText = "album";
                    }

                    if (songCount == 1)
                    {
                        songText = "track";
                    }

                    if (showFetchedMessage)
                    {
                        AppendUpdatesText($"Fetched {albumCount} {albumText}, {songCount} {songText}.",
                                          ColourHelper.HexStringToColor("#0d5601"));
                    }

                    artistNode.Text = artistNode.Text + " (" + artistNode.Nodes.Count + ")";
                    if (expand)
                    {
                        artistNode.Expand();
                    }

                    if (artistNode.Checked)
                    {
                        CheckAllChildNodes(artistNode, true);
                    }

                    if (!isDeleting)
                    {
                        SetMusicDetails((MusicManageTreeNodeModel)artistNode.Tag);
                    }
                }

                if (!isDeleting)
                {
                    ShowPreloader(false);
                    SetTreeViewEnabled(true);
                    DisableAllActionButtons(false);
                }
            }
        }
        private static AlbumSongCollection GetContinuationArtistSongs(
            AlbumSongCollection albumSongCollection,
            string httpResponseResults,
            out string continuation)
        {
            continuation = string.Empty;
            var jo = JObject.Parse(httpResponseResults);
            var musicShelfRendererTokens = jo.Descendants().Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "musicShelfContinuation")
                                           .Select(p => ((JProperty)p).Value)
                                           .ToList();

            var result = JsonConvert.DeserializeObject <BrowseArtistResultsContinuationContext>(httpResponseResults);

            foreach (var token in musicShelfRendererTokens)
            {
                var msr = token.ToObject <BrowseArtistResultsContext.Musicshelfrenderer>();
                if (msr.continuations != null &&
                    msr.continuations.Length > 0 &&
                    msr.continuations[0].nextContinuationData != null &&
                    msr.continuations[0].nextContinuationData.continuation != null)
                {
                    continuation = msr.continuations[0].nextContinuationData.continuation;
                }

                foreach (var content in msr.contents)
                {
                    try
                    {
                        string coverArtUrl = content.musicResponsiveListItemRenderer
                                             .thumbnail
                                             .musicThumbnailRenderer
                                             .thumbnail
                                             .thumbnails[0].url;

                        var song = new Song
                        {
                            Title = content.musicResponsiveListItemRenderer
                                    .flexColumns[0]
                                    .musicResponsiveListItemFlexColumnRenderer
                                    .text
                                    .runs[0]
                                    .text,

                            CoverArtUrl = coverArtUrl,

                            Duration = content.musicResponsiveListItemRenderer
                                       .fixedColumns[0]
                                       .musicResponsiveListItemFixedColumnRenderer
                                       .text
                                       .runs[0]
                                       .text,

                            VideoId = content.musicResponsiveListItemRenderer
                                      .flexColumns[0]
                                      .musicResponsiveListItemFlexColumnRenderer
                                      .text
                                      .runs[0]
                                      .navigationEndpoint
                                      .watchEndpoint
                                      .videoId,

                            EntityId = GetTrackEntityID(content.musicResponsiveListItemRenderer
                                                        .menu
                                                        .menuRenderer)
                        };

                        bool   isSingle   = true;
                        string albumTitle = "[Singles]";

                        if (content.musicResponsiveListItemRenderer
                            .flexColumns[2]
                            .musicResponsiveListItemFlexColumnRenderer
                            .text
                            .runs != null)
                        {
                            isSingle   = false;
                            albumTitle = content.musicResponsiveListItemRenderer
                                         .flexColumns[2]
                                         .musicResponsiveListItemFlexColumnRenderer
                                         .text
                                         .runs[0]
                                         .text;
                        }

                        if (!albumSongCollection.Albums.AlbumHashSet.Contains(albumTitle))
                        {
                            albumSongCollection.Albums.AlbumHashSet.Add(albumTitle);
                            albumSongCollection.Albums.Add(new Alumb
                            {
                                Title       = albumTitle,
                                CoverArtUrl = coverArtUrl,
                                Songs       = new SongCollection(),
                                EntityId    = isSingle ? "[Single]"
                                                    : GetAlbumEntityID(content.musicResponsiveListItemRenderer
                                                                       .flexColumns[2]
                                                                       .musicResponsiveListItemFlexColumnRenderer)
                            });
                        }

                        albumSongCollection.Songs.Add(song);
                        if (albumSongCollection.Albums.Where(m => m.Title == albumTitle).Any())
                        {
                            albumSongCollection.Albums.Where(m => m.Title == albumTitle).FirstOrDefault().Songs.Add(song);
                        }
                    }
                    catch (Exception e)
                    {
                        var _ = e;
#if DEBUG
                        Console.Out.WriteLine(e.Message);
#endif
                    }
                }
            }

            return(albumSongCollection);
        }
        private void BindAlbumNodesFromArtistBind(
            TreeNode artistNode,
            AlbumSongCollection albumSongCollection,
            bool expand             = true,
            bool showFetchedMessage = true)
        {
            var albumNodes = new List <TreeNode>();

            foreach (var album in albumSongCollection.Albums)
            {
                var    songNodes   = new List <TreeNode>();
                string releaseMbId = string.Empty;

                album.Songs.AsParallel().ForAllInApproximateOrder(song =>
                {
                    var musicFile = MainForm.MusicFileRepo.LoadFromEntityId(song.EntityId).Result;
                    string databaseExistenceText = "Not found or not mapped";

                    if (musicFile != null && musicFile.Id != 0 && musicFile.Id != -1)
                    {
                        databaseExistenceText = $"Exists ({musicFile.Id})";
                        releaseMbId           = string.IsNullOrEmpty(musicFile.ReleaseMbId) ? releaseMbId : musicFile.ReleaseMbId;
                    }

                    songNodes.Add(new TreeNode
                    {
                        Name = song.EntityId,
                        Text = song.Title,
                        Tag  = Tag = new MusicManageTreeNodeModel
                        {
                            NodeType          = MusicManageTreeNodeModel.NodeTypeEnum.Song,
                            ArtistTitle       = ((MusicManageTreeNodeModel)artistNode.Tag).ArtistTitle,
                            AlbumTitle        = album.Title,
                            SongTitle         = song.Title,
                            Duration          = song.Duration,
                            CovertArtUrl      = song.CoverArtUrl,
                            DatabaseExistence = databaseExistenceText,
                            MbId             = musicFile == null || string.IsNullOrEmpty(musicFile.MbId) ? "-" : musicFile.MbId,
                            EntityOrBrowseId = song.EntityId,
                            Uploaded         = musicFile == null ? "-" : musicFile.LastUpload.ToString("dd/MM/yyyy HH:mm")
                        }
                    });
                });

                var albumNode = new TreeNode
                {
                    Name = Guid.NewGuid().ToString(),
                    Text = album.Title,
                    Tag  = Tag = new MusicManageTreeNodeModel
                    {
                        NodeType          = MusicManageTreeNodeModel.NodeTypeEnum.Album,
                        ArtistTitle       = ((MusicManageTreeNodeModel)artistNode.Tag).ArtistTitle,
                        AlbumTitle        = album.Title,
                        CovertArtUrl      = album.CoverArtUrl,
                        DatabaseExistence = string.IsNullOrEmpty(releaseMbId) ? "Not found or not mapped" : "Tracks exists for this album",
                        MbId             = string.IsNullOrEmpty(releaseMbId) ? "-" : releaseMbId,
                        EntityOrBrowseId = album.EntityId,
                        Uploaded         = "-"
                    }
                };

                albumNode.Nodes.AddRange(songNodes.ToArray());
                albumNode.Text = albumNode.Text + " (" + songNodes.Count + ")";
                albumNodes.Add(albumNode);
            }

            AddChildNodesFromArtistBind(artistNode, albumNodes);

            int albumCount = albumSongCollection.Albums.Count;
            int songCount  = albumSongCollection.Songs.Count;

            string albumText = "albums";
            string songText  = "tracks";

            if (albumCount == 1)
            {
                albumText = "album";
            }

            if (songCount == 1)
            {
                songText = "track";
            }

            if (showFetchedMessage)
            {
                AppendUpdatesText($"Fetched {albumCount} {albumText}, {songCount} {songText}.",
                                  ColourHelper.HexStringToColor("#0d5601"));
            }

            artistNode.Text = artistNode.Text + " (" + artistNode.Nodes.Count + ")";
            if (expand)
            {
                artistNode.Expand();
            }

            if (artistNode.Checked)
            {
                CheckAllChildNodes(artistNode, true);
            }
        }