Exemple #1
0
        public IObservable <IReadOnlyList <YoutubeSong> > GetSongsAsync(string searchTerm = null)
        {
            searchTerm = searchTerm ?? string.Empty;

            return(Observable.Defer(() => requestCache.GetOrFetchObject(BlobCacheKeys.GetKeyForYoutubeCache(searchTerm),
                                                                        () => RealSearch(searchTerm), DateTimeOffset.Now + CacheDuration)));
        }
Exemple #2
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.");
Exemple #3
0
        private Task <IBitmap> LoadImageFromCache(string key, int size)
        {
            // If we don't have the small version of an artwork, resize it, save it and return it.
            // This saves us a bunch of memory at the next startup, because BitmapImage has some
            // kind of memory leak, so the not-resized image hangs around in memory forever.

            string keyWithSize = BlobCacheKeys.GetArtworkKeyWithSize(key, size);

            return(this.LoadImageFromCacheSave(keyWithSize)
                   .Catch <IBitmap, KeyNotFoundException>(ex =>
                                                          this.LoadImageFromCacheSave(key, size)
                                                          .SelectMany(async resized =>
            {
                await this.SaveImageToBlobCacheAsync(key, resized);
                return resized;
            }))
                   .ToTask());
        }
Exemple #4
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);
        }
        public IObservable <IReadOnlyList <SoundCloudSong> > GetSongsAsync(string searchTerm = null)
        {
            searchTerm = searchTerm ?? string.Empty;

            IObservable <IReadOnlyList <SoundCloudSong> > retrievalFunc = Observable.Defer(() =>
                                                                                           requestCache.GetOrFetchObject(BlobCacheKeys.GetKeyForSoundCloudCache(searchTerm), () =>
                                                                                                                         string.IsNullOrWhiteSpace(searchTerm) ? GetPopularSongs() : SearchSongs(searchTerm), DateTimeOffset.Now + CacheDuration));

            return(retrievalFunc.Catch <IReadOnlyList <SoundCloudSong>, Exception>(ex =>
                                                                                   Observable.Throw <IReadOnlyList <SoundCloudSong> >(new NetworkSongFinderException("SoundCloud search failed", ex)))
                   .Select(x => x.Where(y => y.IsStreamable || y.IsDownloadable).ToList())
                   .Do(SetupSongUrls));
        }