diff --git a/NotZune.sln b/NotZune.sln index 4ea38ae..11b5151 100644 --- a/NotZune.sln +++ b/NotZune.sln @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotZune.Tests.Application", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotZune.Tests.Domain", "tests\NotZune.Tests.Domain\NotZune.Tests.Domain.csproj", "{275BFD54-E360-44EC-8A6C-03D6167413BF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotZune.Infrastructure.Video", "src\NotZune.Infrastructure.Video\NotZune.Infrastructure.Video.csproj", "{E7DE99F2-2C5A-46E4-A646-306A59915A2F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -88,6 +90,10 @@ Global {275BFD54-E360-44EC-8A6C-03D6167413BF}.Debug|Any CPU.Build.0 = Debug|Any CPU {275BFD54-E360-44EC-8A6C-03D6167413BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {275BFD54-E360-44EC-8A6C-03D6167413BF}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DE99F2-2C5A-46E4-A646-306A59915A2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DE99F2-2C5A-46E4-A646-306A59915A2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DE99F2-2C5A-46E4-A646-306A59915A2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DE99F2-2C5A-46E4-A646-306A59915A2F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {9C687BB5-6AE3-4C94-A12F-6D34C5DAAFFD} = {386DD2E6-7733-4758-9DEF-333286DDB86F} @@ -102,5 +108,6 @@ Global {E0F28BF6-1159-41CC-BBEE-1FBAFED53447} = {386DD2E6-7733-4758-9DEF-333286DDB86F} {17B493BB-CFCB-423D-844F-3ECD6BA3D6CC} = {89103A14-FD5B-4F9F-9AEF-803F5D97F55C} {275BFD54-E360-44EC-8A6C-03D6167413BF} = {89103A14-FD5B-4F9F-9AEF-803F5D97F55C} + {E7DE99F2-2C5A-46E4-A646-306A59915A2F} = {386DD2E6-7733-4758-9DEF-333286DDB86F} EndGlobalSection EndGlobal diff --git a/src/NotZune.Application/Interfaces/IPhotoLibraryService.cs b/src/NotZune.Application/Interfaces/IPhotoLibraryService.cs new file mode 100644 index 0000000..b3fb2c7 --- /dev/null +++ b/src/NotZune.Application/Interfaces/IPhotoLibraryService.cs @@ -0,0 +1,13 @@ +using NotZune.Domain.Models; + +namespace NotZune.Application.Interfaces; + +public interface IPhotoLibraryService +{ + Task> GetAllPhotosAsync(); + + Task ScanDirectoryAsync(string directoryPath, IProgress? progress = null); + + /// Distinct folder paths currently present in the photo library. + Task> GetFoldersAsync(); +} diff --git a/src/NotZune.Application/Interfaces/IVideoLibraryService.cs b/src/NotZune.Application/Interfaces/IVideoLibraryService.cs new file mode 100644 index 0000000..e25bdd5 --- /dev/null +++ b/src/NotZune.Application/Interfaces/IVideoLibraryService.cs @@ -0,0 +1,12 @@ +using NotZune.Domain.Models; + +namespace NotZune.Application.Interfaces; + +public interface IVideoLibraryService +{ + Task> GetAllVideosAsync(); + + Task ScanDirectoryAsync(string directoryPath, IProgress? progress = null); + + Task MarkPlayedAsync(Guid videoId); +} diff --git a/src/NotZune.Application/Interfaces/IVideoPlaybackEngine.cs b/src/NotZune.Application/Interfaces/IVideoPlaybackEngine.cs new file mode 100644 index 0000000..adc4a63 --- /dev/null +++ b/src/NotZune.Application/Interfaces/IVideoPlaybackEngine.cs @@ -0,0 +1,30 @@ +namespace NotZune.Application.Interfaces; + +/// +/// Real video playback boundary (libVLC-backed). The Avalonia VideoView binds to +/// ; all operations no-op safely when unavailable. +/// +public interface IVideoPlaybackEngine : IDisposable +{ + bool IsAvailable { get; } + + object? MediaPlayerHandle { get; } + + void LoadAndPlay(string filePath); + + void Play(); + + void Pause(); + + void Stop(); + + void SetVolume(double volume, bool muted); + + void Seek(TimeSpan position); + + TimeSpan GetPosition(); + + TimeSpan GetDuration(); + + event EventHandler? PlaybackEnded; +} diff --git a/src/NotZune.Application/Models/AppSettings.cs b/src/NotZune.Application/Models/AppSettings.cs index bbf1d36..06b0da3 100644 --- a/src/NotZune.Application/Models/AppSettings.cs +++ b/src/NotZune.Application/Models/AppSettings.cs @@ -42,6 +42,8 @@ public class AppSettings public int SpaceReservationPercent { get; set; } = 10; public string MusicSyncRule { get; set; } = "All Music (Automatic Sync)"; public string PodcastSyncRule { get; set; } = "3 Newest Episodes"; + public string VideoSyncRule { get; set; } = "All Videos & Pictures"; + public string PicturesSyncRule { get; set; } = "Newest 25 Items"; public bool WirelessSyncEnabled { get; set; } = true; public string NetworkName { get; set; } = "Home-WiFi (WPA2)"; diff --git a/src/NotZune.Application/Services/DefaultVideoServices.cs b/src/NotZune.Application/Services/DefaultVideoServices.cs new file mode 100644 index 0000000..b4f90e9 --- /dev/null +++ b/src/NotZune.Application/Services/DefaultVideoServices.cs @@ -0,0 +1,37 @@ +using NotZune.Application.Interfaces; +using NotZune.Domain.Models; + +namespace NotZune.Application.Services; + +/// Default no-op video library used when the persistence layer is not wired (e.g. unit tests). +public sealed class EmptyVideoLibraryService : IVideoLibraryService +{ + public Task> GetAllVideosAsync() => Task.FromResult>(new List