Ejemplo n.º 1
0
        public async ValueTask <MuzlanResponse <IList <SitemapRecord> > > GetVideos(CancellationToken token = default)
        {
            var pageUri    = new Uri($"{_baseUri.DnsSafeHost}/sitemap/videos");
            var pagination = new MuzlanPagination(pageUri, _client, _parser);

            var items = new List <SitemapRecord>();

            try
            {
                await foreach (var page in pagination.FollowForward(token).ConfigureAwait(false))
                {
                    await foreach (var item in EnumerateSitemapItems(page, token).ConfigureAwait(false))
                    {
                        items.Add(item);
                    }
                }

                return(MuzlanResponse <IList <SitemapRecord> > .FromResult(items, pagination.PageUri));
            }
            catch (Exception ex)
            {
                if (pagination.PageNumber == -1)
                {
                    return(MuzlanResponse <IList <SitemapRecord> > .FromException(ex, pagination.PageUri));
                }

                return(MuzlanResponse <IList <SitemapRecord> > .FromPartialResult(items, pagination.PageUri, pagination.PageNumber));
            }
        }
Ejemplo n.º 2
0
        public async ValueTask <MuzlanResponse <IList <AlbumRecord> > > FindAlbums(string artist, CancellationToken token = default)
        {
            var pageUri = new Uri($"https://{_baseUri.DnsSafeHost}/artist/{Uri.EscapeDataString(artist)}/all-albums");

            try
            {
                var content = await _client.TryGetStringAsync(pageUri, token).ConfigureAwait(false);

                using var document = await _parser.ParseDocumentAsync(content, token).ConfigureAwait(false);

                var albums = new List <AlbumRecord>();

                foreach (var item in document.QuerySelectorAll <IHtmlDivElement>("div.page-content div.row div.item"))
                {
                    var mediaImage = item.QuerySelector <IHtmlImageElement>("div.item-media a.item-media-content img");
                    var mediaTitle = item.QuerySelector <IHtmlAnchorElement>("div.item-info div.item-title a");

                    var imageUri = new Uri($"https://{_baseUri.DnsSafeHost}{mediaImage.Dataset["original"]}");

                    var albumName = mediaTitle.Text.Trim();
                    var albumUri  = new Uri($"https://{_baseUri.DnsSafeHost}{mediaTitle.PathName}");

                    var album = new AlbumRecord(artist, albumName, albumUri, imageUri);

                    albums.Add(album);
                }

                return(MuzlanResponse <IList <AlbumRecord> > .FromResult(albums, pageUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <IList <AlbumRecord> > .FromException(ex, pageUri));
            }
        }
Ejemplo n.º 3
0
        public async ValueTask <MuzlanResponse <IList <SearchQueryRecord> > > ListRecentSearches(CancellationToken token = default)
        {
            var pageUri = new Uri($"https://{_baseUri.DnsSafeHost}/last-queries");

            try
            {
                var content = await _client.TryGetStringAsync(pageUri, token).ConfigureAwait(false);

                using var document = await _parser.ParseDocumentAsync(content, token).ConfigureAwait(false);

                var searches = new List <SearchQueryRecord>();

                foreach (var searchElement in document.QuerySelectorAll <IHtmlAnchorElement>("div.page-content div.item-meta a.btn"))
                {
                    var name      = searchElement.Text.Trim();
                    var searchUri = new Uri($"https://{_baseUri.DnsSafeHost}{searchElement.PathName}");

                    var search = new SearchQueryRecord(name, searchUri);

                    searches.Add(search);
                }

                return(MuzlanResponse <IList <SearchQueryRecord> > .FromResult(searches, pageUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <IList <SearchQueryRecord> > .FromException(ex, pageUri));
            }
        }
Ejemplo n.º 4
0
        public async ValueTask <MuzlanResponse <IList <ArtistRecord> > > ListPopularArtists(CancellationToken token = default)
        {
            var pageUri = _baseUri;

            try
            {
                var content = await _client.TryGetStringAsync(pageUri, token).ConfigureAwait(false);

                using var document = await _parser.ParseDocumentAsync(content, token).ConfigureAwait(false);

                var artists = new List <ArtistRecord>();

                foreach (var artistElement in document.QuerySelectorAll <IHtmlDivElement>("div.page-content div.row-col div div.padding div.row div div.item"))
                {
                    var anchor = artistElement.QuerySelector <IHtmlAnchorElement>("div.item-info div.item-title.text-ellipsis a");
                    var image  = artistElement.QuerySelector <IHtmlImageElement>("div.item-media a.item-media-content img.lazy.img-responsive");

                    var name = anchor.Text.Trim();

                    var artistUri = new Uri($"https://{_baseUri.DnsSafeHost}/artist/{Uri.EscapeDataString(name)}");
                    var searchUri = new Uri($"https://{_baseUri.DnsSafeHost}{anchor.PathName}");
                    var imageUri  = new Uri($"https://{_baseUri.DnsSafeHost}{image.Dataset["original"]}");

                    var artist = new ArtistRecord(name, artistUri, searchUri, imageUri);

                    artists.Add(artist);
                }

                return(MuzlanResponse <IList <ArtistRecord> > .FromResult(artists, pageUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <IList <ArtistRecord> > .FromException(ex, pageUri));
            }
        }
Ejemplo n.º 5
0
        public async ValueTask <MuzlanResponse <IList <SearchArtistRecord> > > FindArtists(
            string query,
            CancellationToken token = default)
        {
            var pageUri = new Uri($"https://{_baseUri.DnsSafeHost}/search/{Uri.EscapeDataString(query)}");

            try
            {
                var content = await _client.TryGetStringAsync(
                    pageUri,
                    token).ConfigureAwait(false);

                using var document = await _parser.ParseDocumentAsync(content, token).ConfigureAwait(false);

                var artists = new List <SearchArtistRecord>();

                foreach (var artistElement in document.QuerySelectorAll <IHtmlDivElement>("div.page-content div.row-col div.row.item-list:not(#search-result-items)"))
                {
                    var imageElement       = artistElement.QuerySelector <IHtmlAnchorElement>("div.item-media a.item-media-content");
                    var titleElement       = artistElement.QuerySelector <IHtmlAnchorElement>("div.item-info div.item-title.text-ellipsis a");
                    var descriptionElement = artistElement.QuerySelector <IHtmlDivElement>("div.item-info div.item-except");

                    var artistName      = titleElement.Text.Trim();
                    var artistUri       = new Uri($"https://{_baseUri.DnsSafeHost}/artist/{Uri.EscapeDataString(query)}");
                    var artistSearchUri = new Uri($"https://{_baseUri.DnsSafeHost}/search/{Uri.EscapeDataString(query)}");

                    var match = _bgImageRegex.Match(imageElement.OuterHtml);

                    var artistImageUri = new Uri($"https://{_baseUri.DnsSafeHost}{match.Groups[1].Value}");

                    var description = string.IsNullOrEmpty(descriptionElement.TextContent)
                        ? string.Empty
                        : descriptionElement.TextContent.Trim();

                    var tags = new List <TagRecord>();

                    foreach (var tagElement in artistElement.QuerySelectorAll <IHtmlAnchorElement>("div.item-info div.item-meta a.btn"))
                    {
                        var tagName = tagElement.Text.Trim();
                        var tagUri  = new Uri($"https://{_baseUri.DnsSafeHost}{tagElement.PathName}");

                        tags.Add(new TagRecord(tagName, tagUri));
                    }

                    var artist = new ArtistRecord(artistName, artistUri, artistSearchUri, artistImageUri);

                    artists.Add(new SearchArtistRecord(artist, tags, description));
                }

                return(MuzlanResponse <IList <SearchArtistRecord> > .FromResult(artists, pageUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <IList <SearchArtistRecord> > .FromException(ex, pageUri));
            }
        }
Ejemplo n.º 6
0
        public async ValueTask <MuzlanResponse <IList <TrackRecord> > > FindTracks(
            Uri pageUri,
            int?limitItems = null,
            int?limitPages = null,
            CancellationToken cancellationToken = default)
        {
            var pagination = new MuzlanPagination(pageUri, _client, _parser);

            var tracks = new List <TrackRecord>();

            try
            {
                bool reachedLimit = false;
                int  itemCount    = 0;

                await foreach (var pageContent in pagination.FollowForward(cancellationToken).ConfigureAwait(false))
                {
                    reachedLimit = limitPages > 0 && pagination.PageCount >= limitPages;

                    await foreach (var track in EnumerateTracks(pageContent, cancellationToken).ConfigureAwait(false))
                    {
                        tracks.Add(track);

                        itemCount++;

                        reachedLimit = limitItems > 0 && itemCount >= limitItems;
                    }

                    if (reachedLimit)
                    {
                        break;
                    }
                }

                return(MuzlanResponse <IList <TrackRecord> > .FromResult(tracks, pagination.PageUri));
            }
            catch (Exception ex)
            {
                if (pagination.PageNumber == -1)
                {
                    return(MuzlanResponse <IList <TrackRecord> > .FromException(ex, pagination.PageUri, pagination.PageNumber));
                }

                return(MuzlanResponse <IList <TrackRecord> > .FromPartialResult(tracks, pagination.PageUri, pagination.PageNumber));
            }
        }
Ejemplo n.º 7
0
        public async ValueTask <MuzlanResponse <AuthRecord> > Authenticate(CancellationToken token = default)
        {
            ServerOnline = null;
            CsrfToken    = null;
            MediaToken   = null;

            var pageUri = _firstServerUri;

            try
            {
                var online = await CheckServerResponse(pageUri, token).ConfigureAwait(false);

                if (!online)
                {
                    pageUri = _secondServerUri;

                    online = await CheckServerResponse(pageUri, token).ConfigureAwait(false);

                    if (!online)
                    {
                        ServerOnline = false;

                        throw new HttpRequestException("Authentication servers seem to be unavailable.", null, HttpStatusCode.NoContent);
                    }
                }

                ServerOnline = true;

                pageUri = _baseUri;

                CsrfToken = await GetCsrfToken(pageUri, token).ConfigureAwait(false);

                pageUri = _mediaTokenUri;

                MediaToken = await GetMediaToken(pageUri, CsrfToken, token).ConfigureAwait(false);

                return(MuzlanResponse <AuthRecord> .FromResult(new AuthRecord(CsrfToken, MediaToken), pageUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <AuthRecord> .FromException(ex, pageUri));
            }
        }
Ejemplo n.º 8
0
        public async ValueTask <MuzlanResponse <IList <TrackRecord> > > GetTracks(string artist, string album, CancellationToken token = default)
        {
            var pageUri = new Uri($"https://{_baseUri.DnsSafeHost}/artist/{Uri.EscapeDataString(artist)}/album/{Uri.EscapeDataString(album)}");

            try
            {
                var content = await _client.TryGetStringAsync(pageUri, token).ConfigureAwait(false);

                using var document = await _parser.ParseDocumentAsync(content, token).ConfigureAwait(false);

                var tracks = new List <TrackRecord>();

                foreach (var item in document.QuerySelectorAll <IHtmlDivElement>("div.page-content div.row-col div.row.item-list div.item.track"))
                {
                    var trackItem  = item.QuerySelector <IHtmlAnchorElement>("div.item-info div.item-title a");
                    var authorItem = item.QuerySelector <IHtmlAnchorElement>("div.item-info div.item-author a");

                    var trackName = trackItem.Text.Trim();
                    var trackUri  = new Uri($"https://{_baseUri.DnsSafeHost}{trackItem.PathName}");

                    var trackDownload = new Uri($"https://{_baseUri.DnsSafeHost}{item.Dataset["src"]}");

                    var artistName = authorItem.Text.Trim();

                    var track = new TrackRecord(trackName, artistName, trackUri, trackDownload);

                    tracks.Add(track);
                }

                return(MuzlanResponse <IList <TrackRecord> > .FromResult(tracks, pageUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <IList <TrackRecord> > .FromException(ex, pageUri));
            }
        }
Ejemplo n.º 9
0
        public async ValueTask <MuzlanResponse <DownloadRecord> > DownloadTrack(TrackRecord track, AuthRecord auth, CancellationToken cancellationToken = default)
        {
            var sourceUri = new Uri(track.DownloadUri, $"?key={auth.MediaToken}");

            var fileExtension = NormalizeFileExtension(Path.GetExtension(sourceUri.LocalPath));
            var fileName      = track.Name;

            try
            {
                using var response = await _client.GetAsync(sourceUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);

                response.EnsureSuccessStatusCode();

                var contentLength = response.Content.Headers.GetValues("Content-Length").First();

                var fileSize   = int.Parse(contentLength);
                var fileOffset = 0;
                var bytesRead  = 0;
                var fileData   = new byte[fileSize];

                await OnDownloadProgressChanged(fileOffset, fileSize).ConfigureAwait(false);

                using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);

                bool hasCompletedEvent = false;

                do
                {
                    if (fileOffset >= fileSize)
                    {
                        break;
                    }

                    bytesRead = await stream.ReadAsync(
                        ConvertToMemory(fileData, fileOffset, 8192),
                        cancellationToken).ConfigureAwait(false);

                    fileOffset += bytesRead;

                    if (fileOffset == fileSize)
                    {
                        hasCompletedEvent = true;
                    }

                    await OnDownloadProgressChanged(fileOffset, fileSize).ConfigureAwait(false);
                } while (bytesRead > 0);

                if (!hasCompletedEvent)
                {
                    await OnDownloadProgressChanged(fileSize, fileSize).ConfigureAwait(false);
                }

                var file = new DownloadRecord(fileName + fileExtension, fileData, sourceUri);

                return(MuzlanResponse <DownloadRecord> .FromResult(file, sourceUri));
            }
            catch (Exception ex)
            {
                return(MuzlanResponse <DownloadRecord> .FromException(ex, sourceUri));
            }
        }