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))); }
/// <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.");
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()); }
/// <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)); }