Exemple #1
0
        /// <summary>
        /// Fetches an artwork for the specified combination of artist and album.
        /// </summary>
        /// <returns>
        /// The cache key of the fetched artwork, or <c>null</c> , if no artwork could be found.
        /// </returns>
        /// <exception cref="ArtworkCacheException">
        /// An error occured while fetching or storing the artwork.
        /// </exception>
        public async Task <string> FetchOnline(string artist, string album)
        {
            if (artist == null)
            {
                throw new ArgumentNullException(nameof(artist));
            }

            if (album == null)
            {
                throw new ArgumentNullException(nameof(album));
            }

            string lookupKey = BlobCacheKeys.GetKeyForOnlineArtwork(artist, album);

            // Requests with the same lookup key have to wait on the first and then get the cached
            // artwork key. That won't happen often, but when it does, we are save.
            await this.keyedMemoizingSemaphore.Wait(lookupKey);

            string artworkCacheKey = null;

            // Each lookup key gets an artwork key assigned, let's see if it's already in the cache
            if (await this.cache.GetObjectCreatedAt <string>(lookupKey) != null)
            {
                artworkCacheKey = await this.queue.EnqueueObservableOperation(1, () => this.cache.GetObject <string>(lookupKey));
            }

            // Previously failed lookups are marked as failed, it doesn't make sense to let it fail again
            if (artworkCacheKey == OnlineFailMark)
            {
                this.Log().Debug($"Key {lookupKey} is marked as failed, returning.");

                this.keyedMemoizingSemaphore.Release(lookupKey);

                return(null);
            }

            if (artworkCacheKey != null)
            {
                // We already have the artwork cached? Great!

                this.keyedMemoizingSemaphore.Release(lookupKey);

                return(artworkCacheKey);
            }

            this.Log().Info($"Fetching online link for artwork {artist} - {album}");

            Uri artworkLink;

            try
            {
                artworkLink = await this.artworkFetcher.RetrieveAsync(artist, album);
            }

            catch (ArtworkFetchException ex)
            {
                this.keyedMemoizingSemaphore.Release(lookupKey);

                throw new ArtworkCacheException("Could not retrieve the artwork information", ex);
            }

            if (artworkLink == null)
            {
                await this.MarkOnlineLookupKeyAsFailed(lookupKey);

                this.keyedMemoizingSemaphore.Release(lookupKey);

                return(null);
            }

            byte[] imageData;

            using (var client = new HttpClient())
            {
                this.Log().Info($"Downloading artwork data for {artist} - {album} from {artworkLink}");

                try
                {
                    imageData = await client.GetByteArrayAsync(artworkLink);
                }

                catch (HttpRequestException ex)
                {
                    this.keyedMemoizingSemaphore.Release(lookupKey);

                    throw new ArtworkCacheException($"Unable to download artwork from {artworkLink}", ex);
                }
            }

            artworkCacheKey = BlobCacheKeys.GetKeyForArtwork(imageData);
            await this.Store(artworkCacheKey, imageData);

            await this.queue.EnqueueObservableOperation(1, () => this.cache.InsertObject(lookupKey, artworkCacheKey));

            this.keyedMemoizingSemaphore.Release(lookupKey);

            return(artworkCacheKey);
        }