Files
plezy/lib/screens/video_player/parts/shader.dart
T
edde746 352b88109b refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
2026-07-26 06:09:48 +02:00

140 lines
5.2 KiB
Dart

part of '../../video_player_screen.dart';
extension _VideoPlayerShaderMethods on VideoPlayerScreenState {
/// Apply the saved shader preset on playback start.
/// Reads directly from SettingsService (synchronous SharedPreferences) to
/// avoid a race with ShaderProvider's async initialization.
Future<void> _applySavedShaderPreset() async {
if (_shaderService == null || !_shaderService!.isSupported) return;
try {
final shaderProvider = context.read<ShaderProvider>();
final settings = await SettingsService.getInstance();
final presetId = settings.read(SettingsService.globalShaderPreset);
final preset =
(shaderProvider.initialized ? shaderProvider.findPresetById(presetId) : ShaderPreset.fromId(presetId)) ??
ShaderPreset.none;
await _shaderService!.applyPreset(preset);
if (!mounted) return;
shaderProvider.setCurrentPreset(preset);
} catch (e) {
appLogger.d('Could not apply shader preset', error: e);
}
}
/// Enable ambient lighting for the current video/player geometry.
/// Returns false when the aspect ratios cannot be determined yet.
Future<bool> _enableAmbientLighting(AmbientLightingService ambientLighting, ShaderProvider shaderProvider) async {
// Get video display aspect ratio
final dwidth = await player?.getProperty('dwidth');
final dheight = await player?.getProperty('dheight');
if (dwidth == null || dheight == null) return false;
final w = double.tryParse(dwidth);
final h = double.tryParse(dheight);
if (w == null || h == null || h == 0) return false;
final videoAspect = w / h;
// Get player widget aspect ratio
final playerSize = _videoFilterManager?.playerSize;
if (playerSize == null || playerSize.height == 0) return false;
final outputAspect = playerSize.width / playerSize.height;
// Clear shaders — ambient lighting and shaders are mutually exclusive
if (shaderProvider.isShaderEnabled) {
await _shaderService!.applyPreset(ShaderPreset.none);
shaderProvider.setCurrentPreset(ShaderPreset.none);
}
// Force contain mode when enabling ambient lighting
_videoFilterManager?.resetToContain();
await ambientLighting.enable(videoAspect, outputAspect);
return true;
}
/// Restore ambient lighting from persisted setting
Future<void> _restoreAmbientLighting() async {
if (!mounted) return;
final shaderProvider = context.read<ShaderProvider>();
final settings = await SettingsService.getInstance();
if (!mounted) return;
if (!settings.read(SettingsService.ambientLighting)) return;
final ambientLighting = _ambientLightingService;
if (ambientLighting == null || !ambientLighting.isSupported) return;
if (!await _enableAmbientLighting(ambientLighting, shaderProvider)) return;
if (mounted) _setPlayerState(() {});
}
/// Cycle through BoxFit modes: contain → cover → fill → contain (for button)
void _cycleBoxFitMode() {
// Disable ambient lighting when switching boxfit modes
// (cover/fill change the video rect, making the baked-in shader incorrect)
_ambientLightingService?.disable();
_setPlayerState(() {
_videoFilterManager?.cycleBoxFitMode();
});
}
void _showZoomToast(double zoomScale) {
_toastController.show(Symbols.zoom_in_rounded, t.videoControls.zoomPercent(percent: (zoomScale * 100).round()));
}
double _setVideoZoom(double zoomScale, {bool showToast = true}) {
final filterManager = _videoFilterManager;
if (filterManager == null) return 1.0;
_ambientLightingService?.disable();
final next = filterManager.setZoomScale(zoomScale);
if (showToast) _showZoomToast(next);
if (mounted) _setPlayerState(() {});
return next;
}
void _zoomVideoIn() {
final current = _videoFilterManager?.zoomScale ?? 1.0;
_setVideoZoom(current + VideoFilterManager.zoomStep);
}
void _zoomVideoOut() {
final current = _videoFilterManager?.zoomScale ?? 1.0;
_setVideoZoom(current - VideoFilterManager.zoomStep);
}
void _resetVideoZoom() {
_setVideoZoom(1.0);
}
/// Update video-aspect-override when player size changes.
/// The shader adapts automatically via built-in target_size uniform.
void _updateAmbientLightingOnResize(Size newSize) {
final ambientLighting = _ambientLightingService;
if (ambientLighting == null || !ambientLighting.isEnabled) return;
if (newSize.height == 0) return;
ambientLighting.updateOutputAspect(newSize.width / newSize.height);
}
/// Toggle ambient lighting effect on/off
Future<void> _toggleAmbientLighting() async {
final ambientLighting = _ambientLightingService;
if (ambientLighting == null || !ambientLighting.isSupported) return;
final shaderProvider = context.read<ShaderProvider>();
if (ambientLighting.isEnabled) {
await ambientLighting.disable();
unawaited(_videoFilterManager?.updateVideoFilter());
} else {
if (!await _enableAmbientLighting(ambientLighting, shaderProvider)) return;
}
// Persist ambient lighting state
final settings = await SettingsService.getInstance();
unawaited(settings.write(SettingsService.ambientLighting, ambientLighting.isEnabled));
if (mounted) _setPlayerState(() {});
}
}