feat: add video auto play setting (#20416)
* feat: add auto play setting to mobile * feat: add auto play video setting to web * address review comments * fix setting id --------- Co-authored-by: Saschl <noreply@saschl.com>pull/22956/head
parent
7d0228a159
commit
9b5855f848
|
|
@ -1807,6 +1807,8 @@
|
||||||
"setting_notifications_subtitle": "Adjust your notification preferences",
|
"setting_notifications_subtitle": "Adjust your notification preferences",
|
||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
|
"setting_video_viewer_auto_play_subtitle": "Automatically start playing videos when they are opened",
|
||||||
|
"setting_video_viewer_auto_play_title": "Auto play videos",
|
||||||
"setting_video_viewer_looping_title": "Looping",
|
"setting_video_viewer_looping_title": "Looping",
|
||||||
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
"setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.",
|
||||||
"setting_video_viewer_original_video_title": "Force original video",
|
"setting_video_viewer_original_video_title": "Force original video",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ enum Setting<T> {
|
||||||
showStorageIndicator<bool>(StoreKey.storageIndicator, true),
|
showStorageIndicator<bool>(StoreKey.storageIndicator, true),
|
||||||
loadOriginal<bool>(StoreKey.loadOriginal, false),
|
loadOriginal<bool>(StoreKey.loadOriginal, false),
|
||||||
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false),
|
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false),
|
||||||
|
autoPlayVideo<bool>(StoreKey.autoPlayVideo, true),
|
||||||
preferRemoteImage<bool>(StoreKey.preferRemoteImage, false),
|
preferRemoteImage<bool>(StoreKey.preferRemoteImage, false),
|
||||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
|
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
|
||||||
enableBackup<bool>(StoreKey.enableBackup, false);
|
enableBackup<bool>(StoreKey.enableBackup, false);
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,8 @@ enum StoreKey<T> {
|
||||||
// Read-only Mode settings
|
// Read-only Mode settings
|
||||||
readonlyModeEnabled<bool>._(138),
|
readonlyModeEnabled<bool>._(138),
|
||||||
|
|
||||||
|
autoPlayVideo<bool>._(139),
|
||||||
|
|
||||||
// Experimental stuff
|
// Experimental stuff
|
||||||
photoManagerCustomFilter<bool>._(1000),
|
photoManagerCustomFilter<bool>._(1000),
|
||||||
betaPromptShown<bool>._(1001),
|
betaPromptShown<bool>._(1001),
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,10 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
isVideoReady.value = true;
|
isVideoReady.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await videoController.play();
|
final autoPlayVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.autoPlayVideo);
|
||||||
|
if (autoPlayVideo) {
|
||||||
|
await videoController.play();
|
||||||
|
}
|
||||||
await videoController.setVolume(0.9);
|
await videoController.setVolume(0.9);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.severe('Error playing video: $error');
|
log.severe('Error playing video: $error');
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/setting.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
|
|
@ -218,7 +219,10 @@ class NativeVideoViewer extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await videoController.play();
|
final autoPlayVideo = AppSetting.get(Setting.autoPlayVideo);
|
||||||
|
if (autoPlayVideo) {
|
||||||
|
await videoController.play();
|
||||||
|
}
|
||||||
await videoController.setVolume(0.9);
|
await videoController.setVolume(0.9);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.severe('Error playing video: $error');
|
log.severe('Error playing video: $error');
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ enum AppSettingsEnum<T> {
|
||||||
preferRemoteImage<bool>(StoreKey.preferRemoteImage, null, false),
|
preferRemoteImage<bool>(StoreKey.preferRemoteImage, null, false),
|
||||||
loopVideo<bool>(StoreKey.loopVideo, "loopVideo", true),
|
loopVideo<bool>(StoreKey.loopVideo, "loopVideo", true),
|
||||||
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, "loadOriginalVideo", false),
|
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, "loadOriginalVideo", false),
|
||||||
|
autoPlayVideo<bool>(StoreKey.autoPlayVideo, "autoPlayVideo", true),
|
||||||
mapThemeMode<int>(StoreKey.mapThemeMode, null, 0),
|
mapThemeMode<int>(StoreKey.mapThemeMode, null, 0),
|
||||||
mapShowFavoriteOnly<bool>(StoreKey.mapShowFavoriteOnly, null, false),
|
mapShowFavoriteOnly<bool>(StoreKey.mapShowFavoriteOnly, null, false),
|
||||||
mapIncludeArchived<bool>(StoreKey.mapIncludeArchived, null, false),
|
mapIncludeArchived<bool>(StoreKey.mapIncludeArchived, null, false),
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,18 @@ class VideoViewerSettings extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final useLoopVideo = useAppSettingsState(AppSettingsEnum.loopVideo);
|
final useLoopVideo = useAppSettingsState(AppSettingsEnum.loopVideo);
|
||||||
final useOriginalVideo = useAppSettingsState(AppSettingsEnum.loadOriginalVideo);
|
final useOriginalVideo = useAppSettingsState(AppSettingsEnum.loadOriginalVideo);
|
||||||
|
final useAutoPlayVideo = useAppSettingsState(AppSettingsEnum.autoPlayVideo);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SettingsSubTitle(title: "videos".tr()),
|
SettingsSubTitle(title: "videos".tr()),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
valueNotifier: useAutoPlayVideo,
|
||||||
|
title: "setting_video_viewer_auto_play_title".tr(),
|
||||||
|
subtitle: "setting_video_viewer_auto_play_subtitle".tr(),
|
||||||
|
onChanged: (_) => ref.invalidate(appSettingsServiceProvider),
|
||||||
|
),
|
||||||
SettingsSwitchListTile(
|
SettingsSwitchListTile(
|
||||||
valueNotifier: useLoopVideo,
|
valueNotifier: useLoopVideo,
|
||||||
title: "setting_video_viewer_looping_title".tr(),
|
title: "setting_video_viewer_looping_title".tr(),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,12 @@
|
||||||
import { assetViewerFadeDuration } from '$lib/constants';
|
import { assetViewerFadeDuration } from '$lib/constants';
|
||||||
import { castManager } from '$lib/managers/cast-manager.svelte';
|
import { castManager } from '$lib/managers/cast-manager.svelte';
|
||||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||||
import { loopVideo as loopVideoPreference, videoViewerMuted, videoViewerVolume } from '$lib/stores/preferences.store';
|
import {
|
||||||
|
autoPlayVideo,
|
||||||
|
loopVideo as loopVideoPreference,
|
||||||
|
videoViewerMuted,
|
||||||
|
videoViewerVolume,
|
||||||
|
} from '$lib/stores/preferences.store';
|
||||||
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||||
import { AssetMediaSize } from '@immich/sdk';
|
import { AssetMediaSize } from '@immich/sdk';
|
||||||
import { LoadingSpinner } from '@immich/ui';
|
import { LoadingSpinner } from '@immich/ui';
|
||||||
|
|
@ -125,7 +130,7 @@
|
||||||
<video
|
<video
|
||||||
bind:this={videoPlayer}
|
bind:this={videoPlayer}
|
||||||
loop={$loopVideoPreference && loopVideo}
|
loop={$loopVideoPreference && loopVideo}
|
||||||
autoplay
|
autoplay={$autoPlayVideo}
|
||||||
playsinline
|
playsinline
|
||||||
controls
|
controls
|
||||||
class="h-full object-contain"
|
class="h-full object-contain"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { assetViewerFadeDuration } from '$lib/constants';
|
import { assetViewerFadeDuration } from '$lib/constants';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
|
import { autoPlayVideo } from '$lib/stores/preferences.store';
|
||||||
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||||
import { AssetMediaSize } from '@immich/sdk';
|
import { AssetMediaSize } from '@immich/sdk';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
@ -27,7 +28,7 @@
|
||||||
<div class="h-full w-full bg-pink-9000" transition:fade={{ duration: assetViewerFadeDuration }}>
|
<div class="h-full w-full bg-pink-9000" transition:fade={{ duration: assetViewerFadeDuration }}>
|
||||||
<video
|
<video
|
||||||
bind:this={videoPlayer}
|
bind:this={videoPlayer}
|
||||||
autoplay
|
autoplay={$autoPlayVideo}
|
||||||
playsinline
|
playsinline
|
||||||
class="h-full w-full rounded-2xl object-contain transition-all"
|
class="h-full w-full rounded-2xl object-contain transition-all"
|
||||||
src={getAssetPlaybackUrl({ id: asset.id })}
|
src={getAssetPlaybackUrl({ id: asset.id })}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
import { themeManager } from '$lib/managers/theme-manager.svelte';
|
import { themeManager } from '$lib/managers/theme-manager.svelte';
|
||||||
import {
|
import {
|
||||||
alwaysLoadOriginalFile,
|
alwaysLoadOriginalFile,
|
||||||
|
autoPlayVideo,
|
||||||
locale,
|
locale,
|
||||||
loopVideo,
|
loopVideo,
|
||||||
playVideoThumbnailOnHover,
|
playVideoThumbnailOnHover,
|
||||||
|
|
@ -108,6 +109,13 @@
|
||||||
bind:checked={$playVideoThumbnailOnHover}
|
bind:checked={$playVideoThumbnailOnHover}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ms-4">
|
||||||
|
<SettingSwitch
|
||||||
|
title={$t('setting_video_viewer_auto_play_title')}
|
||||||
|
subtitle={$t('setting_video_viewer_auto_play_subtitle')}
|
||||||
|
bind:checked={$autoPlayVideo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="ms-4">
|
<div class="ms-4">
|
||||||
<SettingSwitch title={$t('loop_videos')} subtitle={$t('loop_videos_description')} bind:checked={$loopVideo} />
|
<SettingSwitch title={$t('loop_videos')} subtitle={$t('loop_videos_description')} bind:checked={$loopVideo} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -146,4 +146,6 @@ export const playVideoThumbnailOnHover = persisted<boolean>('play-video-thumbnai
|
||||||
|
|
||||||
export const loopVideo = persisted<boolean>('loop-video', true, {});
|
export const loopVideo = persisted<boolean>('loop-video', true, {});
|
||||||
|
|
||||||
|
export const autoPlayVideo = persisted<boolean>('auto-play-video', true, {});
|
||||||
|
|
||||||
export const recentAlbumsDropdown = persisted<boolean>('recent-albums-open', true, {});
|
export const recentAlbumsDropdown = persisted<boolean>('recent-albums-open', true, {});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue