public YoutubeSongViewModel(YoutubeSong wrapped, Func<string> downloadPathFunc)
            : base(wrapped)
        {
            this.hasThumbnail = this.WhenAnyValue(x => x.Thumbnail)
                .Select(x => x != null)
                .ToProperty(this, x => x.HasThumbnail);

            // Wait for the opening of the context menu to download the YouTube information
            this.WhenAnyValue(x => x.IsContextMenuOpen)
                .FirstAsync(x => x)
                .SelectMany(_ => this.LoadContextMenu().ToObservable())
                .Subscribe();

            // We have to set a dummy here, so that we can connect the commands
            this.isDownloading = Observable.Never<bool>().ToProperty(this, x => x.IsDownloading);

            this.DownloadVideoCommand = ReactiveCommand.CreateAsyncTask(this.WhenAnyValue(x => x.IsDownloading).Select(x => !x),
                x => this.DownloadVideo((VideoInfo)x, downloadPathFunc()));

            this.DownloadAudioCommand = ReactiveCommand.CreateAsyncTask(this.WhenAnyValue(x => x.IsDownloading).Select(x => !x),
                x => this.DownloadAudio((VideoInfo)x, downloadPathFunc()));

            this.isDownloading = this.DownloadVideoCommand.IsExecuting
                .CombineLatest(this.DownloadAudioCommand.IsExecuting, (x1, x2) => x1 || x2)
                .ToProperty(this, x => x.IsDownloading);
        }
예제 #2
0
        public async Task<IReadOnlyList<YoutubeSong>> GetSongsAsync(string searchTerm)
        {
            var query = new YouTubeQuery(YouTubeQuery.DefaultVideoUri)
            {
                OrderBy = "relevance",
                Query = searchTerm,
                SafeSearch = YouTubeQuery.SafeSearchValues.None
            };

            try // The API gives no clue what can throw, wrap it all up
            {
                // NB: I have no idea where this API blocks exactly
                var settings = new YouTubeRequestSettings("Espera", ApiKey);
                var request = new YouTubeRequest(settings);
                Feed<Video> feed = await Task.Run(() => request.Get<Video>(query));
                List<Video> entries = await Task.Run(() => feed.Entries.ToList());

                var songs = new List<YoutubeSong>();

                foreach (Video video in entries)
                {
                    var duration = TimeSpan.FromSeconds(Int32.Parse(video.YouTubeEntry.Duration.Seconds));
                    string url = video.WatchPage.OriginalString
                        .Replace("&feature=youtube_gdata_player", String.Empty) // Unnecessary long url
                        .Replace("https://", "http://"); // Secure connections are not always easy to handle when streaming

                    var song = new YoutubeSong(url, duration)
                    {
                        Title = video.Title,
                        Description = video.Description,
                        Rating = video.RatingAverage >= 1 ? video.RatingAverage : (double?)null,
                        ThumbnailSource = new Uri(video.Thumbnails[0].Url),
                        Views = video.ViewCount
                    };

                    songs.Add(song);
                }

                return songs;
            }

            catch (Exception ex)
            {
                throw new NetworkSongFinderException("YoutubeSongFinder search failed", ex);
            }
        }
예제 #3
0
        private static IObservable <IReadOnlyList <YoutubeSong> > RealSearch(string searchTerm)
        {
            var query = new YouTubeQuery(YouTubeQuery.DefaultVideoUri)
            {
                OrderBy          = "relevance",
                Query            = searchTerm,
                SafeSearch       = YouTubeQuery.SafeSearchValues.None,
                NumberToRetrieve = RequestLimit
            };

            // NB: I have no idea where this API blocks exactly
            var settings = new YouTubeRequestSettings("Espera", ApiKey);
            var request  = new YouTubeRequest(settings);

            return(Observable.FromAsync(async() =>
            {
                Feed <Video> feed = await Task.Run(() => request.Get <Video>(query));
                List <Video> entries = await Task.Run(() => feed.Entries.ToList());

                var songs = new List <YoutubeSong>();

                foreach (Video video in entries)
                {
                    var duration = TimeSpan.FromSeconds(Int32.Parse(video.YouTubeEntry.Duration.Seconds));
                    string url = video.WatchPage.OriginalString
                                 .Replace("&feature=youtube_gdata_player", String.Empty) // Unnecessary long url
                                 .Replace("https://", "http://");                        // Secure connections are not always easy to handle when streaming

                    var song = new YoutubeSong(url, duration)
                    {
                        Artist = video.Uploader,
                        Title = video.Title,
                        Description = video.Description,
                        Rating = video.RatingAverage >= 1 ? video.RatingAverage : (double?)null,
                        ThumbnailSource = new Uri(video.Thumbnails[0].Url),
                        Views = video.ViewCount
                    };

                    songs.Add(song);
                }

                return songs;
            })
                   // The API gives no clue what can throw, wrap it all up
                   .Catch <IReadOnlyList <YoutubeSong>, Exception>(ex => Observable.Throw <IReadOnlyList <YoutubeSong> >(new NetworkSongFinderException("YoutubeSongFinder search failed", ex))));
        }
예제 #4
0
            public async Task NullUriThrowsArgumentNullException()
            {
                const string youtubePath = "http://youtube.com?v=yadda";
                var song = new YoutubeSong(youtubePath, TimeSpan.FromMinutes(1));
                var songFinder = Substitute.For<IYoutubeSongFinder>();
                songFinder.ResolveYoutubeSongFromUrl(Arg.Any<Uri>()).Returns(Task.FromResult(song));

                using (var library = new LibraryBuilder().WithPlaylist().Build())
                {
                    Guid accessToken = library.LocalAccessControl.RegisterLocalAccessToken();

                    var playlist = library.Playlists.First();
                    library.SwitchToPlaylist(playlist, accessToken);

                    var fixture = new DirectYoutubeViewModel(library, accessToken, songFinder);

                    await Helpers.ThrowsAsync<ArgumentNullException>(() => fixture.AddDirectYoutubeUrlToPlaylist(null, null));
                }
            }
        public void SmokeTest()
        {
            var song1 = new YoutubeSong("www.youtube.com?watch=abcde", TimeSpan.Zero) { Title = "A" };
            var song2 = new YoutubeSong("www.youtube.com?watch=abcdef", TimeSpan.Zero) { Title = "B" };

            var songs = (IReadOnlyList<YoutubeSong>)new[] { song1, song2 }.ToList();

            var songFinder = Substitute.For<IYoutubeSongFinder>();
            songFinder.GetSongsAsync(Arg.Any<string>()).Returns(Observable.Return(songs));

            using (var library = Helpers.CreateLibrary())
            {
                Guid token = library.LocalAccessControl.RegisterLocalAccessToken();
                var vm = new YoutubeViewModel(library, new ViewSettings(), new CoreSettings(), token, songFinder);

                Assert.Equal(songs, vm.SelectableSongs.Select(x => x.Model).ToList());
                Assert.Equal(songs.First(), vm.SelectableSongs.First().Model);
                Assert.False(vm.IsSearching);
            }
        }
예제 #6
0
            public async Task SmokeTest()
            {
                const string youtubePath = "http://youtube.com?v=yadda";
                var song = new YoutubeSong(youtubePath, TimeSpan.FromMinutes(1));
                var songFinder = Substitute.For<IYoutubeSongFinder>();
                songFinder.ResolveYoutubeSongFromUrl(Arg.Any<Uri>()).Returns(Task.FromResult(song));

                using (var library = new LibraryBuilder().WithPlaylist().Build())
                {
                    Guid accessToken = library.LocalAccessControl.RegisterLocalAccessToken();

                    var playlist = library.Playlists.First();
                    library.SwitchToPlaylist(playlist, accessToken);

                    var fixture = new DirectYoutubeViewModel(library, accessToken, songFinder);

                    await fixture.AddDirectYoutubeUrlToPlaylist(new Uri(youtubePath), null);

                    Assert.Equal(1, playlist.Count());
                }
            }
예제 #7
0
 public YoutubeSongViewModel(YoutubeSong wrapped)
     : base(wrapped)
 {
 }