Add per-video volume and speed overrides#10
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughThis PR extends livepaper with per-wallpaper volume and speed overrides, persistent metadata tracking, and dynamic video scaling. LibraryService now manages sidecar files (.volume, .speed, .crashed, .whitelist, .id) and discovers .scene files as distinct library entries. PlayerHelper adds a background playlist observer that applies per-video overrides as the playlist position changes. AppSettings gains Speed and VideoScale properties. UI controls are added for global Speed and VideoScale settings, plus per-wallpaper override sliders in the preview modal. ChangesMetadata & Playback Control Expansion
Sequence DiagramsequenceDiagram
participant UI as UI / ViewModel
participant PH as PlayerHelper
participant IPC as mpv IPC Socket
participant Wallpaper as Wallpaper Process
UI->>PH: ApplyPlaylist(video_paths)
PH->>PH: Start playlist observer task
Note over PH: Persist paths to observer file
loop Every mpv playlist-pos change
Wallpaper->>IPC: playlist-pos event
IPC->>PH: (IPC subscription)
PH->>PH: ObservePathAsync()<br/>Read playlist-pos
PH->>PH: ApplyOverridesForPath()<br/>Check .volume/.speed sidecars
alt Override exists
PH->>IPC: volume={override}
IPC->>Wallpaper: Apply volume
PH->>IPC: speed={override}
IPC->>Wallpaper: Apply speed
else Fallback to global
PH->>IPC: volume={global}
IPC->>Wallpaper: Apply global volume
PH->>IPC: speed={global}
IPC->>Wallpaper: Apply global speed
end
end
UI->>PH: Stop()
PH->>PH: StopPlaylistObserver()
PH->>PH: ClearPlaylistObserverPaths()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
src/livepaper/ViewModels/WallpaperCardViewModel.cs (1)
155-162: 💤 Low valueSlider initialization defaults differ between volume and speed.
Line 159 defaults
_sliderVolumeto 0 when no override exists, but line 161 correctly defaults_sliderSpeedto 1.0 (the global default). WhileUpdateGlobalVolumeis called immediately after construction inMakeLibraryCard, initializing volume to 0 could cause brief visual inconsistency.Consider consistent initialization
The constructor could accept global defaults as parameters, or defer slider initialization until
UpdateGlobalVolume/UpdateGlobalSpeedare called. Alternatively, load settings here:- _sliderVolume = item.VolumeOverride ?? 0; + _sliderVolume = item.VolumeOverride ?? SettingsService.Load().Volume;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/livepaper/ViewModels/WallpaperCardViewModel.cs` around lines 155 - 162, The _sliderVolume field is initialized to 0 while _sliderSpeed defaults to 1.0, causing a brief UI inconsistency; update the constructor in WallpaperCardViewModel so _sliderVolume uses the same sensible global default as _sliderSpeed (pass in or read the global/default volume, or defer initialization until UpdateGlobalVolume/UpdateGlobalSpeed is invoked). Locate the initialization block setting _sliderVolume and _sliderSpeed (lines setting _volumeOverride/_sliderVolume/_speedOverride/_sliderSpeed) and change _sliderVolume to initialize from the global/default volume value (or remove its immediate assignment and ensure MakeLibraryCard calls UpdateGlobalVolume after construction to set it). Ensure references to MakeLibraryCard, UpdateGlobalVolume, and UpdateGlobalSpeed are preserved and used to guarantee consistent initial slider state.src/livepaper/Helpers/LibraryService.cs (1)
115-115: 💤 Low value
CreationTimemay be unreliable on Linux filesystems.On ext4 and other Linux filesystems,
FileInfo.CreationTimemay return the epoch (1970-01-01) if birth time isn't tracked. Consider usingLastWriteTimeas a fallback whenCreationTimeequalsDateTime.MinValueor Unix epoch.Proposed fallback
- var fi = new FileInfo(mp4); + var fi = new FileInfo(mp4); + var addedAt = fi.CreationTime; + if (addedAt.Year <= 1970) addedAt = fi.LastWriteTime;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/livepaper/Helpers/LibraryService.cs` at line 115, Replace the direct use of fi.CreationTime for setting AddedAt with a robust fallback: check if fi.CreationTime is DateTime.MinValue or equals the Unix epoch (new DateTime(1970,1,1)) and if so use fi.LastWriteTime instead; update the assignment where AddedAt = fi.CreationTime (in LibraryService.cs, inside the code that processes FileInfo fi) to perform this conditional selection so AddedAt always gets a reliable timestamp.src/livepaper/Helpers/PlayerHelper.cs (1)
996-1010: ⚡ Quick winDuplicated sidecar read helpers.
ReadVolumeOverrideandReadSpeedOverrideare identical to the private helpers inLibraryService(lines 195-209). Consider making theLibraryServiceversions internal/public to avoid duplication.Suggested approach
Make
LibraryService.ReadVolumeOverrideandLibraryService.ReadSpeedOverridepublic, then call them fromPlayerHelper:- private static int? ReadVolumeOverride(string path) - { - var sidecar = Path.ChangeExtension(path, ".volume"); - if (!File.Exists(sidecar)) return null; - try { return int.TryParse(File.ReadAllText(sidecar).Trim(), out int v) ? v : null; } - catch { return null; } - } + private static int? ReadVolumeOverride(string path) => LibraryService.ReadVolumeOverride(path);And in
LibraryService.cs:- private static int? ReadVolumeOverride(string mediaPath) + public static int? ReadVolumeOverride(string mediaPath)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/livepaper/Helpers/PlayerHelper.cs` around lines 996 - 1010, PlayerHelper contains duplicated sidecar readers (ReadVolumeOverride and ReadSpeedOverride) that are identical to helpers in LibraryService; remove these duplicates and reuse the LibraryService implementations by changing the LibraryService methods to an appropriate non-private visibility (make ReadVolumeOverride and ReadSpeedOverride internal or public), then replace the local calls in PlayerHelper with calls to LibraryService.ReadVolumeOverride(...) and LibraryService.ReadSpeedOverride(...), ensuring the namespace/using and static access are adjusted and the original parsing/exception behavior is preserved.src/livepaper/Views/MainWindow.axaml (1)
1036-1044: Consider renamingSyncToGlobalCommandtoSyncVolumeToGlobalCommandfor naming consistency.The
SyncToGlobalCommandis correctly scoped to volume only and does not affect speed overrides. However, the asymmetric naming relative toSyncSpeedToGlobalCommandcreates an unnecessary inconsistency. Renaming toSyncVolumeToGlobalCommandwould make the pattern explicit and match the speed command's convention.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/livepaper/Views/MainWindow.axaml` around lines 1036 - 1044, Rename the command currently named SyncToGlobalCommand to SyncVolumeToGlobalCommand to match the existing SyncSpeedToGlobalCommand naming pattern: update the binding on the button in MainWindow.axaml (PreviewCard.SyncToGlobalCommand) and rename the corresponding property/command declaration and any references in the PreviewCard viewmodel/class to PreviewCard.SyncVolumeToGlobalCommand so the intent (volume-only) is explicit and consistent with SyncSpeedToGlobalCommand.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/livepaper/Helpers/LibraryService.cs`:
- Line 115: Replace the direct use of fi.CreationTime for setting AddedAt with a
robust fallback: check if fi.CreationTime is DateTime.MinValue or equals the
Unix epoch (new DateTime(1970,1,1)) and if so use fi.LastWriteTime instead;
update the assignment where AddedAt = fi.CreationTime (in LibraryService.cs,
inside the code that processes FileInfo fi) to perform this conditional
selection so AddedAt always gets a reliable timestamp.
In `@src/livepaper/Helpers/PlayerHelper.cs`:
- Around line 996-1010: PlayerHelper contains duplicated sidecar readers
(ReadVolumeOverride and ReadSpeedOverride) that are identical to helpers in
LibraryService; remove these duplicates and reuse the LibraryService
implementations by changing the LibraryService methods to an appropriate
non-private visibility (make ReadVolumeOverride and ReadSpeedOverride internal
or public), then replace the local calls in PlayerHelper with calls to
LibraryService.ReadVolumeOverride(...) and
LibraryService.ReadSpeedOverride(...), ensuring the namespace/using and static
access are adjusted and the original parsing/exception behavior is preserved.
In `@src/livepaper/ViewModels/WallpaperCardViewModel.cs`:
- Around line 155-162: The _sliderVolume field is initialized to 0 while
_sliderSpeed defaults to 1.0, causing a brief UI inconsistency; update the
constructor in WallpaperCardViewModel so _sliderVolume uses the same sensible
global default as _sliderSpeed (pass in or read the global/default volume, or
defer initialization until UpdateGlobalVolume/UpdateGlobalSpeed is invoked).
Locate the initialization block setting _sliderVolume and _sliderSpeed (lines
setting _volumeOverride/_sliderVolume/_speedOverride/_sliderSpeed) and change
_sliderVolume to initialize from the global/default volume value (or remove its
immediate assignment and ensure MakeLibraryCard calls UpdateGlobalVolume after
construction to set it). Ensure references to MakeLibraryCard,
UpdateGlobalVolume, and UpdateGlobalSpeed are preserved and used to guarantee
consistent initial slider state.
In `@src/livepaper/Views/MainWindow.axaml`:
- Around line 1036-1044: Rename the command currently named SyncToGlobalCommand
to SyncVolumeToGlobalCommand to match the existing SyncSpeedToGlobalCommand
naming pattern: update the binding on the button in MainWindow.axaml
(PreviewCard.SyncToGlobalCommand) and rename the corresponding property/command
declaration and any references in the PreviewCard viewmodel/class to
PreviewCard.SyncVolumeToGlobalCommand so the intent (volume-only) is explicit
and consistent with SyncSpeedToGlobalCommand.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 487ddce8-f0f3-464c-afd3-58f1a29c5ba8
📒 Files selected for processing (9)
src/livepaper/App.axaml.cssrc/livepaper/Helpers/LibraryService.cssrc/livepaper/Helpers/PlayerHelper.cssrc/livepaper/Models/AppSettings.cssrc/livepaper/Models/LibraryItem.cssrc/livepaper/Models/WallpaperResult.cssrc/livepaper/ViewModels/MainWindowViewModel.cssrc/livepaper/ViewModels/WallpaperCardViewModel.cssrc/livepaper/Views/MainWindow.axaml
…eedOverride to LibraryService Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LibraryItem gains VolumeOverride and SpeedOverride fields - LibraryService.LoadAll reads .volume/.speed sidecars for mp4 and scene files - PlayerHelper.SetSpeed added (mpv IPC) - WallpaperCardViewModel: SliderVolume/SliderSpeed, override tracking, SyncToGlobal/SyncSpeedToGlobal commands, multi-select sync callbacks - MainWindowViewModel: OnVolumeChanged broadcasts to all cards, SyncSelectedVolume/SyncSelectedSpeed helpers, MakeLibraryCard wires up callbacks - Preview modal: volume slider + speed slider rows with per-wallpaper override and global reset button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PlayerHelper: add OnWallpaperChanged callback, ReadSpeedOverride, BakeVolumeOverride, BakeSpeedOverride - SwitchToFile: bake overrides into mpv args on cold launch; apply via IPC on hot switch; fire OnWallpaperChanged for both video and scene paths - MainWindowViewModel: register OnWallpaperChanged to track IsCurrentlyPlaying on library cards, enabling live IPC calls from sliders Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The playlist-pos observer ran only in the GUI process, so per-video volume/speed overrides stopped firing when the app closed. Now the timer daemon is also spawned for IsPlaylist sessions, and reconnects the observer to the already-running mpvpaper socket. The post-shuffle path order is saved to playlist_observer_paths.json so the pos→path mapping stays correct for shuffled playlists.
Required by WallpaperCardViewModel which references these properties. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
b009e35 to
8c0c047
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merges sunwoo101#10. Adds per-wallpaper volume/speed overrides with .volume/.speed sidecars, sliders in preview modal, daemon persistence for native playlists, global speed slider, video scale setting, and real-time IPC control.
Both sides added documentation in the same regions. Preserved sidecar table from PR sunwoo101#10 and soft-delete section from PR sunwoo101#11 in library.md. Combined per-card delete description (PR sunwoo101#11) with preview modal line (PR sunwoo101#10) in ui.md. No logic removed.
LibraryService: adopt mediaFiles approach (mp4+png) from PR sunwoo101#12 while preserving HasCrashed/IsWhitelisted/VolumeOverride/SpeedOverride fields and AddedAt logic from PR sunwoo101#10. LibraryItem: keep all fields from HEAD (IsScene, HasCrashed, IsWhitelisted, VolumeOverride, SpeedOverride); PR sunwoo101#12 only knew WorkshopId and AddedAt. MainWindowViewModel: merged _videoScale (PR sunwoo101#10) with card layout props (PR sunwoo101#12); kept OnSpeedChanged handler from PR sunwoo101#10; adopted efficient _currentlyPlayingCard tracking from PR sunwoo101#12; kept all settings change handlers. WallpaperCardViewModel: preserved volume/speed override logic from PR sunwoo101#10 which PR sunwoo101#12 was unaware of.
ui.md: placed GIF hover note before status bar line (both additions kept). LibraryService: adopted PR sunwoo101#13 inline thumbnail call; preserved ParseWorkshopId/ReadVolumeOverride/ReadSpeedOverride methods from PR sunwoo101#10 which PR sunwoo101#13 had removed. LibraryItem: dropped PR sunwoo101#13 duplicate IsScene field (already declared); kept HasCrashed/IsWhitelisted/VolumeOverride/SpeedOverride from PR sunwoo101#10. MainWindowViewModel: added LoadStaticThumbnailAsync call alongside volume/speed wiring in MakeLibraryCard. WallpaperCardViewModel: merged imports (IO, Dispatcher from PR sunwoo101#13); removed duplicate IsScene/WorkshopId props from HEAD; kept PR sunwoo101#10 field init block alongside PR sunwoo101#13 GIF cache pre-population and new methods.
ui.md: merged Browse tab — kept GIF hover note (PR sunwoo101#13) plus Auto-search and Sort button lines (PR sunwoo101#14); merged Library tab — kept soft-delete/keyboard shortcuts (PR sunwoo101#11) alongside workshop ID copy button (PR sunwoo101#14) in preview modal. WallpaperResult: added AddedAt field from PR sunwoo101#14. WallpaperEngineScraper: integrated scene detection and addedAt from PR sunwoo101#14; added FindThumbnail sync helper (PR sunwoo101#14 had it, PR sunwoo101#13 replaced it with async); kept AnimatedThumbnailUrl (PR sunwoo101#13) and added AddedAt to video results; added sort cases 3-6; removed redundant post-loop query filter (already done inline). WallpaperCardViewModel: kept all GIF animation code from PR sunwoo101#13 that PR sunwoo101#14 was unaware of; kept volume/speed fields from PR sunwoo101#10; PR sunwoo101#14 added WorkshopId display only. MainWindow.axaml: kept volume/speed slider rows at row 1/2 (PR sunwoo101#10) then placed PR sunwoo101#14 title+WorkshopId header at row 3 (correct grid row offset).
ui.md: kept soft-delete/keyboard shortcuts (PR sunwoo101#11) and added right-click context menu lines (PR sunwoo101#15) to Library tab; kept GIF hover note (PR sunwoo101#13) and added playlist right-click menu (PR sunwoo101#15); kept status bar line. WallpaperCardViewModel: merged imports (System.Diagnostics from PR sunwoo101#15 alongside Avalonia.Threading from PR sunwoo101#13); kept GIF animation code (PR sunwoo101#13); added IsLocalSource property (PR sunwoo101#15); removed ghost conflict marker. MainWindowViewModel: kept volume/speed/GIF wiring (PR sunwoo101#10/PR sunwoo101#13) plus added OnOpenSettings callback (PR sunwoo101#15) in MakeLibraryCard. MainWindow.axaml: browse card and playlist card — kept PointerEntered/Exited handlers (PR sunwoo101#13 GIF hover) AND added context menus (PR sunwoo101#15).
session.md: kept VideoScale setting (PR sunwoo101#10) and added AutoMuteOnlyIfMprisActive to auto-mute settings (PR sunwoo101#16). AppSettings: kept ThumbnailAspect/CardSize/AutoPlayGifs/LibrarySortIndex (PR sunwoo101#12/PR sunwoo101#13) and added AutoMuteOnlyIfMprisActive (PR sunwoo101#16).
session.md: kept VideoScale and AutoMuteOnlyIfMprisActive (PR sunwoo101#10/PR sunwoo101#16); added GlobalWaitForVideoEnd to global rotation settings (PR sunwoo101#17). ui.md: kept GIF hover (PR sunwoo101#13), right-click menus (PR sunwoo101#15), Speed in Playback (PR sunwoo101#10); added Wait for video to end to Playlist settings (PR sunwoo101#17). PlayerHelper: dropped duplicate _observerCts field (ghost from previous merge); added _waitForVideoEnd/_advanceOnVideoEnd/_waitingForVideoEnd/_waitCts (PR sunwoo101#17); fixed ApplyPlaylist to use shuffled paths array and not double-shuffle via mpv flag; added SetLoop and SetPlaylistShuffle IPC helpers (PR sunwoo101#17); resolved duplicate StartPlaylistObserver/StopPlaylistObserver/ObservePathAsync/ApplyOverridesForPath by keeping PR sunwoo101#17 insertions with HEAD's full ApplyOverridesForPath (volume+speed overrides) and OnWallpaperChanged invoke; removed duplicate HEAD methods. MainWindowViewModel: kept _autoMuteOnlyIfMprisActive (PR sunwoo101#16); added _speed=1.0 default (PR sunwoo101#17) to existing declaration; added _playlistSyncCts (PR sunwoo101#17); dropped duplicate _currentlyPlayingCard; adopted PR sunwoo101#17 concise OnWallpaperChanged + RefreshPlayingStatus; kept OnVolumeChanged/OnSpeedChanged with per-card override logic (PR sunwoo101#10); kept LastSession with PR sunwoo101#17 new fields. MainWindow.axaml: kept Appearance section (thumbnail/card/gif settings from PR sunwoo101#12/PR sunwoo101#13) that PR sunwoo101#17 was unaware of.
library.md: updated heading to include scene; added volume/speed sidecars (PR sunwoo101#10) alongside scene-specific sidecars (PR sunwoo101#18); kept all sections. session.md: merged WE settings — AllowScenes, LWE config (PR sunwoo101#18) alongside ThumbnailAspect/CardSize/LibrarySortIndex (PR sunwoo101#12/PR sunwoo101#13). ui.md: Browse tab kept GIF hover + auto-search + sort + SCENE badge; Library tab kept full ItemsRepeater grid + all features + SCENE/crash badges; Settings kept Video scale + WE scene support + Appearance section. README: added both playerctl and linux-wallpaperengine to optional deps. LibraryItem: added CopiedSceneDir (PR sunwoo101#18) + kept VolumeOverride/SpeedOverride. AppSettings: kept Speed with clamping; kept PlaylistWaitForVideoEnd; dropped duplicate LibrarySortIndex from PR sunwoo101#18. WallpaperResult: kept AddedAt (PR sunwoo101#14). livepaper.csproj: kept ItemsRepeater + AnimatedImage packages. AudioMonitor: kept async IsAnyMprisPlayerActiveAsync + better playerctl args. DownloadHelper: kept smart thumbnail extension detection. LibraryService: merged scene-copy Trash/Restore/Delete + kept volume/speed. WallpaperEngineScraper: merged all, kept AnimatedThumbnailUrl + AddedAt. WallpaperEngineService: kept SortIndex/SupportsSorting. PlayerHelper: merged 29 conflicts — kept wait/advance/playlist observer from PR sunwoo101#17 + added full scene/LWE support from PR sunwoo101#18; removed duplicates. MainWindowViewModel: merged 25 conflicts — kept all playlist wait/advance proxies from PR sunwoo101#17 + added scene-aware methods from PR sunwoo101#18. WallpaperCardViewModel: kept full GIF+volume/speed code + added scene-aware fields and LoadDurationAsync from PR sunwoo101#18. MainWindow.axaml: kept GIF animation, volume/speed sliders; added SCENE badge and crash warning panel (adjusting grid row offsets). MainWindow.axaml.cs: kept HEAD's existing TextBox guard structure.
Summary
.volume/.speedsidecar files alongside the videoHow overrides work
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes