Skip to content

Latest commit

 

History

History
480 lines (313 loc) · 26.7 KB

PROJECT_STRUCTURE.md

File metadata and controls

480 lines (313 loc) · 26.7 KB

📱 Screenbox Project Structure

This document provides a comprehensive overview of the Screenbox project's architecture, organization, and practical workflows for development.

📋 Table of Contents

📖 Overview

Screenbox is a modern media player for Windows built using the Universal Windows Platform (UWP) and LibVLCSharp. The application follows the Model-View-ViewModel (MVVM) design pattern with dependency injection for maintainable and testable code.

The architecture is built around clean separation of concerns:

  • View Layer: XAML-based UI components and user controls
  • ViewModel Layer: Presentation logic and data binding with MVVM
  • Model Layer: Business logic, services, and media playback engine

🏗️ Solution Structure

The solution contains several projects organized for clear separation of concerns:

Screenbox.slnx
├── Screenbox/                 # Main UWP application
├── Screenbox.Core/            # Core services, models, view models, and playback logic
├── Screenbox.UI/              # Shared UI components and custom controls
├── Screenbox.VideoView/       # Video rendering helper for playback surfaces
├── Screenbox.Lively/          # Lively Wallpaper integration support
└── Screenbox.Core.Tests/      # Unit and integration tests for the core library

Main Project: Screenbox

The main project is the user-facing Windows application. It owns the shell, pages, controls, dialogs, flyouts, localization, and app startup wiring.

Core Library: Screenbox.Core

The core project contains the business logic, services, domain models, view models, contexts, coordinators, playback abstractions, and the app-wide messenger contracts.

UI Library: Screenbox.UI

This library contains shared UI components, theme resources, and templated controls that can be reused across different parts of the application.

Video Rendering Helper: Screenbox.VideoView

The VideoView project provides a helper for rendering video playback surfaces, abstracting the underlying video rendering implementation.

Lively Wallpaper Integration: Screenbox.Lively

The Lively project provides integration support for Lively Wallpaper, allowing the application to interact with and control live wallpapers.

Core Tests: Screenbox.Core.Tests

The Core.Tests project contains unit and integration tests for the core logic, ensuring the correctness and reliability of the application's business logic.

🎨 View Layer

The View layer is contained in the Screenbox project. This project consists primarily of XAML files and custom controls that define the user interface. App.xaml and the Styles folder contain resources referenced throughout the application, while the App.xaml.cs file serves as the main entry point and handles dependency injection configuration.

Core UI Structure

Screenbox uses a single-page application model with MainPage.xaml as the root container. The main page utilizes a NavigationView control for the navigation menu and houses content frames for different application modes:

MainPage.xaml (Root Container)
├── NavigationView (Menu System)
├── ContentFrame (Dynamic Content)
└── PlayerPage.xaml (Media Player Overlay)

Key Page Components

The application organizes its content into several main page categories:

  • HomePage.xaml: Recently accessed media
  • VideosPage.xaml: Video library browsing with nested pages for categories
  • MusicPage.xaml: Music library with artist, album, and song views
  • NetworkPage.xaml: Network streaming and casting features
  • PlayQueuePage.xaml: Current play queue and queue management
  • PlaylistsPage.xaml: Playlist creation and management
  • SettingsPage.xaml: Application configuration and preferences

Custom Controls Architecture

Screenbox implements numerous custom controls for specialized functionality:

Core Playback Controls

  • PlayerControls.xaml: Primary media control interface
  • SeekBar.xaml: Timeline scrubbing and progress indication
  • VolumeControl.xaml: Audio level management
  • PlayerElement.xaml: Main video rendering surface

Media Display Controls

  • MediaListViewItem.xaml: Templated list items for media content
  • CommonGridViewItem.xaml: Grid layout for media thumbnails
  • PlayQueueControl.xaml: Specialized play queue display component

Dialogs and Overlay Controls

  • PropertiesDialog.xaml: Media file information display
  • OpenUrlDialog.xaml: Network URL input interface

Visual States

Visual States enable adaptive layouts that respond to window size changes, device orientation, and user interaction modes. Screenbox uses visual states extensively to create responsive experiences across different form factors.

Data Binding

Screenbox uses data binding extensively to create dynamic, responsive UI components. The application primarily uses the x:Bind markup extension for performance benefits over the legacy Binding syntax.

Example of x:Bind usage in media display:

<TextBlock Text="{x:Bind ViewModel.CurrentMedia.Title, Mode=OneWay}" />
<ProgressBar Value="{x:Bind ViewModel.PlaybackPosition, Mode=OneWay}" />

The binding system enables automatic UI updates when media state changes, providing smooth user experiences without manual UI manipulation code.

Navigation System

Navigation in Screenbox is handled through a custom NavigationService that maps ViewModels to their corresponding Pages. This system is configured in App.xaml.cs and enables loose coupling between the ViewModel and View layers:

new KeyValuePair<Type, Type>(typeof(HomePageViewModel), typeof(HomePage)),
new KeyValuePair<Type, Type>(typeof(VideosPageViewModel), typeof(VideosPage)),
new KeyValuePair<Type, Type>(typeof(MusicPageViewModel), typeof(MusicPage))

Localization System

Screenbox implements comprehensive multi-language support through the Windows and ReswPlus resource system, following UWP localization guidelines and ReswPlus guides for efficient localized resource management.

🔄 ViewModel Layer

The ViewModel layer is contained in the Screenbox.Core project and serves as the intermediary between the UI components and business logic. ViewModels provide data sources for UI binding and encapsulate presentation logic while remaining independent of specific UI implementations.

Key ViewModel Architecture

Primary Application ViewModels

  • MainPageViewModel.cs: Root application state and navigation coordination
  • PlayerPageViewModel.cs: Media playback state and control logic
  • PlayerControlsViewModel.cs: Playback control interactions and state
  • SettingsPageViewModel.cs: Application configuration management

Content Management ViewModels

  • PlayQueuePanelViewModel.cs: ViewModel for the play queue panel/flyout — handles item selection, reordering, and adding files or URLs to the queue
  • CommonViewModel.cs: Shared state across multiple pages
  • MediaViewModel.cs: Individual media item representation
  • PlaylistViewModel.cs: Playlist-specific functionality

Library and Search ViewModels

  • HomePageViewModel.cs: Recent media
  • VideosPageViewModel.cs: Video library browsing
  • MusicPageViewModel.cs: Music library organization
  • SearchResultPageViewModel.cs: Search functionality and results

Observable Domain Entities

Some ViewModels — specifically MediaViewModel, AlbumViewModel, and ArtistViewModel — serve a dual role: they are MVVM observables and the effective domain entities for media items. This is a deliberate pragmatic choice for UWP MVVM: the application domain is inherently observable (tracks must notify when metadata loads, albums notify when thumbnails change), so the domain model and the observable layer naturally coincide.

As a result, these classes appear throughout the codebase in all layers — including LibraryContext, PlaylistsContext, and SearchResult — without violating the intended architecture. They should be thought of as observable domain entities rather than pure presentation objects, and should never contain XAML or UI framework references.

PropertyChanged Events

ViewModels implement property change notification through the CommunityToolkit.Mvvm framework. Most ViewModels inherit from ObservableObject or use source generators to implement INotifyPropertyChanged automatically:

[ObservableProperty]
private partial string CurrentMediaTitle { get; set; }

[ObservableProperty]
private partial TimeSpan PlaybackPosition { get; set; }

This enables automatic UI updates when ViewModel properties change, maintaining synchronization between the data layer and user interface.

Messaging System

Screenbox uses the CommunityToolkit.Mvvm messaging system for decoupled communication between components. This allows ViewModels, Services, and other components to communicate without direct references.

Core Message Types

Playback Control Messages

  • PlayMediaMessage.cs: Initiate media playback
  • TogglePlayPauseMessage.cs: Toggle playback state
  • ChangeTimeRequestMessage.cs: Seek to specific time position

UI State Messages

  • PlayerControlsVisibilityChangedMessage.cs: Show/hide player controls
  • SettingChangedMessage.cs: Strongly-typed domain setting modification notifications
  • NavigationViewDisplayModeRequestMessage.cs: Navigation menu state changes
  • NotificationMessage.cs: Show notifications inside the app
  • PlayerOsdUpdateMessage.cs: On-screen display updates for playback information

System Integration Messages

  • SuspendingMessage.cs: Application lifecycle events

🔧 Model Layer

The Model layer contains the core business logic and is primarily located in the Screenbox.Core project. This layer consists of services, media playback components, and data models that provide the foundation for the application functionality.

Services Architecture

Screenbox implements a comprehensive service-oriented architecture using Microsoft.Extensions.DependencyInjection for service registration and resolution. Services are registered in ServiceHelpers.cs and injected throughout the application.

Core Business Services

Media Management Services

  • IFilesService: File system operations and media discovery
  • ILibraryService: Stateless operations for fetching and managing libraries. FetchMusicAsync returns a MusicLibrary and accepts an optional IProgress<MusicLibrary> for incremental batch updates; FetchVideosAsync works analogously with VideosLibrary.
  • ISearchService: Content search and filtering functionality

Playback and Streaming Services

  • IPlayerService: Media player initialization and playback item management
  • ICastService: Stateless casting operations for renderer creation and configuration
  • ISystemMediaTransportControlsService: Windows media key integration

Persistence Services

  • IDatabaseService: Low-level SQLite database management. Opens (or creates) screenbox.db in ApplicationData.Current.LocalFolder, applies the schema on first open, and exposes CreateConnection() for typed data access. The library tables are rebuildable cache data, but playlists are durable user data. If schema initialization fails with a SQLite or I/O error, the current fallback deletes and recreates the database file, which can also erase playlists. Registered as a singleton and initialized early at app startup.
  • IPlaybackProgressTracker: Tracks and persists the playback progress for each media item, enabling resume-from-position functionality. Handles the SuspendingMessage to flush state to the database on app suspension.

System Integration Services

  • ISettingsStorage: Low-level application configuration persistence
  • IWindowService: Window management and display operations

Application Contexts

Contexts are observable application state holders shared across services and ViewModels. They represent the current runtime state of a domain area and act as the single source of truth for that state within the app.

Architectural rules for contexts:

  • Written by: Services and coordinators (as a side effect of use-case execution)
  • Read/observed by: ViewModels (via data binding and property-changed events)
  • Must not: Call services or coordinators back (no circular dependencies)
  • May contain: Messenger helper methods that broadcast state change events

Available contexts:

  • PlayerContext: Holds the current IMediaPlayer instance, allowing components to observe player state changes
  • CastContext: Holds casting state including the active renderer watcher and selected renderer
  • LibraryContext: Holds library state including the StorageLibrary handles (MusicStorageLibrary, VideosStorageLibrary), loading flags, and the current MusicLibrary and VideosLibrary container objects. Written atomically by LibraryCoordinator after each fetch batch or on completion.
  • PlaylistsContext: Holds the application-wide collection of user playlists
  • PlayQueueContext: Holds the global play queue state — Items (the ordered ObservableCollection<MediaViewModel>), CurrentItem, CurrentIndex, ShuffleMode, and RepeatMode. Written exclusively by PlayQueueCoordinator; observed by any ViewModel that needs play queue state.

Stateful Coordinators

Some platform APIs (file system watchers, timers, device watchers) require stateful ownership to reliably subscribe/unsubscribe and to avoid duplicating watchers across views. Coordinators own these long-lived resources and orchestrate service calls in response to platform events.

Architectural rules for coordinators:

  • Depend on: Services and contexts (never on ViewModels)
  • Consumed by: ViewModels at initialization boundaries (to start lifecycle)
  • Must expose: An interface (ILibraryCoordinator, etc.) for testability
  • Implement: IDisposable to clean up watchers/timers

Available coordinators:

  • LibraryCoordinator / ILibraryCoordinator: Stateful coordinator that owns library StorageFileQueryResult watchers, debounce timers, and Xbox removable-storage device watchers. Calls ILibraryService.FetchMusicAsync / FetchVideosAsync with an IProgress<T> handler that updates LibraryContext.Music / LibraryContext.Videos incrementally as each batch arrives, then applies the final result on completion. This provides live UI progress updates during long library scans.
  • PlayQueueCoordinator / IPlayQueueCoordinator: Stateful coordinator that owns the global play queue for the app session. Handles PlayMediaMessage, SetQueueMessage, ClearQueueMessage, and QueueRequestMessage. Wires Windows System Media Transport Controls (hardware media keys, repeat mode). Pre-buffers thumbnails for items adjacent to the current position. Drives PlayQueueContext state as a side effect of all queue mutations.

Helper Classes

Helper classes provide focused utilities and lightweight wrappers for specific functionality:

  • RendererWatcher: Lightweight wrapper around VLC's RendererDiscoverer for network renderer discovery, exposing events for renderer found/lost notifications and maintaining a list of available renderers
  • DisplayRequestTracker: Manages display sleep prevention during media playback

Media Playback Engine

The media playback system is built on LibVLCSharp with custom abstractions for integration with the MVVM architecture:

Core Playback Components

  • IMediaPlayer: Media player abstraction interface
  • IPlaybackItem: Playback item interface with track, chapter, and timing abstractions
  • VlcMediaPlayer.cs: VLC-based implementation of media player
  • VlcPlaybackItem.cs: VLC-backed implementation of playback item

Track Management System

  • VlcAudioTrackList.cs: Audio track selection and switching
  • VlcVideoTrackList.cs: Video track and subtitle management
  • VlcSubtitleTrackList.cs: Subtitle track handling
  • VlcChapterList.cs: Chapter navigation support

The playback engine provides a clean interface for the ViewModel layer while abstracting the complexities of the underlying VLC media framework.

Data Models and Persistence

Media Information Models

  • MediaInfo.cs: Base media file information
  • VideoInfo.cs: Video-specific metadata and properties
  • MusicInfo.cs: Audio metadata and music library information

Application State Models

  • MusicLibrary.cs: Immutable snapshot of the music library — Songs, Albums, Artists, UnknownAlbum, and UnknownArtist. Replaced by a new instance whenever the library is refreshed, so all relationships stay consistent. Has a static Empty instance used as the initial LibraryContext.Music value.
  • VideosLibrary.cs: Immutable snapshot of the video library — Videos list. Same replacement model as MusicLibrary.
  • RawMediaRecordDto.cs: Serializable snapshot of a media item's metadata (title, path, duration, year, type, and type-specific properties). Used as the unit of persistence for playlist items and as an intermediate representation when reading the media-records cache from the database.

SQLite Database Schema

The library tables in screenbox.db (SQLite, LocalFolder) are rebuildable cache data. The playlists and playlist_items tables contain durable user data and are not disposable. A missing database is created on startup; if schema initialization fails with a SQLite or I/O error, the current fallback recreates the whole file, which can also erase playlists.

Table Purpose
library_folders Tracks which folders belong to the music and video libraries
media_records Caches media-file metadata indexed by path; queried instead of crawling disk on subsequent launches
playback_progress Stores the last playback position per media location for resume-from-position
playlists One row per saved playlist (id, display name, last-updated timestamp)
playlist_items Ordered list of media snapshots for each playlist; cascade-deleted with the parent playlist

🏛️ Architecture Rules

Dependency Flow

The following table summarizes the allowed dependency directions between layers:

Layer May depend on Must not depend on
View (XAML + code-behind) ViewModels, Contexts (read-only via DI) Services, Coordinators directly
ViewModels Services (via interface), Contexts, Factories Coordinators (except at startup boundaries)
Contexts Models, Playback abstractions Services, Coordinators, ViewModels
Coordinators Services (via interface), Contexts ViewModels
Services Other Services (via interface), Models, Playback ViewModels, Contexts
Models / Playback (no application dependencies) All layers above

DI and Factory Patterns

  • All services and coordinators must be registered against their interface, not their concrete type.
  • Use constructor injection exclusively. The service locator pattern (Ioc.Default.GetRequiredService<T>()) is acceptable only in View code-behind for resolving ViewModels into page DataContext. It must not be used inside ViewModels, Services, or Coordinators.

Stateless vs Stateful

  • Stateless services (e.g., LibraryService, CastService): execute operations and return data to the caller. They do not own long-lived observable state, and must not read from or write to a Context directly. Results are returned as plain models (e.g., MusicLibrary) and applied to the context by the coordinator.
  • Stateful coordinators (e.g., LibraryCoordinator): own watchers/timers and manage subscriptions/lifetimes. Register as singletons and implement IDisposable.
  • Stateful services (e.g., PlaybackProgressTracker): own in-memory state but no platform subscriptions. Prefer a service interface; register as singletons.

🧰 Technology Stack

  • UWP (Universal Windows Platform): Windows application framework providing native performance and system integration
  • .NET 10: Cross-platform runtime for building modern applications
  • XAML: Declarative markup for user interface definition
  • LibVLCSharp: Cross-platform media playback engine with extensive codec support
  • CommunityToolkit.Mvvm: MVVM framework for property change notification, messaging, and source generation

Development and Build Tools

  • Visual Studio 2026: Primary integrated development environment
  • MSBuild: Build system and project management
  • XAML Styler: XAML formatting and style enforcement

Key Dependencies

MVVM and UI Framework

Dependency Injection

Media and Data Processing

Monitoring and Analytics

📋 Development Guidelines

Code Organization

Follow C# coding conventions and UWP best practices throughout the codebase.

Naming Conventions

Adhere to .NET naming guidelines:

  • PascalCase: Classes, methods, properties, public fields, enums
  • camelCase: Local variables, parameters, private fields
  • Interfaces: Prefixed with I (e.g., IMediaPlayer)
  • Private fields: Prefixed with _ (e.g., _settingsService)
  • XAML resources: Descriptive, consistent naming patterns

File Organization

  • One class per file
  • Nested namespaces match folder structure
  • Partial classes for XAML code-behind

Performance Considerations

Asynchronous Programming

  • Use async/await for I/O operations following async best practices
  • Proper ConfigureAwait(false) usage in library code
  • Task-based operations for file system access

Memory Management

UI Performance

  • Follow UWP performance best practices
  • Enable virtualization for large collections
  • Implement proper image caching
  • Process heavy operations on background threads

Build Configuration and Deployment

Supported Platforms

  • x86: 32-bit Intel/AMD processors
  • x64: 64-bit Intel/AMD processors
  • ARM64: 64-bit ARM processors

Build Modes

  • Debug: Development builds with debugging symbols
  • Release: Optimized production builds

For detailed build configuration, see UWP packaging documentation.

External Resources and Documentation

Documentation

Best Practices and Guidelines

This structure enables maintainable, scalable development while supporting the rich feature set of a modern media player application.