diff --git a/ZuneHost.Wpf/ZuneHost.Wpf.csproj b/ZuneHost.Wpf/ZuneHost.Wpf.csproj index fa637df..0678a68 100644 --- a/ZuneHost.Wpf/ZuneHost.Wpf.csproj +++ b/ZuneHost.Wpf/ZuneHost.Wpf.csproj @@ -6,13 +6,17 @@ 11.0 true Assets\ZuneFluentGem.ico - App.config + App.config + + + + diff --git a/ZuneHost/ZuneHost.csproj b/ZuneHost/ZuneHost.csproj index efc0241..27edeea 100644 --- a/ZuneHost/ZuneHost.csproj +++ b/ZuneHost/ZuneHost.csproj @@ -1,21 +1,25 @@  - - Exe - net461;net6.0-windows10.0.22000;net6.0; - AnyCPU;x64;x86;ARM32 - 11.0 - App.config - + + Exe + net461;net6.0-windows10.0.22000;net6.0; + AnyCPU;x64;x86;ARM32 + 11.0 + App.config + - - - - - + + + + + - - - + + + + + + + diff --git a/ZuneImpl/Extensions.cs b/ZuneImpl/Extensions.cs index 74148eb..98da979 100644 --- a/ZuneImpl/Extensions.cs +++ b/ZuneImpl/Extensions.cs @@ -5,6 +5,8 @@ using System.Net.Http; using System.Threading.Tasks; using System.Security.Cryptography; using System.Text; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; namespace Microsoft.Zune; @@ -50,4 +52,29 @@ internal static class Extensions MD5 md5 = MD5.Create(); return md5.ComputeHash(Encoding.Default.GetBytes(id)); } + + /// + /// Returns a value that indicates whether there is an object at the top of the System.Collections.Generic.Stack`1, + /// and if one is present, copies it to the result parameter, and removes it from + /// the System.Collections.Generic.Stack`1. + /// + /// + /// If present, the object at the top of the System.Collections.Generic.Stack`1; + /// otherwise, the default value of T. + /// + /// + /// true if there is an object at the top of the System.Collections.Generic.Stack`1; + /// false if the System.Collections.Generic.Stack`1 is empty. + /// + public static bool TryPop(this Stack stack, [MaybeNullWhen(false)] out T result) + { + if (stack.Count > 0) + { + result = stack.Pop(); + return true; + } + + result = default; + return false; + } } diff --git a/ZuneImpl/Library/StrixLibraryDataProviderQuery.cs b/ZuneImpl/Library/StrixLibraryDataProviderQuery.cs index f8c9faf..8ad8b4c 100644 --- a/ZuneImpl/Library/StrixLibraryDataProviderQuery.cs +++ b/ZuneImpl/Library/StrixLibraryDataProviderQuery.cs @@ -470,118 +470,7 @@ namespace Microsoft.Zune.Library } var stTracks = m_dataRoot.Library.GetTracksAsync(int.MaxValue, 0); - await foreach (var stTrack in stTracks) - { - var stAlbum = stTrack.Album; - var stAlbumArtist = stAlbum != null - ? await stAlbum.GetArtistItemsAsync(1, 0).FirstOrDefaultAsync() - : null; - - // Handle filters - if (queryType == EQueryType.eQueryTypeTracksForAlbumId) - { - var currentAlbumId = stAlbum?.Id.HashToInt32(); - if (currentAlbumId != albumId) continue; - } - else if (queryType == EQueryType.eQueryTypeTracksForAlbumArtistId) - { - var currentAlbumArtistId = stAlbumArtist?.Id.HashToInt32(); - if (currentAlbumArtistId != albumArtistId) continue; - } - else if (queryType == EQueryType.eQueryTypeTracksByGenreId) - { - var containsCurrentGenre = await stTrack.GetGenresAsync(int.MaxValue, 0) - .Select(g => g.Name.HashToInt32()) - .AnyAsync(g => g == genreId); - - if (!containsCurrentGenre && stAlbum != null) - { - // If the track doesn't have the genre, the album might. - containsCurrentGenre = await stAlbum.GetGenresAsync(int.MaxValue, 0) - .Select(g => g.Name.HashToInt32()) - .AnyAsync(g => g == genreId); - } - - if (!containsCurrentGenre) continue; - } - - Schemas.Track track = new(m_dataType) - { - AlbumName = stTrack.Album?.Name, - AlbumLibraryId = stTrack.Album?.Id.HashToInt32() ?? 0, - - TitleYomi = stTrack.Name, - ReleaseDate = DateTime.Now, - ZuneMediaId = stTrack.Id.HashToGuid(), - TrackNumber = stTrack.TrackNumber ?? 0, - Duration = stTrack.Duration, - ComponentId = "strix", - FileName = System.IO.Path.GetFileName(stTrack.Id), - FolderName = System.IO.Path.GetDirectoryName(stTrack.Id), - FilePath = stTrack.Id, - NowPlaying = false, - InLibrary = true, - DateLastPlayed = stTrack.LastPlayed ?? DateTime.MinValue, - PlayCount = 0, - FileSize = 0, - ComposerName = null, - ConductorName = null, - IsProtected = 0, - DateAdded = stTrack.AddedAt ?? DateTime.Now, - Bitrate = 0, - MediaType = "track", - DiscNumber = stTrack.DiscNumber ?? 0, - DateAlbumAdded = DateTime.Now, - FileCount = 1L, - DrmState = 0, - DrmStateMask = 0L, - QuickMixState = 0, - - LibraryId = stTrack.Id.HashToInt32(), - Title = stTrack.Name, - SyncState = 0, - Type = "track", - DeviceFileSize = 0L, - - UserRating = 0, - - AlbumArtistName = stAlbumArtist?.Name, - AlbumArtistLibraryId = stAlbumArtist?.Id.HashToInt32() ?? 0 - }; - - var stArtists = stTrack.GetArtistItemsAsync(int.MaxValue, 0); - bool setPrimaryArtist = false; - List contributingArtists = new(); - await foreach (var stArtist in stArtists) - { - if (!setPrimaryArtist) - { - track.ArtistLibraryId = stArtist.Id.HashToInt32(); - track.ArtistName = stArtist.Name; - track.ArtistNameYomi = stArtist.Name; - setPrimaryArtist = true; - } - else if (!string.IsNullOrEmpty(stArtist.Name)) - { - contributingArtists.Add(stArtist.Name); - } - } - track.ContributingArtistCount = contributingArtists.Count; - track.ContributingArtistNames = contributingArtists; - - // No artists - if (!setPrimaryArtist) - { - track.ArtistLibraryId = 0; - track.ArtistName = null; - track.ArtistNameYomi = null; - } - - var stGenre = await stTrack.GetGenresAsync(1, 0).FirstOrDefaultAsync(); - track.Genre = stGenre?.Name; - - queryList.Add(track.Item); - } + } else if (text == "AlbumByTOC") { @@ -659,6 +548,8 @@ namespace Microsoft.Zune.Library if (!TryGetProperty("PinType", out EPinType pinType)) pinType = EPinType.ePinTypeGeneric; + var pinnedTracks = m_dataRoot.Library.GetTracksAsync(3, 0); + Schemas.Pin pin = new(m_dataType) { PinId = 42, @@ -715,6 +606,123 @@ namespace Microsoft.Zune.Library Result = libraryDataProviderQueryResult; Status = DataProviderQueryStatus.Complete; } + + private Schemas.Track CreateTrack(ZuneQueryList queryList, ) + { + await foreach (var stTrack in stTracks) + { + var stAlbum = stTrack.Album; + var stAlbumArtist = stAlbum != null + ? await stAlbum.GetArtistItemsAsync(1, 0).FirstOrDefaultAsync() + : null; + + // Handle filters + if (queryType == EQueryType.eQueryTypeTracksForAlbumId) + { + var currentAlbumId = stAlbum?.Id.HashToInt32(); + if (currentAlbumId != albumId) continue; + } + else if (queryType == EQueryType.eQueryTypeTracksForAlbumArtistId) + { + var currentAlbumArtistId = stAlbumArtist?.Id.HashToInt32(); + if (currentAlbumArtistId != albumArtistId) continue; + } + else if (queryType == EQueryType.eQueryTypeTracksByGenreId) + { + var containsCurrentGenre = await stTrack.GetGenresAsync(int.MaxValue, 0) + .Select(g => g.Name.HashToInt32()) + .AnyAsync(g => g == genreId); + + if (!containsCurrentGenre && stAlbum != null) + { + // If the track doesn't have the genre, the album might. + containsCurrentGenre = await stAlbum.GetGenresAsync(int.MaxValue, 0) + .Select(g => g.Name.HashToInt32()) + .AnyAsync(g => g == genreId); + } + + if (!containsCurrentGenre) continue; + } + + Schemas.Track track = new(m_dataType) + { + AlbumName = stTrack.Album?.Name, + AlbumLibraryId = stTrack.Album?.Id.HashToInt32() ?? 0, + + TitleYomi = stTrack.Name, + ReleaseDate = DateTime.Now, + ZuneMediaId = stTrack.Id.HashToGuid(), + TrackNumber = stTrack.TrackNumber ?? 0, + Duration = stTrack.Duration, + ComponentId = "strix", + FileName = System.IO.Path.GetFileName(stTrack.Id), + FolderName = System.IO.Path.GetDirectoryName(stTrack.Id), + FilePath = stTrack.Id, + NowPlaying = false, + InLibrary = true, + DateLastPlayed = stTrack.LastPlayed ?? DateTime.MinValue, + PlayCount = 0, + FileSize = 0, + ComposerName = null, + ConductorName = null, + IsProtected = 0, + DateAdded = stTrack.AddedAt ?? DateTime.Now, + Bitrate = 0, + MediaType = "track", + DiscNumber = stTrack.DiscNumber ?? 0, + DateAlbumAdded = DateTime.Now, + FileCount = 1L, + DrmState = 0, + DrmStateMask = 0L, + QuickMixState = 0, + + LibraryId = stTrack.Id.HashToInt32(), + Title = stTrack.Name, + SyncState = 0, + Type = "track", + DeviceFileSize = 0L, + + UserRating = 0, + + AlbumArtistName = stAlbumArtist?.Name, + AlbumArtistLibraryId = stAlbumArtist?.Id.HashToInt32() ?? 0 + }; + + var stArtists = stTrack.GetArtistItemsAsync(int.MaxValue, 0); + bool setPrimaryArtist = false; + List contributingArtists = new(); + await foreach (var stArtist in stArtists) + { + if (!setPrimaryArtist) + { + track.ArtistLibraryId = stArtist.Id.HashToInt32(); + track.ArtistName = stArtist.Name; + track.ArtistNameYomi = stArtist.Name; + setPrimaryArtist = true; + } + else if (!string.IsNullOrEmpty(stArtist.Name)) + { + contributingArtists.Add(stArtist.Name); + } + } + track.ContributingArtistCount = contributingArtists.Count; + track.ContributingArtistNames = contributingArtists; + + // No artists + if (!setPrimaryArtist) + { + track.ArtistLibraryId = 0; + track.ArtistName = null; + track.ArtistNameYomi = null; + } + + var stGenre = await stTrack.GetGenresAsync(1, 0).FirstOrDefaultAsync(); + track.Genre = stGenre?.Name; + + track.Item.Storage["StrixItem"] = stTrack; + queryList.Add(track.Item); + } + } } internal class DeferredSetResultArgs diff --git a/ZuneImpl/Playback/VlcAudioService.cs b/ZuneImpl/Playback/VlcAudioService.cs index 2a85a17..e4da2b2 100644 --- a/ZuneImpl/Playback/VlcAudioService.cs +++ b/ZuneImpl/Playback/VlcAudioService.cs @@ -3,6 +3,7 @@ using LibVLCSharp.Shared; using StrixMusic.Sdk.MediaPlayback; using System; +using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -16,6 +17,7 @@ namespace Microsoft.Zune.Playback readonly LibVLC m_vlc; readonly MediaPlayer m_player; private Media m_media, m_nextMedia; + private Stack m_toDisposeOnEnd = new(2); private PlaybackItem m_currentSource; private TimeSpan m_position; @@ -46,6 +48,11 @@ namespace Microsoft.Zune.Playback m_player = new(m_vlc); m_player.TimeChanged += (sender, e) => Position = TimeSpan.FromMilliseconds(e.Time); + m_player.EndReached += (sender, e) => + { + while (m_toDisposeOnEnd.TryPop(out var item)) + item.Dispose(); + }; } public PlaybackItem CurrentSource @@ -179,12 +186,24 @@ namespace Microsoft.Zune.Playback var uri = sourceConfig.MediaConfig.MediaSourceUri; var stream = sourceConfig.MediaConfig.FileStreamSource; + Media media = null; if (uri != null) - return new(m_vlc, uri); + { + media = new(m_vlc, uri); + } else if (stream != null) - return new(m_vlc, new StreamMediaInput(stream)); - - throw new NotImplementedException(); + { + StreamMediaInput streamInput = new(stream); + media = new(m_vlc, streamInput); + + m_toDisposeOnEnd.Push(streamInput); + } + + if (media is null) + throw new NotImplementedException(); + + m_toDisposeOnEnd.Push(media); + return media; } } } diff --git a/ZuneImpl/ZuneImpl.csproj b/ZuneImpl/ZuneImpl.csproj index c993763..0da9e3b 100644 --- a/ZuneImpl/ZuneImpl.csproj +++ b/ZuneImpl/ZuneImpl.csproj @@ -21,8 +21,4 @@ - - - - diff --git a/ZuneShell.sln b/ZuneShell.sln index d4733af..762c581 100644 --- a/ZuneShell.sln +++ b/ZuneShell.sln @@ -83,8 +83,8 @@ Global {CB2B7609-C7B8-4E2A-82D8-DD631B70B621}.Release|x64.Build.0 = Release|x64 {CB2B7609-C7B8-4E2A-82D8-DD631B70B621}.Release|x86.ActiveCfg = Release|x86 {CB2B7609-C7B8-4E2A-82D8-DD631B70B621}.Release|x86.Build.0 = Release|x86 - {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|Any CPU.ActiveCfg = Debug|x64 + {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|Any CPU.Build.0 = Debug|x64 {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|ARM32.ActiveCfg = Debug|ARM32 {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|ARM32.Build.0 = Debug|ARM32 {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Debug|x64.ActiveCfg = Debug|x64 @@ -99,8 +99,8 @@ Global {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Release|x64.Build.0 = Release|x64 {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Release|x86.ActiveCfg = Release|x86 {4041A91F-EF2A-4C47-9EBE-F4E862532345}.Release|x86.Build.0 = Release|x86 - {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|Any CPU.ActiveCfg = Debug|x64 + {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|Any CPU.Build.0 = Debug|x64 {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|ARM32.ActiveCfg = Debug|x64 {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|ARM32.Build.0 = Debug|x64 {1FEAC59B-F012-4ACC-9F11-E7ADF4C77593}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs b/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs index 9d3a746..c848480 100644 --- a/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs +++ b/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs @@ -150,7 +150,7 @@ namespace Microsoft.Zune.Shell cacheFolderPath.Create(); OwlCore.Storage.SystemIO.SystemFolder cacheFolder = new(cacheFolderPath); - OwlCore.Storage.SystemIO.SystemFolder musicFolder = new(@"D:\Music\Zune\Test"); + OwlCore.Storage.SystemIO.SystemFolder musicFolder = new(@"C:\Users\jjask\OneDrive\Music\Rush"); var localCore = new StrixMusic.Cores.Storage.StorageCore(musicFolder, cacheFolder, "Local Test") { ScannerWaitBehavior = StrixMusic.Cores.Storage.ScannerWaitBehavior.AlwaysWait, diff --git a/ZuneShell/ZuneUI/PlaylistManager.cs b/ZuneShell/ZuneUI/PlaylistManager.cs index 4fd5a98..39ed87d 100644 --- a/ZuneShell/ZuneUI/PlaylistManager.cs +++ b/ZuneShell/ZuneUI/PlaylistManager.cs @@ -465,8 +465,17 @@ namespace ZuneUI case QuickMixItem _: AddQuickMixItemToTrackList((QuickMixItem)obj, tracks, containerPlayMarkerOverride); continue; + +#if OPENZUNE + case Microsoft.Iris.UI.Class ozMediaItem: + var stTrack = (StrixMusic.Sdk.AppModels.ITrack)ozMediaItem.Storage["StrixItem"]; + var zuneMediaId = (Guid)ozMediaItem.Storage["ZuneMediaId"]; + tracks.Add(new Strix.StrixPlaybackTrack(stTrack, zuneMediaId)); + break; +#endif + default: - continue; + continue; } } } diff --git a/ZuneShell/ZuneUI/Strix/StrixPlaybackTrack.cs b/ZuneShell/ZuneUI/Strix/StrixPlaybackTrack.cs new file mode 100644 index 0000000..8d24854 --- /dev/null +++ b/ZuneShell/ZuneUI/Strix/StrixPlaybackTrack.cs @@ -0,0 +1,37 @@ +using Microsoft.Zune; +using StrixMusic.Sdk.AppModels; +using System; +using System.Linq; + +namespace ZuneUI.Strix; + +public class StrixPlaybackTrack : PlaybackTrack +{ + public StrixPlaybackTrack(ITrack stTrack, Guid zuneId) + { + Track = stTrack; + ZuneMediaId = zuneId; + } + + public ITrack Track { get; } + + public override string Title => Track.Name; + + public override TimeSpan Duration => Track.Duration; + + public override Guid ZuneMediaId { get; } + + public override HRESULT GetURI(out string uri) + { + uri = null; + if (Track.TotalUrlCount <= 0) + return HRESULT._DB_E_NOTFOUND; + + var url = AsyncHelper.Run( + () => Track.GetUrlsAsync(1, 0).FirstAsync().AsTask() + ); + + uri = url.Url.ToString(); + return HRESULT._S_OK; + } +} diff --git a/ZuneShell/ZuneUI/TransportControls/TransportControls.cs b/ZuneShell/ZuneUI/TransportControls/TransportControls.cs index 453f762..51a34f9 100644 --- a/ZuneShell/ZuneUI/TransportControls/TransportControls.cs +++ b/ZuneShell/ZuneUI/TransportControls/TransportControls.cs @@ -24,6 +24,7 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Threading; using UIXControls; +using ZuneUI.Strix; namespace ZuneUI { @@ -1838,20 +1839,19 @@ namespace ZuneUI int myID = ++_lastKnownSetUriCallID; ThreadPool.QueueUserWorkItem(args => { + var dataRoot = Microsoft.Zune.Shell.ZuneApplication.DataRoot; if (track != null) { - HRESULT hr = track.GetURI(out string trackUri); - if (string.IsNullOrEmpty(trackUri)) - trackUri = ".:* INVALID URI *:."; + try + { + var stTrack = ((StrixPlaybackTrack)track).Track; - if (hr.IsSuccess) Application.DeferredInvoke(delegate { if (IsDisposed || myID != _lastKnownSetUriCallID) return; - PlaybackItem item = ZuneMediaSourceConfig.CreatePlaybackItem(trackUri, track.PlaybackID); - _player.PlaySync(item); + AsyncHelper.Run(dataRoot.Library.PlayTrackCollectionAsync(stTrack)); ReportStreamingAction(PlayerState.Stopped); _tracksSubmittedToPlayer.Remove(track); @@ -1862,35 +1862,40 @@ namespace ZuneUI // TODO: _playbackWrapper.CancelNext(); UpdatePropertiesAndCommands(); }, null); - else + } + catch (Exception ex) + { OnAlertSent(new Announcement() { - HResult = hr.Int, + HResult = ex.HResult, PlaybackID = track.PlaybackID }); + } } if (nextTrack == null) return; - HRESULT nextTrackHr = nextTrack.GetURI(out string nextTrackUri); - if (string.IsNullOrEmpty(nextTrackUri)) - nextTrackUri = ".:* INVALID URI *:."; - if (!nextTrackHr.IsSuccess) - return; - - Application.DeferredInvoke(delegate + try { - if (IsDisposed || myID != _lastKnownSetUriCallID) - return; + var stTrack = ((StrixPlaybackTrack)nextTrack).Track; - PlaybackItem item = ZuneMediaSourceConfig.CreatePlaybackItem(nextTrackUri, track.PlaybackID); - _player.PreloadSync(item); + Application.DeferredInvoke(delegate + { + if (IsDisposed || myID != _lastKnownSetUriCallID) + return; - _tracksSubmittedToPlayer.Remove(nextTrack); - _tracksSubmittedToPlayer.Add(nextTrack); - UpdatePropertiesAndCommands(); - }, null); + // Preload track + //AsyncHelper.Run(dataRoot.Library.PlayTrackCollectionAsync(stTrack)); + + _tracksSubmittedToPlayer.Remove(nextTrack); + _tracksSubmittedToPlayer.Add(nextTrack); + UpdatePropertiesAndCommands(); + }, null); + } + catch + { + } }, null); }