Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

API updates

  • Loading branch information...
commit 601297867ef0175a24ed8f1ee44f10e32b929943 1 parent 7788946
Alxandr authored
478 SpotiFire.SpotifyLib/AwaitHelper.cs
View
@@ -1,239 +1,239 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.ExceptionServices;
-using System.Security;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace SpotiFire.SpotifyLib
-{
- static class AwaitHelper
- {
- internal static void OnComplete(ISpotifyAwaitable awaitable, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
- {
- Continuation _continuation = null;
- if (continuation != null)
- {
- if (continueOnCapturedContext)
- {
- SynchronizationContext synchronizationContext = SynchronizationContext.Current;
- if (synchronizationContext == null)
- {
- /*do nothing*/
- }
- else
- {
- _continuation = new SynchronizationContextContinuation(synchronizationContext, continuation, flowExecutionContext);
- }
- }
-
- if (_continuation == null && flowExecutionContext)
- {
- _continuation = new Continuation(continuation, true);
- }
-
- if (_continuation == null)
- {
- if (!awaitable.AddContinuation(new Continuation(continuation, false), false))
- {
- UnsafeScheduleAction(continuation);
- }
- }
- else
- {
- if (!awaitable.AddContinuation(_continuation, false))
- {
- _continuation.Run(awaitable, false);
- }
- }
- }
- else
- {
- throw new ArgumentNullException("continuation");
- }
- }
-
- internal static ISpotifyAwaiter<T> GetAwaiter<T>(T value)
- where T : ISpotifyObject
- {
- return new AwaitableAwaiter<T>(value);
- }
-
- internal class Continuation
- {
- [SecurityCritical]
- static ContextCallback _invokeActionCallback;
-
- readonly protected Action _action;
- readonly protected bool _flowExecutionContext;
- readonly protected ExecutionContext _capturedContext;
-
- [SecurityCritical]
- protected static ContextCallback GetInvokeActionCallback()
- {
- ContextCallback sInvokeCallback = _invokeActionCallback;
- if (sInvokeCallback == null)
- {
- ContextCallback contextCallback = new ContextCallback(InvokeAction);
- sInvokeCallback = contextCallback;
- _invokeActionCallback = contextCallback;
- }
- return sInvokeCallback;
- }
-
- static void InvokeAction(object state)
- {
- ((Action)state)();
- }
-
- public Continuation (Action action, bool flowExecutionContext)
- {
- _action = action;
- _flowExecutionContext = flowExecutionContext;
- if (flowExecutionContext)
- _capturedContext = ExecutionContext.Capture();
- }
-
- public virtual void WaitCallback(object state)
- {
- throw new NotImplementedException();
- }
-
-
- public virtual void Run(ISpotifyAwaitable awaitable, bool canInlineContinueTask)
- {
- throw new NotImplementedException();
- }
-
- [SecurityCritical]
- protected void RunCallback(ContextCallback callback, object state)
- {
- try
- {
- if (_capturedContext != null)
- ExecutionContext.Run(_capturedContext, callback, state);
- else
- callback(state);
- }
- catch (Exception e)
- {
- if (e as ThreadAbortException == null || e as AppDomainUnloadedException == null)
- {
- ExceptionDispatchInfo info = ExceptionDispatchInfo.Capture(e);
- ThreadPool.QueueUserWorkItem((object s) => ((ExceptionDispatchInfo)s).Throw(), info);
- }
- }
- finally
- {
- if (_capturedContext != null)
- _capturedContext.Dispose();
- }
- }
- }
-
- sealed class SynchronizationContextContinuation : Continuation
- {
- readonly SynchronizationContext _synchronizationContext;
-
- static readonly SendOrPostCallback _postCallback;
-
- [SecurityCritical]
- static ContextCallback _postActionCallback;
-
- public SynchronizationContextContinuation (SynchronizationContext synchronizationContext, Action action, bool flowExecutionContext)
- : base(action, flowExecutionContext)
- {
- _synchronizationContext = synchronizationContext;
- }
-
- static SynchronizationContextContinuation()
- {
- _postCallback = (object state) => ((Action)state)();
- }
-
- public override void WaitCallback(object state)
- {
- throw new NotImplementedException();
- }
-
- [SecurityCritical]
- static ContextCallback GetPostActionCallback()
- {
- ContextCallback sPostActionCallback = _postActionCallback;
- if (sPostActionCallback == null)
- {
- ContextCallback contextCallback = new ContextCallback(PostAction);
- sPostActionCallback = contextCallback;
- _postActionCallback = contextCallback;
- }
- return sPostActionCallback;
- }
-
- [SecurityCritical]
- static void PostAction(object state)
- {
- SynchronizationContextContinuation context = (SynchronizationContextContinuation)state;
- context._synchronizationContext.Post(_postCallback, context._action);
- }
-
- [SecurityCritical]
- public override void Run(ISpotifyAwaitable awaitable, bool canInlineContinueTask)
- {
- if (!canInlineContinueTask || _synchronizationContext != SynchronizationContext.Current)
- RunCallback(GetPostActionCallback(), this);
- else
- RunCallback(GetInvokeActionCallback(), _action);
- }
- }
-
- static void UnsafeScheduleAction(Action action)
- {
- ThreadPool.UnsafeQueueUserWorkItem(new Continuation(action, false).WaitCallback, null);
- }
-
- struct AwaitableAwaiter<T> : ISpotifyAwaiter<T>
- where T : ISpotifyObject
- {
- T result;
- ISpotifyAwaitable awaitable;
-
- public AwaitableAwaiter(T value)
- {
- result = value;
- awaitable = null;
- }
-
- ISpotifyAwaitable Awaitable
- {
- get
- {
- if (awaitable == null)
- Interlocked.CompareExchange(ref awaitable, (ISpotifyAwaitable)result, null);
- return awaitable;
- }
- }
-
- public bool IsCompleted
- {
- get { return Awaitable.IsComplete; }
- }
-
- public T GetResult()
- {
- return result;
- }
-
- public void OnCompleted(Action continuation)
- {
- AwaitHelper.OnComplete(Awaitable, continuation, true, true);
- }
-
- public void UnsafeOnCompleted(Action continuation)
- {
- AwaitHelper.OnComplete(Awaitable, continuation, true, false);
- }
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.ExceptionServices;
+using System.Security;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpotiFire.SpotifyLib
+{
+ static class AwaitHelper
+ {
+ internal static void OnComplete(ISpotifyAwaitable awaitable, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
+ {
+ Continuation _continuation = null;
+ if (continuation != null)
+ {
+ if (continueOnCapturedContext)
+ {
+ SynchronizationContext synchronizationContext = SynchronizationContext.Current;
+ if (synchronizationContext == null)
+ {
+ /*do nothing*/
+ }
+ else
+ {
+ _continuation = new SynchronizationContextContinuation(synchronizationContext, continuation, flowExecutionContext);
+ }
+ }
+
+ if (_continuation == null && flowExecutionContext)
+ {
+ _continuation = new Continuation(continuation, true);
+ }
+
+ if (_continuation == null)
+ {
+ if (!awaitable.AddContinuation(new Continuation(continuation, false), false))
+ {
+ UnsafeScheduleAction(continuation);
+ }
+ }
+ else
+ {
+ if (!awaitable.AddContinuation(_continuation, false))
+ {
+ _continuation.Run(awaitable, false);
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentNullException("continuation");
+ }
+ }
+
+ internal static ISpotifyAwaiter<T> GetAwaiter<T>(T value)
+ where T : ISpotifyObject
+ {
+ return new AwaitableAwaiter<T>(value);
+ }
+
+ internal class Continuation
+ {
+ [SecurityCritical]
+ static ContextCallback _invokeActionCallback;
+
+ readonly protected Action _action;
+ readonly protected bool _flowExecutionContext;
+ readonly protected ExecutionContext _capturedContext;
+
+ [SecurityCritical]
+ protected static ContextCallback GetInvokeActionCallback()
+ {
+ ContextCallback sInvokeCallback = _invokeActionCallback;
+ if (sInvokeCallback == null)
+ {
+ ContextCallback contextCallback = new ContextCallback(InvokeAction);
+ sInvokeCallback = contextCallback;
+ _invokeActionCallback = contextCallback;
+ }
+ return sInvokeCallback;
+ }
+
+ static void InvokeAction(object state)
+ {
+ ((Action)state)();
+ }
+
+ public Continuation (Action action, bool flowExecutionContext)
+ {
+ _action = action;
+ _flowExecutionContext = flowExecutionContext;
+ if (flowExecutionContext)
+ _capturedContext = ExecutionContext.Capture();
+ }
+
+ public virtual void WaitCallback(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+
+ public virtual void Run(ISpotifyAwaitable awaitable, bool canInlineContinueTask)
+ {
+ RunCallback(GetInvokeActionCallback(), _action);
+ }
+
+ [SecurityCritical]
+ protected void RunCallback(ContextCallback callback, object state)
+ {
+ try
+ {
+ if (_capturedContext != null)
+ ExecutionContext.Run(_capturedContext, callback, state);
+ else
+ callback(state);
+ }
+ catch (Exception e)
+ {
+ if (e as ThreadAbortException == null || e as AppDomainUnloadedException == null)
+ {
+ ExceptionDispatchInfo info = ExceptionDispatchInfo.Capture(e);
+ ThreadPool.QueueUserWorkItem((object s) => ((ExceptionDispatchInfo)s).Throw(), info);
+ }
+ }
+ finally
+ {
+ if (_capturedContext != null)
+ _capturedContext.Dispose();
+ }
+ }
+ }
+
+ sealed class SynchronizationContextContinuation : Continuation
+ {
+ readonly SynchronizationContext _synchronizationContext;
+
+ static readonly SendOrPostCallback _postCallback;
+
+ [SecurityCritical]
+ static ContextCallback _postActionCallback;
+
+ public SynchronizationContextContinuation (SynchronizationContext synchronizationContext, Action action, bool flowExecutionContext)
+ : base(action, flowExecutionContext)
+ {
+ _synchronizationContext = synchronizationContext;
+ }
+
+ static SynchronizationContextContinuation()
+ {
+ _postCallback = (object state) => ((Action)state)();
+ }
+
+ public override void WaitCallback(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ [SecurityCritical]
+ static ContextCallback GetPostActionCallback()
+ {
+ ContextCallback sPostActionCallback = _postActionCallback;
+ if (sPostActionCallback == null)
+ {
+ ContextCallback contextCallback = new ContextCallback(PostAction);
+ sPostActionCallback = contextCallback;
+ _postActionCallback = contextCallback;
+ }
+ return sPostActionCallback;
+ }
+
+ [SecurityCritical]
+ static void PostAction(object state)
+ {
+ SynchronizationContextContinuation context = (SynchronizationContextContinuation)state;
+ context._synchronizationContext.Post(_postCallback, context._action);
+ }
+
+ [SecurityCritical]
+ public override void Run(ISpotifyAwaitable awaitable, bool canInlineContinueTask)
+ {
+ if (!canInlineContinueTask || _synchronizationContext != SynchronizationContext.Current)
+ RunCallback(GetPostActionCallback(), this);
+ else
+ RunCallback(GetInvokeActionCallback(), _action);
+ }
+ }
+
+ static void UnsafeScheduleAction(Action action)
+ {
+ ThreadPool.UnsafeQueueUserWorkItem(new Continuation(action, false).WaitCallback, null);
+ }
+
+ struct AwaitableAwaiter<T> : ISpotifyAwaiter<T>
+ where T : ISpotifyObject
+ {
+ T result;
+ ISpotifyAwaitable awaitable;
+
+ public AwaitableAwaiter(T value)
+ {
+ result = value;
+ awaitable = null;
+ }
+
+ ISpotifyAwaitable Awaitable
+ {
+ get
+ {
+ if (awaitable == null)
+ Interlocked.CompareExchange(ref awaitable, (ISpotifyAwaitable)result, null);
+ return awaitable;
+ }
+ }
+
+ public bool IsCompleted
+ {
+ get { return Awaitable.IsComplete; }
+ }
+
+ public T GetResult()
+ {
+ return result;
+ }
+
+ public void OnCompleted(Action continuation)
+ {
+ AwaitHelper.OnComplete(Awaitable, continuation, true, true);
+ }
+
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ AwaitHelper.OnComplete(Awaitable, continuation, true, false);
+ }
+ }
+ }
+}
2  SpotiFire.SpotifyLib/Interfaces/ITrack.cs
View
@@ -9,7 +9,7 @@ public interface ITrack : ISpotifyObject, IAsyncLoaded
TimeSpan Duration { get; }
sp_error Error { get; }
int Index { get; }
- bool IsAvailable { get; }
+ TrackAvailability Availability { get; }
bool IsStarred { get; set; }
string Name { get; }
int Popularity { get; }
267 SpotiFire.SpotifyLib/SpotiFire.SpotifyLib.csproj
View
@@ -1,134 +1,135 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>8.0.30703</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{CBF5614B-AF79-4228-8265-7DC2BAF9A626}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>SpotiFire.SpotifyLib</RootNamespace>
- <AssemblyName>SpotiFire.SpotifyLib</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <TargetFrameworkProfile />
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="NLog">
- <HintPath>..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Drawing" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="AwaitHelper.cs" />
- <Compile Include="EventArgs\AlbumBrowseEventArgs.cs" />
- <Compile Include="EventArgs\ArtistBrowseEventArgs.cs" />
- <Compile Include="Interfaces\IAlbumBrowse.cs" />
- <Compile Include="Interfaces\IArtistBrowse.cs" />
- <Compile Include="Interfaces\IAsyncLoaded.cs" />
- <Compile Include="Interfaces\IContainerPlaylist.cs" />
- <Compile Include="Interfaces\IPlaylistList.cs" />
- <Compile Include="Interfaces\IPlaylistTrack.cs" />
- <Compile Include="Interfaces\ILink.cs" />
- <Compile Include="Interfaces\IPlaylistTrackList.cs" />
- <Compile Include="Interfaces\ISpotifyAwaitable.cs" />
- <Compile Include="Interfaces\ISpotifyAwaiter.cs" />
- <Compile Include="LinkExtensions.cs" />
- <Compile Include="Interfaces\ITrackAndOffset.cs" />
- <Compile Include="PlaylistList.cs" />
- <Compile Include="PlaylistTrackList.cs" />
- <Compile Include="SpotifyLibExtensions.cs" />
- <Compile Include="SpotifyTypes\AlbumBrowse.cs" />
- <Compile Include="SpotifyTypes\ArtistBrowse.cs" />
- <Compile Include="SpotifyTypes\Link.cs" />
- <Compile Include="SpotifyTypes\PlaylistTrack.cs" />
- <Compile Include="Spotify.cs" />
- <Compile Include="SpotifyTypes\Album.cs" />
- <Compile Include="SpotifyTypes\Artist.cs" />
- <Compile Include="CountedDisposeableSpotifyObject.cs" />
- <Compile Include="DelegateArray.cs" />
- <Compile Include="DelegateList.cs" />
- <Compile Include="EventArgs\DescriptionEventArgs.cs" />
- <Compile Include="DisposeableSpotifyObject.cs" />
- <Compile Include="EventWorkerItem.cs" />
- <Compile Include="Interfaces\IAlbum.cs" />
- <Compile Include="Interfaces\IArray.cs" />
- <Compile Include="Interfaces\IArtist.cs" />
- <Compile Include="Interfaces\IEditableArray.cs" />
- <Compile Include="Interfaces\IImage.cs" />
- <Compile Include="SpotifyTypes\ContainerPlaylist.cs" />
- <Compile Include="SpotifyTypes\Image.cs" />
- <Compile Include="Interfaces\IPlaylist.cs" />
- <Compile Include="Interfaces\IPlaylistContainer.cs" />
- <Compile Include="Interfaces\ISearch.cs" />
- <Compile Include="Interfaces\ISpotifyObject.cs" />
- <Compile Include="Interfaces\ITrack.cs" />
- <Compile Include="libspotify.cs" />
- <Compile Include="EventArgs\MusicDeliveryEventArgs.cs" />
- <Compile Include="EventArgs\ImageEventArgs.cs" />
- <Compile Include="Interfaces\ISession.cs" />
- <Compile Include="SpotifyTypes\Playlist.cs" />
- <Compile Include="SpotifyTypes\PlaylistContainer.cs" />
- <Compile Include="EventArgs\PlaylistEventArgs.cs" />
- <Compile Include="EventArgs\PlaylistMovedEventArgs.cs" />
- <Compile Include="EventArgs\PlaylistUpdateEventArgs.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="SpotifyTypes\Search.cs" />
- <Compile Include="EventArgs\SearchEventArgs.cs" />
- <Compile Include="SpotifyTypes\Session.cs" />
- <Compile Include="EventArgs\SessionEventArgs.cs" />
- <Compile Include="SpotifyException.cs" />
- <Compile Include="SpotifyTypes\Track.cs" />
- <Compile Include="EventArgs\TracksEventArgs.cs" />
- <Compile Include="EventArgs\TracksAddedEventArgs.cs" />
- <Compile Include="EventArgs\TracksMovedEventArgs.cs" />
- <Compile Include="EventArgs\TrackCreatedChangedEventArgs.cs" />
- <Compile Include="EventArgs\TrackEventArgs.cs" />
- <Compile Include="EventArgs\TrackSeenEventArgs.cs" />
- </ItemGroup>
- <ItemGroup>
- <Content Include="..\libspotify.dll">
- <Link>libspotify.dll</Link>
- <CopyToOutputDirectory>Always</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{CBF5614B-AF79-4228-8265-7DC2BAF9A626}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>SpotiFire.SpotifyLib</RootNamespace>
+ <AssemblyName>SpotiFire.SpotifyLib</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+ <Prefer32Bit>false</Prefer32Bit>
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="NLog">
+ <HintPath>..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AwaitHelper.cs" />
+ <Compile Include="EventArgs\AlbumBrowseEventArgs.cs" />
+ <Compile Include="EventArgs\ArtistBrowseEventArgs.cs" />
+ <Compile Include="Interfaces\IAlbumBrowse.cs" />
+ <Compile Include="Interfaces\IArtistBrowse.cs" />
+ <Compile Include="Interfaces\IAsyncLoaded.cs" />
+ <Compile Include="Interfaces\IContainerPlaylist.cs" />
+ <Compile Include="Interfaces\IPlaylistList.cs" />
+ <Compile Include="Interfaces\IPlaylistTrack.cs" />
+ <Compile Include="Interfaces\ILink.cs" />
+ <Compile Include="Interfaces\IPlaylistTrackList.cs" />
+ <Compile Include="Interfaces\ISpotifyAwaitable.cs" />
+ <Compile Include="Interfaces\ISpotifyAwaiter.cs" />
+ <Compile Include="LinkExtensions.cs" />
+ <Compile Include="Interfaces\ITrackAndOffset.cs" />
+ <Compile Include="PlaylistList.cs" />
+ <Compile Include="PlaylistTrackList.cs" />
+ <Compile Include="SpotifyLibExtensions.cs" />
+ <Compile Include="SpotifyTypes\AlbumBrowse.cs" />
+ <Compile Include="SpotifyTypes\ArtistBrowse.cs" />
+ <Compile Include="SpotifyTypes\Link.cs" />
+ <Compile Include="SpotifyTypes\PlaylistTrack.cs" />
+ <Compile Include="Spotify.cs" />
+ <Compile Include="SpotifyTypes\Album.cs" />
+ <Compile Include="SpotifyTypes\Artist.cs" />
+ <Compile Include="CountedDisposeableSpotifyObject.cs" />
+ <Compile Include="DelegateArray.cs" />
+ <Compile Include="DelegateList.cs" />
+ <Compile Include="EventArgs\DescriptionEventArgs.cs" />
+ <Compile Include="DisposeableSpotifyObject.cs" />
+ <Compile Include="EventWorkerItem.cs" />
+ <Compile Include="Interfaces\IAlbum.cs" />
+ <Compile Include="Interfaces\IArray.cs" />
+ <Compile Include="Interfaces\IArtist.cs" />
+ <Compile Include="Interfaces\IEditableArray.cs" />
+ <Compile Include="Interfaces\IImage.cs" />
+ <Compile Include="SpotifyTypes\ContainerPlaylist.cs" />
+ <Compile Include="SpotifyTypes\Image.cs" />
+ <Compile Include="Interfaces\IPlaylist.cs" />
+ <Compile Include="Interfaces\IPlaylistContainer.cs" />
+ <Compile Include="Interfaces\ISearch.cs" />
+ <Compile Include="Interfaces\ISpotifyObject.cs" />
+ <Compile Include="Interfaces\ITrack.cs" />
+ <Compile Include="libspotify.cs" />
+ <Compile Include="EventArgs\MusicDeliveryEventArgs.cs" />
+ <Compile Include="EventArgs\ImageEventArgs.cs" />
+ <Compile Include="Interfaces\ISession.cs" />
+ <Compile Include="SpotifyTypes\Playlist.cs" />
+ <Compile Include="SpotifyTypes\PlaylistContainer.cs" />
+ <Compile Include="EventArgs\PlaylistEventArgs.cs" />
+ <Compile Include="EventArgs\PlaylistMovedEventArgs.cs" />
+ <Compile Include="EventArgs\PlaylistUpdateEventArgs.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SpotifyTypes\Search.cs" />
+ <Compile Include="EventArgs\SearchEventArgs.cs" />
+ <Compile Include="SpotifyTypes\Session.cs" />
+ <Compile Include="EventArgs\SessionEventArgs.cs" />
+ <Compile Include="SpotifyException.cs" />
+ <Compile Include="SpotifyTypes\Track.cs" />
+ <Compile Include="EventArgs\TracksEventArgs.cs" />
+ <Compile Include="EventArgs\TracksAddedEventArgs.cs" />
+ <Compile Include="EventArgs\TracksMovedEventArgs.cs" />
+ <Compile Include="EventArgs\TrackCreatedChangedEventArgs.cs" />
+ <Compile Include="EventArgs\TrackEventArgs.cs" />
+ <Compile Include="EventArgs\TrackSeenEventArgs.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="..\libspotify.dll">
+ <Link>libspotify.dll</Link>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
</Project>
1,540 SpotiFire.SpotifyLib/SpotifyTypes/Playlist.cs
View
@@ -1,770 +1,770 @@
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
-using System.Runtime.InteropServices;
-
-namespace SpotiFire.SpotifyLib
-{
- public delegate void PlaylistEventHandler(IPlaylist playlist, EventArgs args);
- public delegate void PlaylistEventHandler<TEventArgs>(IPlaylist playlist, TEventArgs args) where TEventArgs : EventArgs;
-
-
-
- internal class Playlist : CountedDisposeableSpotifyObject, IPlaylist
- {
- #region Wrapper
- internal protected class PlaylistWrapper : DisposeableSpotifyObject, IPlaylist
- {
- internal Playlist playlist;
- public PlaylistWrapper(Playlist playlist)
- {
- this.playlist = playlist;
- playlist.TracksAdded += new PlaylistEventHandler<TracksAddedEventArgs>(playlist_TracksAdded);
- playlist.TracksRemoved += new PlaylistEventHandler<TracksEventArgs>(playlist_TracksRemoved);
- playlist.TracksMoved += new PlaylistEventHandler<TracksMovedEventArgs>(playlist_TracksMoved);
- playlist.Renamed += new PlaylistEventHandler(playlist_Renamed);
- playlist.StateChanged += new PlaylistEventHandler(playlist_StateChanged);
- playlist.UpdateInProgress += new PlaylistEventHandler<PlaylistUpdateEventArgs>(playlist_UpdateInProgress);
- playlist.MetadataUpdated += new PlaylistEventHandler(playlist_MetadataUpdated);
- playlist.TrackCreatedChanged += new PlaylistEventHandler<TrackCreatedChangedEventArgs>(playlist_TrackCreatedChanged);
- playlist.TrackSeenChanged += new PlaylistEventHandler<TrackSeenEventArgs>(playlist_TrackSeenChanged);
- playlist.DescriptionChanged += new PlaylistEventHandler<DescriptionEventArgs>(playlist_DescriptionChanged);
- playlist.ImageChanged += new PlaylistEventHandler<ImageEventArgs>(playlist_ImageChanged);
- }
-
- void playlist_ImageChanged(IPlaylist playlist, ImageEventArgs args)
- {
- if (ImageChanged != null)
- ImageChanged(this, args);
- }
-
- void playlist_DescriptionChanged(IPlaylist playlist, DescriptionEventArgs args)
- {
- if (DescriptionChanged != null)
- DescriptionChanged(this, args);
- }
-
- void playlist_TrackSeenChanged(IPlaylist playlist, TrackSeenEventArgs args)
- {
- if (TrackSeenChanged != null)
- TrackSeenChanged(this, args);
- }
-
- void playlist_TrackCreatedChanged(IPlaylist playlist, TrackCreatedChangedEventArgs args)
- {
- if (TrackCreatedChanged != null)
- TrackCreatedChanged(this, args);
- }
-
- void playlist_MetadataUpdated(IPlaylist playlist, EventArgs args)
- {
- if (MetadataUpdated != null)
- MetadataUpdated(this, args);
- }
-
- void playlist_UpdateInProgress(IPlaylist playlist, PlaylistUpdateEventArgs args)
- {
- if (UpdateInProgress != null)
- UpdateInProgress(this, args);
- }
-
- void playlist_StateChanged(IPlaylist playlist, EventArgs args)
- {
- if (StateChanged != null)
- StateChanged(this, args);
- }
-
- void playlist_Renamed(IPlaylist playlist, EventArgs args)
- {
- if (Renamed != null)
- Renamed(this, args);
- }
-
- void playlist_TracksMoved(IPlaylist playlist, TracksMovedEventArgs args)
- {
- if (TracksMoved != null)
- TracksMoved(this, args);
- }
-
- void playlist_TracksRemoved(IPlaylist playlist, TracksEventArgs args)
- {
- if (TracksRemoved != null)
- TracksRemoved(this, args);
- }
-
- void playlist_TracksAdded(IPlaylist playlist, TracksAddedEventArgs args)
- {
- if (TracksAdded != null)
- TracksAdded(this, args);
- }
-
- protected override void OnDispose()
- {
- playlist.TracksAdded -= new PlaylistEventHandler<TracksAddedEventArgs>(playlist_TracksAdded);
- playlist.TracksRemoved -= new PlaylistEventHandler<TracksEventArgs>(playlist_TracksRemoved);
- playlist.TracksMoved -= new PlaylistEventHandler<TracksMovedEventArgs>(playlist_TracksMoved);
- playlist.Renamed -= new PlaylistEventHandler(playlist_Renamed);
- playlist.StateChanged -= new PlaylistEventHandler(playlist_StateChanged);
- playlist.UpdateInProgress -= new PlaylistEventHandler<PlaylistUpdateEventArgs>(playlist_UpdateInProgress);
- playlist.MetadataUpdated -= new PlaylistEventHandler(playlist_MetadataUpdated);
- playlist.TrackCreatedChanged -= new PlaylistEventHandler<TrackCreatedChangedEventArgs>(playlist_TrackCreatedChanged);
- playlist.TrackSeenChanged -= new PlaylistEventHandler<TrackSeenEventArgs>(playlist_TrackSeenChanged);
- playlist.DescriptionChanged -= new PlaylistEventHandler<DescriptionEventArgs>(playlist_DescriptionChanged);
- playlist.ImageChanged -= new PlaylistEventHandler<ImageEventArgs>(playlist_ImageChanged);
- if (this.GetType() == typeof(PlaylistWrapper))
- Playlist.Delete(playlist.playlistPtr);
- playlist = null;
- }
-
- protected override int IntPtrHashCode()
- {
- return IsAlive() ? playlist.IntPtrHashCode() : 0;
- }
-
- public IPlaylistTrackList Tracks
- {
- get { IsAlive(true); return playlist.Tracks; }
- }
-
- public IPlaylistTrack this[int i]
- {
- get { IsAlive(true); return playlist[i]; }
- }
-
- public bool IsLoaded
- {
- get { IsAlive(true); return playlist.IsLoaded; }
- }
-
- public bool IsReady
- {
- get { IsAlive(true); return playlist.IsReady; }
- }
-
- public string Name
- {
- get { IsAlive(true); return playlist.Name; }
- set { IsAlive(true); playlist.Name = value; }
- }
-
- public ISession Session
- {
- get { IsAlive(true); return playlist.Session; }
- }
-
- public event PlaylistEventHandler<TracksAddedEventArgs> TracksAdded;
-
- public event PlaylistEventHandler<TracksEventArgs> TracksRemoved;
-
- public event PlaylistEventHandler<TracksMovedEventArgs> TracksMoved;
-
- public event PlaylistEventHandler Renamed;
-
- public event PlaylistEventHandler StateChanged;
-
- public event PlaylistEventHandler<PlaylistUpdateEventArgs> UpdateInProgress;
-
- public event PlaylistEventHandler MetadataUpdated;
-
- public event PlaylistEventHandler<TrackCreatedChangedEventArgs> TrackCreatedChanged;
-
- public event PlaylistEventHandler<TrackSeenEventArgs> TrackSeenChanged;
-
- public event PlaylistEventHandler<DescriptionEventArgs> DescriptionChanged;
-
- public event PlaylistEventHandler<ImageEventArgs> ImageChanged;
-
-
- public string ImageId
- {
- get { IsAlive(true); return playlist.ImageId; }
- }
-
-
- public bool IsColaberativ
- {
- get { IsAlive(true); return playlist.IsColaberativ; }
- set { IsAlive(true); playlist.IsColaberativ = value; }
- }
-
- public void AutoLinkTracks(bool autoLink)
- {
- IsAlive(true);
- playlist.AutoLinkTracks(autoLink);
- }
-
- public string Description
- {
- get { IsAlive(true); return playlist.Description; }
- }
-
- public bool PendingChanges
- {
- get { IsAlive(true); return playlist.PendingChanges; }
- }
- }
- internal static IntPtr GetPointer(IPlaylist playlist)
- {
- if (playlist.GetType() == typeof(PlaylistWrapper))
- return ((PlaylistWrapper)playlist).playlist.playlistPtr;
- throw new ArgumentException("Invalid playlist");
- }
- #endregion
- #region Counter
- private static Dictionary<IntPtr, Playlist> playlists = new Dictionary<IntPtr, Playlist>();
- private static readonly object playlistsLock = new object();
-
- internal static IPlaylist Get(Session session, IntPtr playlistPtr)
- {
- Playlist playlist;
- lock (playlistsLock)
- {
- if (!playlists.ContainsKey(playlistPtr))
- {
- playlists.Add(playlistPtr, new Playlist(session, playlistPtr));
- }
- playlist = playlists[playlistPtr];
- playlist.AddRef();
- }
- return new PlaylistWrapper(playlist);
- }
-
- internal static void Delete(IntPtr playlistPtr)
- {
- lock (playlistsLock)
- {
- Playlist playlist = playlists[playlistPtr];
- int count = playlist.RemRef();
- if (count == 0)
- playlists.Remove(playlistPtr);
- }
- }
- #endregion
-
- #region Spotify Delegates
- #region Struct
- private struct sp_playlist_callbacks
- {
- internal IntPtr tracks_added;
- internal IntPtr tracks_removed;
- internal IntPtr tracks_moved;
- internal IntPtr playlist_renamed;
- internal IntPtr playlist_state_changed;
- internal IntPtr playlist_update_in_progress;
- internal IntPtr playlist_metadata_updated;
- internal IntPtr track_created_changed;
- internal IntPtr track_seen_changed;
- internal IntPtr description_changed;
- internal IntPtr image_changed;
- internal IntPtr track_message_changed; //TODO: Implement
- internal IntPtr subscribers_changed; // TODO: Implement
- }
- #endregion
- private delegate void tracks_added_cb(IntPtr playlistPtr, IntPtr tracksPtr, int num_tracks, int position, IntPtr userdataPtr);
- private delegate void tracks_removed_cb(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, IntPtr userdataPtr);
- private delegate void tracks_moved_cd(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, int new_position, IntPtr userdataPtr);
- private delegate void playlist_renamed_cb(IntPtr playlistPtr, IntPtr userdataPtr);
- private delegate void playlist_state_changed_cb(IntPtr playlistPtr, IntPtr userdataPtr);
- private delegate void playlist_update_in_progress_cb(IntPtr playlistPtr, bool done, IntPtr userdataPtr);
- private delegate void playlist_metadata_updated_cb(IntPtr playlistPtr, IntPtr userdataPtr);
- private delegate void track_created_changed_cb(IntPtr playlistPtr, int position, IntPtr userPtr, int when, IntPtr userdataPtr);
- private delegate void track_seen_changed_cb(IntPtr playlistPtr, int position, bool seen, IntPtr userdataPtr);
- private delegate void description_changed_cb(IntPtr playlistPtr, IntPtr descPtr, IntPtr userdataPtr);
- private delegate void image_changed_cb(IntPtr playlistPtr, IntPtr imgIdPtr, IntPtr userdataPtr);
- #endregion
-
- #region Spotify Callbacks
- private tracks_added_cb tracks_added;
- private tracks_removed_cb tracks_removed;
- private tracks_moved_cd tracks_moved;
- private playlist_renamed_cb playlist_renamed;
- private playlist_state_changed_cb playlist_state_changed;
- private playlist_update_in_progress_cb playlist_update_in_progress;
- private playlist_metadata_updated_cb playlist_metadata_updated;
- private track_created_changed_cb track_created_changed;
- private track_seen_changed_cb track_seen_changed;
- private description_changed_cb description_changed;
- private image_changed_cb image_changed;
- #endregion
-
- #region Spotify Callback Implementations
- private void TracksAddedCallback(IntPtr playlistPtr, IntPtr tracksPtr, int num_tracks, int position, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- int[] trackIndices = new int[num_tracks];
- ITrack[] tracks = new ITrack[num_tracks];
- IntPtr[] trackPtrs = new IntPtr[num_tracks];
- Marshal.Copy(tracksPtr, trackPtrs, 0, num_tracks);
-
- for (int i = 0; i < num_tracks; i++)
- {
- trackIndices[i] = position + i;
- tracks[i] = Track.Get(session, trackPtrs[i]);
- }
-
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TracksAddedEventArgs>(p => p.OnTracksAdded, this), new TracksAddedEventArgs(trackIndices, tracks)));
- }
- }
-
- private void TracksRemovedCallback(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- int[] trackIndices = new int[num_tracks];
- Marshal.Copy(trackIndicesPtr, trackIndices, 0, num_tracks);
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TracksEventArgs>(p => p.OnTracksRemoved, this), new TracksEventArgs(trackIndices)));
- }
- }
-
- private void TracksMovedCallback(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, int new_position, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- int[] trackIndices = new int[num_tracks];
- Marshal.Copy(trackIndicesPtr, trackIndices, 0, num_tracks);
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TracksMovedEventArgs>(p => p.OnTracksMoved, this), new TracksMovedEventArgs(trackIndices, new_position)));
- }
- }
-
- private void RenamedCallback(IntPtr playlistPtr, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<EventArgs>(p => p.OnRenamed, this), new EventArgs()));
- }
- }
-
- private void StateChangedCallback(IntPtr playlistPtr, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<EventArgs>(p => p.OnStateChanged, this), new EventArgs()));
- }
- }
-
- private void UpdateInProgressCallback(IntPtr playlistPtr, bool complete, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<PlaylistUpdateEventArgs>(p => p.OnUpdateInProgress, this), new PlaylistUpdateEventArgs(complete)));
- }
- }
-
- private void MetadataUpdatedCallback(IntPtr playlistPtr, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<EventArgs>(p => p.OnMetadataUpdated, this), new EventArgs()));
- }
- }
-
- private void TrackCreatedChangedCallback(IntPtr playlistPtr, int position, IntPtr userPtr, int when, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- ITrack track = Track.Get(session, libspotify.sp_playlist_track(playlistPtr, position));
- //IUser user = User.Get(session, userPtr);
- DateTime dtWhen = new DateTime(TimeSpan.FromSeconds(when).Ticks, DateTimeKind.Utc);
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TrackCreatedChangedEventArgs>(p => p.OnTrackCreatedChanged, this), new TrackCreatedChangedEventArgs(track, dtWhen)));
- }
- }
-
- private void TrackSeenChangedCallback(IntPtr playlistPtr, int position, bool seen, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- ITrack track = Track.Get(session, libspotify.sp_playlist_track(playlistPtr, position));
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TrackSeenEventArgs>(p => p.OnTrackSeenChanged, this), new TrackSeenEventArgs(track, seen)));
- }
- }
-
- private void DescriptionChangedCallback(IntPtr playlistPtr, IntPtr descriptionPtr, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- string description = libspotify.GetString(descriptionPtr, String.Empty);
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<DescriptionEventArgs>(p => p.OnDescriptionChanged, this), new DescriptionEventArgs(description)));
- }
- }
-
- private void ImageChangedCallback(IntPtr playlistPtr, IntPtr imageIdPtr, IntPtr userdataPtr)
- {
- if (playlistPtr == this.playlistPtr)
- {
- string imgId = libspotify.ImageIdToString(imageIdPtr);
- session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<ImageEventArgs>(p => p.OnImageChanged, this), new ImageEventArgs(imgId)));
- }
- }
- #endregion
-
- #region Event Functions
- protected virtual void OnTracksAdded(TracksAddedEventArgs args)
- {
- PlaylistTrack.Update(this, aditions: args.TrackIndices);
- if (TracksAdded != null)
- TracksAdded(this, args);
- }
- protected virtual void OnTracksRemoved(TracksEventArgs args)
- {
- PlaylistTrack.Update(this, removals: args.TrackIndices);
- if (TracksRemoved != null)
- TracksRemoved(this, args);
- }
- protected virtual void OnTracksMoved(TracksMovedEventArgs args)
- {
- PlaylistTrack.Update(this, moves: args.TrackIndices, movedTo: args.NewPosition);
- if (TracksMoved != null)
- TracksMoved(this, args);
- }
- protected virtual void OnRenamed(EventArgs args)
- {
- if (Renamed != null)
- Renamed(this, args);
- }
- protected virtual void OnStateChanged(EventArgs args)
- {
- if (StateChanged != null)
- StateChanged(this, args);
- }
- protected virtual void OnUpdateInProgress(PlaylistUpdateEventArgs args)
- {
- if (UpdateInProgress != null)
- UpdateInProgress(this, args);
- }
- protected virtual void OnMetadataUpdated(EventArgs args)
- {
- if (MetadataUpdated != null)
- MetadataUpdated(this, args);
- }
- protected virtual void OnTrackCreatedChanged(TrackCreatedChangedEventArgs args)
- {
- if (TrackCreatedChanged != null)
- TrackCreatedChanged(this, args);
- }
- protected virtual void OnTrackSeenChanged(TrackSeenEventArgs args)
- {
- if (TrackSeenChanged != null)
- TrackSeenChanged(this, args);
- }
- protected virtual void OnDescriptionChanged(DescriptionEventArgs args)
- {
- if (DescriptionChanged != null)
- DescriptionChanged(this, args);
- }
- protected virtual void OnImageChanged(ImageEventArgs args)
- {
- if (ImageChanged != null)
- ImageChanged(this, args);
- }
- #endregion
-
- #region Events
- public event PlaylistEventHandler<TracksAddedEventArgs> TracksAdded;
- public event PlaylistEventHandler<TracksEventArgs> TracksRemoved;
- public event PlaylistEventHandler<TracksMovedEventArgs> TracksMoved;
- public event PlaylistEventHandler Renamed;
- public event PlaylistEventHandler StateChanged;
- public event PlaylistEventHandler<PlaylistUpdateEventArgs> UpdateInProgress;
- public event PlaylistEventHandler MetadataUpdated;
- public event PlaylistEventHandler<TrackCreatedChangedEventArgs> TrackCreatedChanged;
- public event PlaylistEventHandler<TrackSeenEventArgs> TrackSeenChanged;
- public event PlaylistEventHandler<DescriptionEventArgs> DescriptionChanged;
- public event PlaylistEventHandler<ImageEventArgs> ImageChanged;
- #endregion
-
- #region Declarations
- internal IntPtr playlistPtr = IntPtr.Zero;
- private Session session;
- private IntPtr callbacksPtr = IntPtr.Zero;
- private bool registerCallbacks;
-
- private IPlaylistTrackList tracks;
- #endregion
-
- #region Constructor
- protected Playlist(Session session, IntPtr playlistPtr, bool registerCallbacks = true)
- {
- if (playlistPtr == IntPtr.Zero)
- throw new ArgumentException("playlistPtr can't be zero.");
-
- if (session == null)
- throw new ArgumentNullException("Session can't be null.");
-
- this.session = session;
- this.playlistPtr = playlistPtr;
- this.registerCallbacks = registerCallbacks;
-
- if (registerCallbacks)
- {
- tracks_added = new tracks_added_cb(TracksAddedCallback);
- tracks_removed = new tracks_removed_cb(TracksRemovedCallback);
- tracks_moved = new tracks_moved_cd(TracksMovedCallback);
- playlist_renamed = new playlist_renamed_cb(RenamedCallback);
- playlist_state_changed = new playlist_state_changed_cb(StateChangedCallback);
- playlist_update_in_progress = new playlist_update_in_progress_cb(UpdateInProgressCallback);
- playlist_metadata_updated = new playlist_metadata_updated_cb(MetadataUpdatedCallback);
- track_created_changed = new track_created_changed_cb(TrackCreatedChangedCallback);
- track_seen_changed = new track_seen_changed_cb(TrackSeenChangedCallback);
- description_changed = new description_changed_cb(DescriptionChangedCallback);
- image_changed = new image_changed_cb(ImageChangedCallback);
- sp_playlist_callbacks callbacks = new sp_playlist_callbacks
- {
- tracks_added = Marshal.GetFunctionPointerForDelegate(tracks_added),
- tracks_removed = Marshal.GetFunctionPointerForDelegate(tracks_removed),
- tracks_moved = Marshal.GetFunctionPointerForDelegate(tracks_moved),
- playlist_renamed = Marshal.GetFunctionPointerForDelegate(playlist_renamed),
- playlist_state_changed = Marshal.GetFunctionPointerForDelegate(playlist_state_changed),
- playlist_update_in_progress = Marshal.GetFunctionPointerForDelegate(playlist_update_in_progress),
- playlist_metadata_updated = Marshal.GetFunctionPointerForDelegate(playlist_metadata_updated),
- track_created_changed = Marshal.GetFunctionPointerForDelegate(track_created_changed),
- track_seen_changed = Marshal.GetFunctionPointerForDelegate(track_seen_changed),
- description_changed = Marshal.GetFunctionPointerForDelegate(description_changed),
- image_changed = Marshal.GetFunctionPointerForDelegate(image_changed)
- };
-
- callbacksPtr = Marshal.AllocHGlobal(Marshal.SizeOf(callbacks));
- Marshal.StructureToPtr(callbacks, callbacksPtr, true);
- }
-
- lock (libspotify.Mutex)
- {
- libspotify.sp_playlist_add_ref(playlistPtr);
- if (registerCallbacks)
- libspotify.sp_playlist_add_callbacks(playlistPtr, callbacksPtr, IntPtr.Zero);
- }
-
- session.DisposeAll += new SessionEventHandler(session_DisposeAll);
-
- tracks = new PlaylistTrackList(
- () =>
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_playlist_num_tracks(playlistPtr);
- },
- (index) =>
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return PlaylistTrack.Get(session, this, libspotify.sp_playlist_track(playlistPtr, index), index);
- },
- (track, index) =>
- {
- IsAlive(true);
- IntPtr trackArrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
- IntPtr[] ptrArray = new[] { Track.GetPointer(track) };
-
- try
- {
- Marshal.Copy(ptrArray, 0, trackArrayPtr, 1);
-
- lock (libspotify.Mutex)
- libspotify.sp_playlist_add_tracks(playlistPtr, trackArrayPtr, 1, index, session.sessionPtr);
- }
- finally
- {
- try { Marshal.FreeHGlobal(trackArrayPtr); }
- catch
- { }
- }
-
-
- },
- (index) =>
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- libspotify.sp_playlist_remove_tracks(playlistPtr, new int[] { index }, 1);
- },
- () => false
- );
- PlaylistTrack.RegisterPlaylist(this);
- }
-
- void session_DisposeAll(ISession sender, SessionEventArgs e)
- {
- Dispose();
- }
- #endregion
-
- #region Private Methods
- private static Delegate CreateDelegate<T>(Expression<Func<Playlist, Action<T>>> expr, Playlist p) where T : EventArgs
- {
- return expr.Compile().Invoke(p);
- }
- #endregion
-
- #region Internal Track Methods
- internal DateTime GetTrackCreationTime(PlaylistTrack track)
- {
- lock (libspotify.Mutex)
- return new DateTime(TimeSpan.FromSeconds(libspotify.sp_playlist_track_create_time(playlistPtr, track.position)).Ticks);
- }
-
- internal bool GetTrackSeen(PlaylistTrack track)
- {
- lock (libspotify.Mutex)
- return libspotify.sp_playlist_track_seen(playlistPtr, track.position);
- }
- #endregion
-
- #region Public Functions
- public void AutoLinkTracks(bool autoLink)
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- libspotify.sp_playlist_set_autolink_tracks(playlistPtr, autoLink);
- }
- #endregion
-
- #region Properties
- public bool IsLoaded
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_playlist_is_loaded(playlistPtr);
- }
- }
-
- public virtual string Name
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.GetString(libspotify.sp_playlist_name(playlistPtr), String.Empty);
- }
- set
- {
- if (value.Length > 255)
- throw new ArgumentException("value can't be longer than 255 chars.");
- IsAlive(true);
- lock (libspotify.Mutex)
- libspotify.sp_playlist_rename(playlistPtr, value);
- }
- }
-
- public IPlaylistTrackList Tracks
- {
- get
- {
- IsAlive(true);
- return tracks;
- }
- }
-
- public IPlaylistTrack this[int i]
- {
- get
- {
- IsAlive(true);
- return tracks[i];
- }
- }
-
- public string ImageId
- {
- get
- {
- IsAlive(true);
- IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)) * 20);
- bool ans = false;
- lock (libspotify.Mutex)
- ans = libspotify.sp_playlist_get_image(playlistPtr, ptr);
- try
- {
- if (ans)
- {
- return libspotify.ImageIdToString(ptr);
- }
- else
- {
- return null;
- }
- }
- finally
- {
- Marshal.FreeHGlobal(ptr);
- }
- }
- }
-
- public bool IsColaberativ
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_playlist_is_collaborative(playlistPtr);
- }
- set
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- libspotify.sp_playlist_set_collaborative(playlistPtr, value);
- }
- }
-
- public string Description
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.GetString(libspotify.sp_playlist_get_description(playlistPtr), String.Empty);
- }
- }
-
- public bool PendingChanges
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_playlist_has_pending_changes(playlistPtr);
- }
- }
-
- public ISession Session
- {
- get { IsAlive(true); return session; }
- }
- #endregion
-
- #region Cleanup
- protected override void OnDispose()
- {
- session.DisposeAll -= new SessionEventHandler(session_DisposeAll);
- PlaylistTrack.UnregisterPlaylist(this);
- if (!session.ProcExit)
- lock (libspotify.Mutex)
- {
- if (registerCallbacks)
- {
- try { libspotify.sp_playlist_remove_callbacks(playlistPtr, callbacksPtr, IntPtr.Zero); }
- finally { Marshal.FreeHGlobal(callbacksPtr); }
- }
- libspotify.sp_playlist_release(playlistPtr);
- }
-
- playlistPtr = IntPtr.Zero;
- callbacksPtr = IntPtr.Zero;
- }
- #endregion
-
- #region Await
- public bool IsReady
- {
- get { return !string.IsNullOrEmpty(Name); }
- }
- #endregion
-
- protected override int IntPtrHashCode()
- {
- return playlistPtr.GetHashCode();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Runtime.InteropServices;
+
+namespace SpotiFire.SpotifyLib
+{
+ public delegate void PlaylistEventHandler(IPlaylist playlist, EventArgs args);
+ public delegate void PlaylistEventHandler<TEventArgs>(IPlaylist playlist, TEventArgs args) where TEventArgs : EventArgs;
+
+
+
+ internal class Playlist : CountedDisposeableSpotifyObject, IPlaylist
+ {
+ #region Wrapper
+ internal protected class PlaylistWrapper : DisposeableSpotifyObject, IPlaylist
+ {
+ internal Playlist playlist;
+ public PlaylistWrapper(Playlist playlist)
+ {
+ this.playlist = playlist;
+ playlist.TracksAdded += new PlaylistEventHandler<TracksAddedEventArgs>(playlist_TracksAdded);
+ playlist.TracksRemoved += new PlaylistEventHandler<TracksEventArgs>(playlist_TracksRemoved);
+ playlist.TracksMoved += new PlaylistEventHandler<TracksMovedEventArgs>(playlist_TracksMoved);
+ playlist.Renamed += new PlaylistEventHandler(playlist_Renamed);
+ playlist.StateChanged += new PlaylistEventHandler(playlist_StateChanged);
+ playlist.UpdateInProgress += new PlaylistEventHandler<PlaylistUpdateEventArgs>(playlist_UpdateInProgress);
+ playlist.MetadataUpdated += new PlaylistEventHandler(playlist_MetadataUpdated);
+ playlist.TrackCreatedChanged += new PlaylistEventHandler<TrackCreatedChangedEventArgs>(playlist_TrackCreatedChanged);
+ playlist.TrackSeenChanged += new PlaylistEventHandler<TrackSeenEventArgs>(playlist_TrackSeenChanged);
+ playlist.DescriptionChanged += new PlaylistEventHandler<DescriptionEventArgs>(playlist_DescriptionChanged);
+ playlist.ImageChanged += new PlaylistEventHandler<ImageEventArgs>(playlist_ImageChanged);
+ }
+
+ void playlist_ImageChanged(IPlaylist playlist, ImageEventArgs args)
+ {
+ if (ImageChanged != null)
+ ImageChanged(this, args);
+ }
+
+ void playlist_DescriptionChanged(IPlaylist playlist, DescriptionEventArgs args)
+ {
+ if (DescriptionChanged != null)
+ DescriptionChanged(this, args);
+ }
+
+ void playlist_TrackSeenChanged(IPlaylist playlist, TrackSeenEventArgs args)
+ {
+ if (TrackSeenChanged != null)
+ TrackSeenChanged(this, args);
+ }
+
+ void playlist_TrackCreatedChanged(IPlaylist playlist, TrackCreatedChangedEventArgs args)
+ {
+ if (TrackCreatedChanged != null)
+ TrackCreatedChanged(this, args);
+ }
+
+ void playlist_MetadataUpdated(IPlaylist playlist, EventArgs args)
+ {
+ if (MetadataUpdated != null)
+ MetadataUpdated(this, args);
+ }
+
+ void playlist_UpdateInProgress(IPlaylist playlist, PlaylistUpdateEventArgs args)
+ {
+ if (UpdateInProgress != null)
+ UpdateInProgress(this, args);
+ }
+
+ void playlist_StateChanged(IPlaylist playlist, EventArgs args)
+ {
+ if (StateChanged != null)
+ StateChanged(this, args);
+ }
+
+ void playlist_Renamed(IPlaylist playlist, EventArgs args)
+ {
+ if (Renamed != null)
+ Renamed(this, args);
+ }
+
+ void playlist_TracksMoved(IPlaylist playlist, TracksMovedEventArgs args)
+ {
+ if (TracksMoved != null)
+ TracksMoved(this, args);
+ }
+
+ void playlist_TracksRemoved(IPlaylist playlist, TracksEventArgs args)
+ {
+ if (TracksRemoved != null)
+ TracksRemoved(this, args);
+ }
+
+ void playlist_TracksAdded(IPlaylist playlist, TracksAddedEventArgs args)
+ {
+ if (TracksAdded != null)
+ TracksAdded(this, args);
+ }
+
+ protected override void OnDispose()
+ {
+ playlist.TracksAdded -= new PlaylistEventHandler<TracksAddedEventArgs>(playlist_TracksAdded);
+ playlist.TracksRemoved -= new PlaylistEventHandler<TracksEventArgs>(playlist_TracksRemoved);
+ playlist.TracksMoved -= new PlaylistEventHandler<TracksMovedEventArgs>(playlist_TracksMoved);
+ playlist.Renamed -= new PlaylistEventHandler(playlist_Renamed);
+ playlist.StateChanged -= new PlaylistEventHandler(playlist_StateChanged);
+ playlist.UpdateInProgress -= new PlaylistEventHandler<PlaylistUpdateEventArgs>(playlist_UpdateInProgress);
+ playlist.MetadataUpdated -= new PlaylistEventHandler(playlist_MetadataUpdated);
+ playlist.TrackCreatedChanged -= new PlaylistEventHandler<TrackCreatedChangedEventArgs>(playlist_TrackCreatedChanged);
+ playlist.TrackSeenChanged -= new PlaylistEventHandler<TrackSeenEventArgs>(playlist_TrackSeenChanged);
+ playlist.DescriptionChanged -= new PlaylistEventHandler<DescriptionEventArgs>(playlist_DescriptionChanged);
+ playlist.ImageChanged -= new PlaylistEventHandler<ImageEventArgs>(playlist_ImageChanged);
+ if (this.GetType() == typeof(PlaylistWrapper))
+ Playlist.Delete(playlist.playlistPtr);
+ playlist = null;
+ }
+
+ protected override int IntPtrHashCode()
+ {
+ return IsAlive() ? playlist.IntPtrHashCode() : 0;
+ }
+
+ public IPlaylistTrackList Tracks
+ {
+ get { IsAlive(true); return playlist.Tracks; }
+ }
+
+ public IPlaylistTrack this[int i]
+ {
+ get { IsAlive(true); return playlist[i]; }
+ }
+
+ public bool IsLoaded
+ {
+ get { IsAlive(true); return playlist.IsLoaded; }
+ }
+
+ public bool IsReady
+ {
+ get { IsAlive(true); return playlist.IsReady; }
+ }
+
+ public string Name
+ {
+ get { IsAlive(true); return playlist.Name; }
+ set { IsAlive(true); playlist.Name = value; }
+ }
+
+ public ISession Session
+ {
+ get { IsAlive(true); return playlist.Session; }
+ }
+
+ public event PlaylistEventHandler<TracksAddedEventArgs> TracksAdded;
+
+ public event PlaylistEventHandler<TracksEventArgs> TracksRemoved;
+
+ public event PlaylistEventHandler<TracksMovedEventArgs> TracksMoved;
+
+ public event PlaylistEventHandler Renamed;
+
+ public event PlaylistEventHandler StateChanged;
+
+ public event PlaylistEventHandler<PlaylistUpdateEventArgs> UpdateInProgress;
+
+ public event PlaylistEventHandler MetadataUpdated;
+
+ public event PlaylistEventHandler<TrackCreatedChangedEventArgs> TrackCreatedChanged;
+
+ public event PlaylistEventHandler<TrackSeenEventArgs> TrackSeenChanged;
+
+ public event PlaylistEventHandler<DescriptionEventArgs> DescriptionChanged;
+
+ public event PlaylistEventHandler<ImageEventArgs> ImageChanged;
+
+
+ public string ImageId
+ {
+ get { IsAlive(true); return playlist.ImageId; }
+ }
+
+
+ public bool IsColaberativ
+ {
+ get { IsAlive(true); return playlist.IsColaberativ; }
+ set { IsAlive(true); playlist.IsColaberativ = value; }
+ }
+
+ public void AutoLinkTracks(bool autoLink)
+ {
+ IsAlive(true);
+ playlist.AutoLinkTracks(autoLink);
+ }
+
+ public string Description
+ {
+ get { IsAlive(true); return playlist.Description; }
+ }
+
+ public bool PendingChanges
+ {
+ get { IsAlive(true); return playlist.PendingChanges; }
+ }
+ }
+ internal static IntPtr GetPointer(IPlaylist playlist)
+ {
+ if (playlist.GetType() == typeof(PlaylistWrapper))
+ return ((PlaylistWrapper)playlist).playlist.playlistPtr;
+ throw new ArgumentException("Invalid playlist");
+ }
+ #endregion
+ #region Counter
+ private static Dictionary<IntPtr, Playlist> playlists = new Dictionary<IntPtr, Playlist>();
+ private static readonly object playlistsLock = new object();
+
+ internal static IPlaylist Get(Session session, IntPtr playlistPtr)
+ {
+ Playlist playlist;
+ lock (playlistsLock)
+ {
+ if (!playlists.ContainsKey(playlistPtr))
+ {
+ playlists.Add(playlistPtr, new Playlist(session, playlistPtr));
+ }
+ playlist = playlists[playlistPtr];
+ playlist.AddRef();
+ }
+ return new PlaylistWrapper(playlist);
+ }
+
+ internal static void Delete(IntPtr playlistPtr)
+ {
+ lock (playlistsLock)
+ {
+ Playlist playlist = playlists[playlistPtr];
+ int count = playlist.RemRef();
+ if (count == 0)
+ playlists.Remove(playlistPtr);
+ }
+ }
+ #endregion
+
+ #region Spotify Delegates
+ #region Struct
+ private struct sp_playlist_callbacks
+ {
+ internal IntPtr tracks_added;
+ internal IntPtr tracks_removed;
+ internal IntPtr tracks_moved;
+ internal IntPtr playlist_renamed;
+ internal IntPtr playlist_state_changed;
+ internal IntPtr playlist_update_in_progress;
+ internal IntPtr playlist_metadata_updated;
+ internal IntPtr track_created_changed;
+ internal IntPtr track_seen_changed;
+ internal IntPtr description_changed;
+ internal IntPtr image_changed;
+ internal IntPtr track_message_changed; //TODO: Implement
+ internal IntPtr subscribers_changed; // TODO: Implement
+ }
+ #endregion
+ private delegate void tracks_added_cb(IntPtr playlistPtr, IntPtr tracksPtr, int num_tracks, int position, IntPtr userdataPtr);
+ private delegate void tracks_removed_cb(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, IntPtr userdataPtr);
+ private delegate void tracks_moved_cd(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, int new_position, IntPtr userdataPtr);
+ private delegate void playlist_renamed_cb(IntPtr playlistPtr, IntPtr userdataPtr);
+ private delegate void playlist_state_changed_cb(IntPtr playlistPtr, IntPtr userdataPtr);
+ private delegate void playlist_update_in_progress_cb(IntPtr playlistPtr, bool done, IntPtr userdataPtr);
+ private delegate void playlist_metadata_updated_cb(IntPtr playlistPtr, IntPtr userdataPtr);
+ private delegate void track_created_changed_cb(IntPtr playlistPtr, int position, IntPtr userPtr, int when, IntPtr userdataPtr);
+ private delegate void track_seen_changed_cb(IntPtr playlistPtr, int position, bool seen, IntPtr userdataPtr);
+ private delegate void description_changed_cb(IntPtr playlistPtr, IntPtr descPtr, IntPtr userdataPtr);
+ private delegate void image_changed_cb(IntPtr playlistPtr, IntPtr imgIdPtr, IntPtr userdataPtr);
+ #endregion
+
+ #region Spotify Callbacks
+ private tracks_added_cb tracks_added;
+ private tracks_removed_cb tracks_removed;
+ private tracks_moved_cd tracks_moved;
+ private playlist_renamed_cb playlist_renamed;
+ private playlist_state_changed_cb playlist_state_changed;
+ private playlist_update_in_progress_cb playlist_update_in_progress;
+ private playlist_metadata_updated_cb playlist_metadata_updated;
+ private track_created_changed_cb track_created_changed;
+ private track_seen_changed_cb track_seen_changed;
+ private description_changed_cb description_changed;
+ private image_changed_cb image_changed;
+ #endregion
+
+ #region Spotify Callback Implementations
+ private void TracksAddedCallback(IntPtr playlistPtr, IntPtr tracksPtr, int num_tracks, int position, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ int[] trackIndices = new int[num_tracks];
+ ITrack[] tracks = new ITrack[num_tracks];
+ IntPtr[] trackPtrs = new IntPtr[num_tracks];
+ Marshal.Copy(tracksPtr, trackPtrs, 0, num_tracks);
+
+ for (int i = 0; i < num_tracks; i++)
+ {
+ trackIndices[i] = position + i;
+ tracks[i] = Track.Get(session, trackPtrs[i]);
+ }
+
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TracksAddedEventArgs>(p => p.OnTracksAdded, this), new TracksAddedEventArgs(trackIndices, tracks)));
+ }
+ }
+
+ private void TracksRemovedCallback(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ int[] trackIndices = new int[num_tracks];
+ Marshal.Copy(trackIndicesPtr, trackIndices, 0, num_tracks);
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TracksEventArgs>(p => p.OnTracksRemoved, this), new TracksEventArgs(trackIndices)));
+ }
+ }
+
+ private void TracksMovedCallback(IntPtr playlistPtr, IntPtr trackIndicesPtr, int num_tracks, int new_position, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ int[] trackIndices = new int[num_tracks];
+ Marshal.Copy(trackIndicesPtr, trackIndices, 0, num_tracks);
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TracksMovedEventArgs>(p => p.OnTracksMoved, this), new TracksMovedEventArgs(trackIndices, new_position)));
+ }
+ }
+
+ private void RenamedCallback(IntPtr playlistPtr, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<EventArgs>(p => p.OnRenamed, this), new EventArgs()));
+ }
+ }
+
+ private void StateChangedCallback(IntPtr playlistPtr, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<EventArgs>(p => p.OnStateChanged, this), new EventArgs()));
+ }
+ }
+
+ private void UpdateInProgressCallback(IntPtr playlistPtr, bool complete, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<PlaylistUpdateEventArgs>(p => p.OnUpdateInProgress, this), new PlaylistUpdateEventArgs(complete)));
+ }
+ }
+
+ private void MetadataUpdatedCallback(IntPtr playlistPtr, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<EventArgs>(p => p.OnMetadataUpdated, this), new EventArgs()));
+ }
+ }
+
+ private void TrackCreatedChangedCallback(IntPtr playlistPtr, int position, IntPtr userPtr, int when, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ ITrack track = Track.Get(session, libspotify.sp_playlist_track(playlistPtr, position));
+ //IUser user = User.Get(session, userPtr);
+ DateTime dtWhen = new DateTime(TimeSpan.FromSeconds(when).Ticks, DateTimeKind.Utc);
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TrackCreatedChangedEventArgs>(p => p.OnTrackCreatedChanged, this), new TrackCreatedChangedEventArgs(track, dtWhen)));
+ }
+ }
+
+ private void TrackSeenChangedCallback(IntPtr playlistPtr, int position, bool seen, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ ITrack track = Track.Get(session, libspotify.sp_playlist_track(playlistPtr, position));
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<TrackSeenEventArgs>(p => p.OnTrackSeenChanged, this), new TrackSeenEventArgs(track, seen)));
+ }
+ }
+
+ private void DescriptionChangedCallback(IntPtr playlistPtr, IntPtr descriptionPtr, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ string description = libspotify.GetString(descriptionPtr, String.Empty);
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<DescriptionEventArgs>(p => p.OnDescriptionChanged, this), new DescriptionEventArgs(description)));
+ }
+ }
+
+ private void ImageChangedCallback(IntPtr playlistPtr, IntPtr imageIdPtr, IntPtr userdataPtr)
+ {
+ if (playlistPtr == this.playlistPtr)
+ {
+ string imgId = libspotify.ImageIdToString(imageIdPtr);
+ session.EnqueueEventWorkItem(new EventWorkItem(CreateDelegate<ImageEventArgs>(p => p.OnImageChanged, this), new ImageEventArgs(imgId)));
+ }
+ }
+ #endregion
+
+ #region Event Functions
+ protected virtual void OnTracksAdded(TracksAddedEventArgs args)
+ {
+ PlaylistTrack.Update(this, aditions: args.TrackIndices);
+ if (TracksAdded != null)
+ TracksAdded(this, args);
+ }
+ protected virtual void OnTracksRemoved(TracksEventArgs args)
+ {
+ PlaylistTrack.Update(this, removals: args.TrackIndices);
+ if (TracksRemoved != null)
+ TracksRemoved(this, args);
+ }
+ protected virtual void OnTracksMoved(TracksMovedEventArgs args)
+ {
+ PlaylistTrack.Update(this, moves: args.TrackIndices, movedTo: args.NewPosition);
+ if (TracksMoved != null)
+ TracksMoved(this, args);
+ }
+ protected virtual void OnRenamed(EventArgs args)
+ {
+ if (Renamed != null)
+ Renamed(this, args);
+ }
+ protected virtual void OnStateChanged(EventArgs args)
+ {
+ if (StateChanged != null)
+ StateChanged(this, args);
+ }
+ protected virtual void OnUpdateInProgress(PlaylistUpdateEventArgs args)
+ {
+ if (UpdateInProgress != null)
+ UpdateInProgress(this, args);
+ }
+ protected virtual void OnMetadataUpdated(EventArgs args)
+ {
+ if (MetadataUpdated != null)
+ MetadataUpdated(this, args);
+ }
+ protected virtual void OnTrackCreatedChanged(TrackCreatedChangedEventArgs args)
+ {
+ if (TrackCreatedChanged != null)
+ TrackCreatedChanged(this, args);
+ }
+ protected virtual void OnTrackSeenChanged(TrackSeenEventArgs args)
+ {
+ if (TrackSeenChanged != null)
+ TrackSeenChanged(this, args);
+ }
+ protected virtual void OnDescriptionChanged(DescriptionEventArgs args)
+ {
+ if (DescriptionChanged != null)
+ DescriptionChanged(this, args);
+ }
+ protected virtual void OnImageChanged(ImageEventArgs args)
+ {
+ if (ImageChanged != null)
+ ImageChanged(this, args);
+ }
+ #endregion
+
+ #region Events
+ public event PlaylistEventHandler<TracksAddedEventArgs> TracksAdded;
+ public event PlaylistEventHandler<TracksEventArgs> TracksRemoved;
+ public event PlaylistEventHandler<TracksMovedEventArgs> TracksMoved;
+ public event PlaylistEventHandler Renamed;
+ public event PlaylistEventHandler StateChanged;
+ public event PlaylistEventHandler<PlaylistUpdateEventArgs> UpdateInProgress;
+ public event PlaylistEventHandler MetadataUpdated;
+ public event PlaylistEventHandler<TrackCreatedChangedEventArgs> TrackCreatedChanged;
+ public event PlaylistEventHandler<TrackSeenEventArgs> TrackSeenChanged;
+ public event PlaylistEventHandler<DescriptionEventArgs> DescriptionChanged;
+ public event PlaylistEventHandler<ImageEventArgs> ImageChanged;
+ #endregion
+
+ #region Declarations
+ internal IntPtr playlistPtr = IntPtr.Zero;
+ private Session session;
+ private IntPtr callbacksPtr = IntPtr.Zero;
+ private bool registerCallbacks;
+
+ private IPlaylistTrackList tracks;
+ #endregion
+
+ #region Constructor
+ protected Playlist(Session session, IntPtr playlistPtr, bool registerCallbacks = true)
+ {
+ if (playlistPtr == IntPtr.Zero)
+ throw new ArgumentException("playlistPtr can't be zero.");
+
+ if (session == null)
+ throw new ArgumentNullException("Session can't be null.");
+
+ this.session = session;
+ this.playlistPtr = playlistPtr;
+ this.registerCallbacks = registerCallbacks;
+
+ if (registerCallbacks)
+ {
+ tracks_added = new tracks_added_cb(TracksAddedCallback);
+ tracks_removed = new tracks_removed_cb(TracksRemovedCallback);
+ tracks_moved = new tracks_moved_cd(TracksMovedCallback);
+ playlist_renamed = new playlist_renamed_cb(RenamedCallback);
+ playlist_state_changed = new playlist_state_changed_cb(StateChangedCallback);
+ playlist_update_in_progress = new playlist_update_in_progress_cb(UpdateInProgressCallback);
+ playlist_metadata_updated = new playlist_metadata_updated_cb(MetadataUpdatedCallback);
+ track_created_changed = new track_created_changed_cb(TrackCreatedChangedCallback);
+ track_seen_changed = new track_seen_changed_cb(TrackSeenChangedCallback);
+ description_changed = new description_changed_cb(DescriptionChangedCallback);
+ image_changed = new image_changed_cb(ImageChangedCallback);
+ sp_playlist_callbacks callbacks = new sp_playlist_callbacks
+ {
+ tracks_added = Marshal.GetFunctionPointerForDelegate(tracks_added),
+ tracks_removed = Marshal.GetFunctionPointerForDelegate(tracks_removed),
+ tracks_moved = Marshal.GetFunctionPointerForDelegate(tracks_moved),
+ playlist_renamed = Marshal.GetFunctionPointerForDelegate(playlist_renamed),
+ playlist_state_changed = Marshal.GetFunctionPointerForDelegate(playlist_state_changed),
+ playlist_update_in_progress = Marshal.GetFunctionPointerForDelegate(playlist_update_in_progress),
+ playlist_metadata_updated = Marshal.GetFunctionPointerForDelegate(playlist_metadata_updated),
+ track_created_changed = Marshal.GetFunctionPointerForDelegate(track_created_changed),
+ track_seen_changed = Marshal.GetFunctionPointerForDelegate(track_seen_changed),
+ description_changed = Marshal.GetFunctionPointerForDelegate(description_changed),
+ image_changed = Marshal.GetFunctionPointerForDelegate(image_changed)
+ };
+
+ callbacksPtr = Marshal.AllocHGlobal(Marshal.SizeOf(callbacks));
+ Marshal.StructureToPtr(callbacks, callbacksPtr, true);
+ }
+
+ lock (libspotify.Mutex)
+ {
+ libspotify.sp_playlist_add_ref(playlistPtr);
+ if (registerCallbacks)
+ libspotify.sp_playlist_add_callbacks(playlistPtr, callbacksPtr, IntPtr.Zero);
+ }
+
+ session.DisposeAll += new SessionEventHandler(session_DisposeAll);
+
+ tracks = new PlaylistTrackList(
+ () =>
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return libspotify.sp_playlist_num_tracks(playlistPtr);
+ },
+ (index) =>
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return PlaylistTrack.Get(session, this, libspotify.sp_playlist_track(playlistPtr, index), index);
+ },
+ (track, index) =>
+ {
+ IsAlive(true);
+ IntPtr trackArrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
+ IntPtr[] ptrArray = new[] { Track.GetPointer(track) };
+
+ try
+ {
+ Marshal.Copy(ptrArray, 0, trackArrayPtr, 1);
+
+ lock (libspotify.Mutex)
+ libspotify.sp_playlist_add_tracks(playlistPtr, trackArrayPtr, 1, index, session.sessionPtr);
+ }
+ finally
+ {
+ try { Marshal.FreeHGlobal(trackArrayPtr); }
+ catch
+ { }
+ }
+
+
+ },
+ (index) =>
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ libspotify.sp_playlist_remove_tracks(playlistPtr, new int[] { index }, 1);
+ },
+ () => false
+ );
+ PlaylistTrack.RegisterPlaylist(this);
+ }
+
+ void session_DisposeAll(ISession sender, SessionEventArgs e)
+ {
+ Dispose();
+ }
+ #endregion
+
+ #region Private Methods
+ private static Delegate CreateDelegate<T>(Expression<Func<Playlist, Action<T>>> expr, Playlist p) where T : EventArgs
+ {
+ return expr.Compile().Invoke(p);
+ }
+ #endregion
+
+ #region Internal Track Methods
+ internal DateTime GetTrackCreationTime(PlaylistTrack track)
+ {
+ lock (libspotify.Mutex)
+ return new DateTime(TimeSpan.FromSeconds(libspotify.sp_playlist_track_create_time(playlistPtr, track.position)).Ticks);
+ }
+
+ internal bool GetTrackSeen(PlaylistTrack track)
+ {
+ lock (libspotify.Mutex)
+ return libspotify.sp_playlist_track_seen(playlistPtr, track.position);
+ }
+ #endregion
+
+ #region Public Functions
+ public void AutoLinkTracks(bool autoLink)
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ libspotify.sp_playlist_set_autolink_tracks(playlistPtr, autoLink);
+ }
+ #endregion
+
+ #region Properties
+ public bool IsLoaded
+ {
+ get
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return libspotify.sp_playlist_is_loaded(playlistPtr);
+ }
+ }
+
+ public virtual string Name
+ {
+ get
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return libspotify.GetString(libspotify.sp_playlist_name(playlistPtr), String.Empty);
+ }
+ set
+ {
+ if (value.Length > 255)
+ throw new ArgumentException("value can't be longer than 255 chars.");
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ libspotify.sp_playlist_rename(playlistPtr, value);
+ }
+ }
+
+ public IPlaylistTrackList Tracks
+ {
+ get
+ {
+ IsAlive(true);
+ return tracks;
+ }
+ }
+
+ public IPlaylistTrack this[int i]
+ {
+ get
+ {
+ IsAlive(true);
+ return tracks[i];
+ }
+ }
+
+ public string ImageId
+ {
+ get
+ {
+ IsAlive(true);
+ IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)) * 20);
+ bool ans = false;
+ lock (libspotify.Mutex)
+ ans = libspotify.sp_playlist_get_image(playlistPtr, ptr);
+ try
+ {
+ if (ans)
+ {
+ return libspotify.ImageIdToString(ptr);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+ }
+
+ public bool IsColaberativ
+ {
+ get
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return libspotify.sp_playlist_is_collaborative(playlistPtr);
+ }
+ set
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ libspotify.sp_playlist_set_collaborative(playlistPtr, value);
+ }
+ }
+
+ public string Description
+ {
+ get
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return libspotify.GetString(libspotify.sp_playlist_get_description(playlistPtr), String.Empty);
+ }
+ }
+
+ public bool PendingChanges
+ {
+ get
+ {
+ IsAlive(true);
+ lock (libspotify.Mutex)
+ return libspotify.sp_playlist_has_pending_changes(playlistPtr);
+ }
+ }
+
+ public ISession Session
+ {
+ get { IsAlive(true); return session; }
+ }
+ #endregion
+
+ #region Cleanup
+ protected override void OnDispose()
+ {
+ session.DisposeAll -= new SessionEventHandler(session_DisposeAll);
+ PlaylistTrack.UnregisterPlaylist(this);
+ if (!session.ProcExit)
+ lock (libspotify.Mutex)
+ {
+ if (registerCallbacks)
+ {
+ try { libspotify.sp_playlist_remove_callbacks(playlistPtr, callbacksPtr, IntPtr.Zero); }
+ finally { Marshal.FreeHGlobal(callbacksPtr); }
+ }
+ libspotify.sp_playlist_release(playlistPtr);
+ }
+
+ playlistPtr = IntPtr.Zero;
+ callbacksPtr = IntPtr.Zero;
+ }
+ #endregion
+
+ #region Await
+ public bool IsReady
+ {
+ get { return !string.IsNullOrEmpty(Name) || !string.IsNullOrEmpty(ImageId); }
+ }
+ #endregion
+
+ protected override int IntPtrHashCode()
+ {
+ return playlistPtr.GetHashCode();
+ }
+ }
+}
693 SpotiFire.SpotifyLib/SpotifyTypes/Track.cs
View
@@ -1,350 +1,343 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace SpotiFire.SpotifyLib
-{
- internal class Track : CountedDisposeableSpotifyObject, ITrack
- {
- #region Wrapper
- protected class TrackWrapper : DisposeableSpotifyObject, ITrack
- {
- internal Track track;
- internal TrackWrapper(Track track)
- {
- this.track = track;
- }
-
- protected override void OnDispose()
- {
- Track.Delete(track.trackPtr);
- track = null;
- }
-
- public IAlbum Album
- {
- get { IsAlive(true); return track.Album; }
- }
-
- public IArray<IArtist> Artists
- {
- get { IsAlive(true); return track.Artists; }
- }
-
- public int Disc
- {
- get { IsAlive(true); return track.Disc; }
- }
-
- public TimeSpan Duration
- {
- get { IsAlive(true); return track.Duration; }
- }
-
- public sp_error Error
- {
- get { IsAlive(true); return track.Error; }
- }
-
- public int Index
- {
- get { IsAlive(true); return track.Index; }
- }
-
- public bool IsAvailable
- {
- get { IsAlive(true); return track.IsAvailable; }
- }
-
- public bool IsLoaded
- {
- get { IsAlive(true); return track.IsLoaded; }
- }
-
- public bool IsReady
- {
- get { IsAlive(true); return track.IsReady; }
- }
-
- public string Name
- {
- get { IsAlive(true); return track.Name; }
- }
-
- public int Popularity
- {
- get { IsAlive(true); return track.Popularity; }
- }
-
- public bool IsStarred
- {
- get { IsAlive(true); return track.IsStarred; }
- set { IsAlive(true); track.IsStarred = value; }
- }
-
- public ISession Session
- {
- get { IsAlive(true); return track.Session; }
- }
-
- protected override int IntPtrHashCode()
- {
- return IsAlive() ? track.trackPtr.GetHashCode() : 0;
- }
- }
- internal static IntPtr GetPointer(ITrack track)
- {
- if (track is TrackWrapper)
- return ((TrackWrapper)track).track.trackPtr;
- throw new ArgumentException("Invalid track");
- }
- #endregion
- #region Counter
- private static Dictionary<IntPtr, Track> tracks = new Dictionary<IntPtr, Track>();
- private static readonly object tracksLock = new object();
-
- internal static ITrack Get(Session session, IntPtr trackPtr)
- {
- Track track;
- lock (tracksLock)
- {
- if (!tracks.ContainsKey(trackPtr))
- {
- tracks.Add(trackPtr, new Track(session, trackPtr));
- }
- track = tracks[trackPtr];
- track.AddRef();
- }
- return new TrackWrapper(track);
- }
-
- internal static void Delete(IntPtr trackPtr)
- {
- lock (tracksLock)
- {
- Track track = tracks[trackPtr];
- int count = track.RemRef();
- if (count == 0)
- tracks.Remove(trackPtr);
- }
- }
- #endregion
-
- #region Declarations
- internal IntPtr trackPtr = IntPtr.Zero;
- private Session session;
-
- private IAlbum album = null;
- private DelegateArray<IArtist> artists = null;
- #endregion
-
- #region Constructor
- internal protected Track(Session session, IntPtr trackPtr)
- {
- if (trackPtr == IntPtr.Zero)
- throw new ArgumentException("trackPtr can't be zero.");
-
- if (session == null)
- throw new ArgumentNullException("Session can't be null.");
- this.session = session;
- this.trackPtr = trackPtr;
- lock (libspotify.Mutex)
- libspotify.sp_track_add_ref(trackPtr);
-
- this.artists = new DelegateArray<IArtist>(() =>
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_num_artists(trackPtr);
- }, (index) =>
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return Artist.Get(session, libspotify.sp_track_artist(trackPtr, index));
- });
-
- session.DisposeAll += new SessionEventHandler(session_DisposeAll);
- }
-
- void session_DisposeAll(ISession sender, SessionEventArgs e)
- {
- Dispose();
- }
- #endregion
-
- #region Properties
- public bool IsLoaded
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_is_loaded(trackPtr);
- }
- }
-
- public bool IsAvailable
- {
- get
- {
- IsAlive(true);
- try
- {
- lock (libspotify.Mutex)
- return libspotify.sp_track_is_available(session.sessionPtr, trackPtr);
- }
- catch (EntryPointNotFoundException e)
- {
- return false;
- }
- }
- }
-
- public sp_error Error
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_error(trackPtr);
- }
- }
-
- public IAlbum Album
- {
- get
- {
- IsAlive(true);
- if (album == null)
- lock (libspotify.Mutex)
- album = SpotifyLib.Album.Get(session, libspotify.sp_track_album(trackPtr));
-
- return album;
- }
- }
-
- public IArray<IArtist> Artists
- {
- get
- {
- IsAlive(true);
- return artists;
- }
- }
-
- public string Name
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.GetString(libspotify.sp_track_name(trackPtr), String.Empty);
- }
- }
-
- public TimeSpan Duration
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return TimeSpan.FromMilliseconds(libspotify.sp_track_duration(trackPtr));
- }
- }
-
- public int Popularity
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_popularity(trackPtr);
- }
- }
-
- public bool IsStarred
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_is_starred(session.sessionPtr, trackPtr);
-
- //return session.Starred.Tracks.Any(t => Track.GetPointer(t) == this.trackPtr);
- }
- set
- {
- IsAlive(true);
- IntPtr arrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
- IntPtr[] ptrArray = new IntPtr[] { trackPtr };
- try
- {
- Marshal.Copy(ptrArray, 0, arrayPtr, 1);
- lock (libspotify.Mutex)
- libspotify.sp_track_set_starred(session.sessionPtr, arrayPtr, 1, value);
- }
- finally
- {
- try { Marshal.FreeHGlobal(arrayPtr); }
- catch { }
- }
- }
- }
-
- public int Disc
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_disc(trackPtr);
- }
- }
-
- public int Index
- {
- get
- {
- IsAlive(true);
- lock (libspotify.Mutex)
- return libspotify.sp_track_index(trackPtr);
- }
- }
-
- public ISession Session
- {
- get
- {
- IsAlive(true);
- return session;
- }
- }
- #endregion
-
- #region IDisposeable
- protected override void OnDispose()
- {
- session.DisposeAll -= new SessionEventHandler(session_DisposeAll);
-
- if (!session.ProcExit)
- lock (libspotify.Mutex)
- libspotify.sp_track_release(trackPtr);
-
- trackPtr = IntPtr.Zero;
- }
- #endregion
-
- #region Await
- public bool IsReady
- {
- get { return !string.IsNullOrEmpty(Name); }
- }
- #endregion
-
- protected override int IntPtrHashCode()
- {
- return trackPtr.GetHashCode();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace SpotiFire.SpotifyLib
+{
+ internal class Track : CountedDisposeableSpotifyObject, ITrack
+ {
+ #region Wrapper
+ protected class TrackWrapper : DisposeableSpotifyObject, ITrack
+ {
+ internal Track track;
+ internal TrackWrapper(Track track)
+ {
+ this.track = track;
+ }
+
+ protected override void OnDispose()
+ {
+ Track.Delete(track.trackPtr);
+ track = null;
+ }
+
+ public IAlbum Album
+ {
+ get { IsAlive(true); return track.Album; }
+ }
+
+ public IArray<IArtist> Artists
+ {
+ get { IsAlive(true); return track.Artists; }
+ }
+
+ public int Disc
+ {
+ get { IsAlive(true); return track.Disc; }
+ }
+
+ public TimeSpan Duration
+ {
+ get { IsAlive(true); return track.Duration; }
+ }
+
+ public sp_error Error
+ {
+ get { IsAlive(true); return track.Error; }
+ }
+
+ public int Index
+ {
+ get { IsAlive(true); return track.Index; }