Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Song download #10

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions Play.Tests/IntegrationTestUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ public static class IntegrationTestUrl
{
public static string Current {
get {
//return "https://play.yourcompany.com";
return "https://play.githubapp.com";
throw new Exception("Configure IntegrationTestUrl.cs first");
}
}

public static string Token {
get {
//return "a04d03";
return "f07c8b";
throw new Exception("Configure IntegrationTestUrl.cs first");
}
}
Expand Down
20 changes: 19 additions & 1 deletion Play.Tests/Models/PlayApiTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
Expand Down Expand Up @@ -112,5 +113,22 @@ public void MakeAlbumSearchIntegrationTest()
result.Count.Should().BeGreaterThan(2);
}

[Fact(Skip = "Only enable this to test downloads")]
public void DownloadAlbumIntegrationTest()
{
var kernel = new MoqMockingKernel();
var client = new RestClient(IntegrationTestUrl.Current);

client.AddDefaultHeader("Authorization", IntegrationTestUrl.Token);
kernel.Bind<IBlobCache>().To<TestBlobCache>();

var api = new PlayApi(client, kernel.Get<IBlobCache>());

var result = api.DownloadAlbum("Beirut", "Lon Gisland EP").First();

using (var of = File.OpenWrite("C:\\Users\\Administrator\\" + result.Item1)) {
new MemoryStream(result.Item2).CopyTo(of);
}
}
}
}
}
33 changes: 19 additions & 14 deletions Play.Tests/Play.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
<HintPath>..\packages\Microsoft.CompilerServices.AsyncTargetingPack.1.0.0\lib\net40\Microsoft.CompilerServices.AsyncTargetingPack.Net4.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Reactive.Testing, Version=1.1.11111.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ext\Microsoft.Reactive.Testing.dll</HintPath>
<HintPath>..\packages\Rx_Experimental-Testing.1.1.11111\lib\Net4-Full\Microsoft.Reactive.Testing.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
<Private>True</Private>
Expand Down Expand Up @@ -74,18 +74,24 @@
<Reference Include="PusherClientDotNet">
<HintPath>..\ext\PusherClientDotNet.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI">
<HintPath>..\ext\ReactiveUI.dll</HintPath>
<Reference Include="ReactiveUI, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-core.3.2.0\lib\Net4\ReactiveUI.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Routing">
<HintPath>..\ext\ReactiveUI.Routing.dll</HintPath>
<Reference Include="ReactiveUI.Blend">
<HintPath>..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Blend.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Testing">
<HintPath>..\ext\ReactiveUI.Testing.dll</HintPath>
<Reference Include="ReactiveUI.Routing, Version=3.1.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Routing.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Xaml, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ReactiveUI.Testing, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ext\ReactiveUI.Xaml.dll</HintPath>
<HintPath>..\packages\reactiveui-testing.3.2.0\lib\Net4\ReactiveUI.Testing.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Xaml, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Xaml.dll</HintPath>
</Reference>
<Reference Include="RestSharp">
<HintPath>..\packages\RestSharp.103.0.0-nojsondotnet\lib\net35\RestSharp.dll</HintPath>
Expand All @@ -94,12 +100,10 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Reactive, Version=1.1.11111.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ext\System.Reactive.dll</HintPath>
<HintPath>..\packages\Rx_Experimental-Main.1.1.11111\lib\Net4\System.Reactive.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Windows.Threading, Version=1.1.11111.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ext\System.Reactive.Windows.Threading.dll</HintPath>
<HintPath>..\packages\Rx_Experimental-Xaml.1.1.11111\lib\Net4\System.Reactive.Windows.Threading.dll</HintPath>
</Reference>
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
Expand All @@ -121,6 +125,7 @@
<Compile Include="Models\PlayApiTests.cs" />
<Compile Include="Models\PusherHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\BackgroundTaskTileViewModel.cs" />
<Compile Include="ViewModels\PlayViewModel.cs" />
<Compile Include="ViewModels\SearchViewModel.cs" />
<Compile Include="ViewModels\SongTileViewModelTests.cs" />
Expand Down
71 changes: 71 additions & 0 deletions Play.Tests/ViewModels/BackgroundTaskTileViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using Akavache;
using FluentAssertions;
using Moq;
using Ninject;
using Ninject.MockingKernel.Moq;
using Play.Models;
using Play.ViewModels;
using ReactiveUI;
using Xunit;

namespace Play.Tests.ViewModels
{
public class BackgroundTaskTileViewModelTests
{
[Fact]
public void DownloadAlbumShouldQueueABackgroundTask()
{
var kernel = new MoqMockingKernel();
var taskHost = new BackgroundTaskHostViewModel();
kernel.Bind<IBackgroundTaskHostViewModel>().ToConstant(taskHost);

var fixture = setupStandardFixture(Fakes.GetSong(), kernel);

var list = new List<int>();
taskHost.BackgroundTasks.CollectionCountChanged.Subscribe(list.Add);
fixture.DownloadAlbum.Execute(null);

list.Contains(1).Should().BeTrue();
}

[Fact]
public void DownloadSongShouldQueueABackgroundTask()
{
var kernel = new MoqMockingKernel();
var taskHost = new BackgroundTaskHostViewModel();
kernel.Bind<IBackgroundTaskHostViewModel>().ToConstant(taskHost);

var fixture = setupStandardFixture(Fakes.GetSong(), kernel);

var list = new List<int>();
taskHost.BackgroundTasks.CollectionCountChanged.Subscribe(list.Add);
fixture.DownloadSong.Execute(null);

list.Contains(1).Should().BeTrue();
}

static ISongTileViewModel setupStandardFixture(Song song, MoqMockingKernel kernel)
{
kernel.Bind<IBlobCache>().To<TestBlobCache>().Named("UserAccount");
kernel.Bind<IBlobCache>().To<TestBlobCache>().Named("LocalMachine");
RxApp.ConfigureServiceLocator((t,s) => kernel.Get(t,s), (t,s) => kernel.GetAll(t,s));

kernel.GetMock<IPlayApi>().Setup(x => x.FetchImageForAlbum(It.IsAny<Song>()))
.Returns(Observable.Return(new BitmapImage()));

kernel.GetMock<IPlayApi>().Setup(x => x.DownloadAlbum(It.IsAny<string>(), It.IsAny<string>()))
.Returns(Observable.Return<Tuple<string, byte[]>>(null));
kernel.GetMock<IPlayApi>().Setup(x => x.DownloadSong(It.IsAny<Song>()))
.Returns(Observable.Return<Tuple<string, byte[]>>(null));

return new SongTileViewModel(song, kernel.Get<IPlayApi>());
}
}
}
6 changes: 6 additions & 0 deletions Play.Tests/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
<package id="Ninject.MockingKernel" version="3.0.0.5" />
<package id="Ninject.MockingKernel.Moq" version="3.0.0.5" />
<package id="NLog" version="2.0.0.2000" />
<package id="reactiveui-core" version="3.2.0" targetFramework="net40" />
<package id="reactiveui-testing" version="3.2.0" targetFramework="net40" />
<package id="reactiveui-xaml" version="3.2.0" targetFramework="net40" />
<package id="RestSharp" version="103.0.0-nojsondotnet" />
<package id="Rx_Experimental-Main" version="1.1.11111" targetFramework="net40" />
<package id="Rx_Experimental-Testing" version="1.1.11111" targetFramework="net40" />
<package id="Rx_Experimental-Xaml" version="1.1.11111" targetFramework="net40" />
<package id="xunit" version="1.9.0.1566" />
<package id="xunit.extensions" version="1.9.0.1566" />
</packages>
Binary file added Play/Images/carbon_fibre_big.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions Play/Models/PlayApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Reactive;
using System.Reactive.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using Akavache;
Expand All @@ -28,6 +29,8 @@ public interface IPlayApi
IObservable<List<Song>> Search(string query);
IObservable<List<Song>> AllSongsForArtist(string name);
IObservable<List<Song>> AllSongsOnAlbum(string artist, string album);
IObservable<Tuple<string, byte[]>> DownloadSong(Song song);
IObservable<Tuple<string, byte[]>> DownloadAlbum(string artist, string album);

IObservable<Unit> ConnectToSongChangeNotifications();
}
Expand Down Expand Up @@ -124,6 +127,24 @@ public IObservable<List<Song>> AllSongsOnAlbum(string artist, string album)
return client.RequestAsync<SongQueue>(rq).Select(x => x.Data.songs);
}

public IObservable<Tuple<string, byte[]>> DownloadSong(Song song)
{
var rq = new RestRequest(String.Format("song/{0}/download", song.id));

return client.RequestAsync(rq)
.Select(x => Tuple.Create(fileNameFromResponse(x) ?? "song.mp3", x.RawBytes));
}

public IObservable<Tuple<string, byte[]>> DownloadAlbum(string artist, string album)
{
var rq = new RestRequest(String.Format("artist/{0}/album/{1}/download",
HttpUtility.UrlEncode(artist).Replace("+", "%20"),
HttpUtility.UrlEncode(album).Replace("+", "%20")));

return client.RequestAsync(rq)
.Select(x => Tuple.Create(fileNameFromResponse(x) ?? "songs.zip", x.RawBytes));
}

public IObservable<Unit> ConnectToSongChangeNotifications()
{
var rq = new RestRequest("streaming_info");
Expand All @@ -138,5 +159,18 @@ public IObservable<string> ListenUrl()
var rq = new RestRequest("streaming_info");
return client.RequestAsync<StreamingInfo>(rq).Select(x => x.Data.stream_url);
}

string fileNameFromResponse(RestResponse response)
{
var disp = response.Headers.FirstOrDefault(y => y.Name.Equals("content-disposition", StringComparison.InvariantCultureIgnoreCase));
if (disp == null) {
return null;
}

var re = new Regex("filename=\"([^\"]+)\"(;|$)");
var m = re.Match((string) disp.Value);

return m.Success ? m.Groups[1].Value : null;
}
}
}
61 changes: 49 additions & 12 deletions Play/Play.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
<CreateDesktopShortcut>true</CreateDesktopShortcut>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
<Utf8Output>true</Utf8Output>
<ExpressionBlendVersion>4.0.30816.0</ExpressionBlendVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand Down Expand Up @@ -80,6 +82,9 @@
<Reference Include="Hardcodet.Wpf.TaskbarNotification">
<HintPath>..\packages\Hardcodet.Wpf.TaskbarNotification.1.0.4.0\lib\net40\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CompilerServices.AsyncTargetingPack.Net4">
<HintPath>..\packages\Microsoft.CompilerServices.AsyncTargetingPack.1.0.0\lib\net40\Microsoft.CompilerServices.AsyncTargetingPack.Net4.dll</HintPath>
</Reference>
Expand Down Expand Up @@ -111,17 +116,21 @@
<Reference Include="PusherClientDotNet">
<HintPath>..\ext\PusherClientDotNet.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI">
<HintPath>..\ext\ReactiveUI.dll</HintPath>
<Reference Include="ReactiveUI, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-core.3.2.0\lib\Net4\ReactiveUI.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Blend">
<HintPath>..\ext\ReactiveUI.Blend.dll</HintPath>
<Reference Include="ReactiveUI.Blend, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Blend.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Routing">
<HintPath>..\ext\ReactiveUI.Routing.dll</HintPath>
<Reference Include="ReactiveUI.Routing, Version=3.1.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Routing.dll</HintPath>
</Reference>
<Reference Include="ReactiveUI.Xaml">
<HintPath>..\ext\ReactiveUI.Xaml.dll</HintPath>
<Reference Include="ReactiveUI.Xaml, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Xaml.dll</HintPath>
</Reference>
<Reference Include="RestSharp">
<HintPath>..\packages\RestSharp.103.0.0-nojsondotnet\lib\net35\RestSharp.dll</HintPath>
Expand All @@ -137,12 +146,10 @@
</Reference>
<Reference Include="System.Management" />
<Reference Include="System.Reactive, Version=1.1.11111.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ext\System.Reactive.dll</HintPath>
<HintPath>..\packages\Rx_Experimental-Main.1.1.11111\lib\Net4\System.Reactive.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Windows.Threading, Version=1.1.11111.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ext\System.Reactive.Windows.Threading.dll</HintPath>
<HintPath>..\packages\Rx_Experimental-Xaml.1.1.11111\lib\Net4\System.Reactive.Windows.Threading.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
Expand Down Expand Up @@ -174,10 +181,20 @@
<Compile Include="Models\Song.cs" />
<Compile Include="RestSharpRxHelper.cs" />
<Compile Include="ViewModels\AppBootstrapper.cs" />
<Compile Include="ViewModels\BackgroundTaskTileViewModel.cs" />
<Compile Include="ViewModels\PlayViewModel.cs" />
<Compile Include="ViewModels\SearchViewModel.cs" />
<Compile Include="ViewModels\SongTileViewModel.cs" />
<Compile Include="ViewModels\WelcomeViewModel.cs" />
<Compile Include="Views\BackgroundTaskHostView.xaml.cs">
<DependentUpon>BackgroundTaskHostView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\BackgroundTaskTileView.xaml.cs">
<DependentUpon>BackgroundTaskTileView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Dummy.xaml.cs">
<DependentUpon>Dummy.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SearchView.xaml.cs">
<DependentUpon>SearchView.xaml</DependentUpon>
</Compile>
Expand All @@ -202,10 +219,27 @@
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="Properties\DesignTimeResources.xaml" Condition="'$(DesignTime)'=='true' OR ('$(SolutionPath)'!='' AND Exists('$(SolutionPath)') AND '$(BuildingInsideVisualStudio)'!='true' AND '$(BuildingInsideExpressionBlend)'!='true')">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<ContainsDesignTimeResources>true</ContainsDesignTimeResources>
</Page>
<Page Include="Styles.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\BackgroundTaskHostView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\BackgroundTaskTileView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\Dummy.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SearchView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
Expand Down Expand Up @@ -296,6 +330,9 @@
<ItemGroup>
<Resource Include="Images\status-icon-on.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\carbon_fibre_big.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
8 changes: 8 additions & 0 deletions Play/Properties/DesignTimeResources.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Play;component/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Resource dictionary entries should be defined here. -->
</ResourceDictionary>
Loading