mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat: support iOS LivePhoto backup (#950)
This commit is contained in:
parent
83e2cabbcc
commit
8bc64be77b
30 changed files with 678 additions and 243 deletions
|
|
@ -15,15 +15,26 @@ import 'package:video_player/video_player.dart';
|
|||
// ignore: must_be_immutable
|
||||
class VideoViewerPage extends HookConsumerWidget {
|
||||
final Asset asset;
|
||||
final bool isMotionVideo;
|
||||
final VoidCallback onVideoEnded;
|
||||
|
||||
const VideoViewerPage({Key? key, required this.asset}) : super(key: key);
|
||||
const VideoViewerPage({
|
||||
Key? key,
|
||||
required this.asset,
|
||||
required this.isMotionVideo,
|
||||
required this.onVideoEnded,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
if (asset.isLocal) {
|
||||
final AsyncValue<File> videoFile = ref.watch(_fileFamily(asset.local!));
|
||||
return videoFile.when(
|
||||
data: (data) => VideoThumbnailPlayer(file: data),
|
||||
data: (data) => VideoThumbnailPlayer(
|
||||
file: data,
|
||||
isMotionVideo: false,
|
||||
onVideoEnded: () {},
|
||||
),
|
||||
error: (error, stackTrace) => Icon(
|
||||
Icons.image_not_supported_outlined,
|
||||
color: Theme.of(context).primaryColor,
|
||||
|
|
@ -41,14 +52,17 @@ class VideoViewerPage extends HookConsumerWidget {
|
|||
ref.watch(imageViewerStateProvider).downloadAssetStatus;
|
||||
final box = Hive.box(userInfoBox);
|
||||
final String jwtToken = box.get(accessTokenKey);
|
||||
final String videoUrl =
|
||||
'${box.get(serverEndpointKey)}/asset/file/${asset.id}';
|
||||
final String videoUrl = isMotionVideo
|
||||
? '${box.get(serverEndpointKey)}/asset/file/${asset.remote?.livePhotoVideoId!}'
|
||||
: '${box.get(serverEndpointKey)}/asset/file/${asset.id}';
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
VideoThumbnailPlayer(
|
||||
url: videoUrl,
|
||||
jwtToken: jwtToken,
|
||||
isMotionVideo: isMotionVideo,
|
||||
onVideoEnded: onVideoEnded,
|
||||
),
|
||||
if (downloadAssetStatus == DownloadAssetStatus.loading)
|
||||
const Center(
|
||||
|
|
@ -72,9 +86,17 @@ class VideoThumbnailPlayer extends StatefulWidget {
|
|||
final String? url;
|
||||
final String? jwtToken;
|
||||
final File? file;
|
||||
final bool isMotionVideo;
|
||||
final VoidCallback onVideoEnded;
|
||||
|
||||
const VideoThumbnailPlayer({Key? key, this.url, this.jwtToken, this.file})
|
||||
: super(key: key);
|
||||
const VideoThumbnailPlayer({
|
||||
Key? key,
|
||||
this.url,
|
||||
this.jwtToken,
|
||||
this.file,
|
||||
required this.onVideoEnded,
|
||||
required this.isMotionVideo,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VideoThumbnailPlayer> createState() => _VideoThumbnailPlayerState();
|
||||
|
|
@ -88,6 +110,13 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
initializePlayer();
|
||||
|
||||
videoPlayerController.addListener(() {
|
||||
if (videoPlayerController.value.position ==
|
||||
videoPlayerController.value.duration) {
|
||||
widget.onVideoEnded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> initializePlayer() async {
|
||||
|
|
@ -115,7 +144,7 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
|||
autoPlay: true,
|
||||
autoInitialize: true,
|
||||
allowFullScreen: true,
|
||||
showControls: true,
|
||||
showControls: !widget.isMotionVideo,
|
||||
hideControlsTimer: const Duration(seconds: 5),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue