Download asset to local and error fixing (#100)

* Update photo_manager pub package
* Added download endpoint for assets
* Successfully save a photo to the local device's gallery
* Save save a video to the local device's gallery
* Fixed #97
* Added download loading indicator
* Refactor and increase the font size for curated search thumbnail images
* Reposition loading animation on the search result page
This commit is contained in:
Alex
2022-04-02 12:31:53 -05:00
committed by GitHub
parent 60df387459
commit 90ef64efa3
34 changed files with 538 additions and 257 deletions

View File

@@ -10,6 +10,8 @@ class ImmichAsset {
final String modifiedAt;
final bool isFavorite;
final String? duration;
final String originalPath;
final String resizePath;
ImmichAsset({
required this.id,
@@ -21,6 +23,8 @@ class ImmichAsset {
required this.modifiedAt,
required this.isFavorite,
this.duration,
required this.originalPath,
required this.resizePath,
});
ImmichAsset copyWith({
@@ -33,6 +37,8 @@ class ImmichAsset {
String? modifiedAt,
bool? isFavorite,
String? duration,
String? originalPath,
String? resizePath,
}) {
return ImmichAsset(
id: id ?? this.id,
@@ -44,6 +50,8 @@ class ImmichAsset {
modifiedAt: modifiedAt ?? this.modifiedAt,
isFavorite: isFavorite ?? this.isFavorite,
duration: duration ?? this.duration,
originalPath: originalPath ?? this.originalPath,
resizePath: resizePath ?? this.resizePath,
);
}
@@ -58,6 +66,8 @@ class ImmichAsset {
'modifiedAt': modifiedAt,
'isFavorite': isFavorite,
'duration': duration,
'originalPath': originalPath,
'resizePath': resizePath,
};
}
@@ -72,6 +82,8 @@ class ImmichAsset {
modifiedAt: map['modifiedAt'] ?? '',
isFavorite: map['isFavorite'] ?? false,
duration: map['duration'],
originalPath: map['originalPath'] ?? '',
resizePath: map['resizePath'] ?? '',
);
}
@@ -81,7 +93,7 @@ class ImmichAsset {
@override
String toString() {
return 'ImmichAsset(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, type: $type, createdAt: $createdAt, modifiedAt: $modifiedAt, isFavorite: $isFavorite, duration: $duration)';
return 'ImmichAsset(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, type: $type, createdAt: $createdAt, modifiedAt: $modifiedAt, isFavorite: $isFavorite, duration: $duration, originalPath: $originalPath, resizePath: $resizePath)';
}
@override
@@ -97,7 +109,9 @@ class ImmichAsset {
other.createdAt == createdAt &&
other.modifiedAt == modifiedAt &&
other.isFavorite == isFavorite &&
other.duration == duration;
other.duration == duration &&
other.originalPath == originalPath &&
other.resizePath == resizePath;
}
@override
@@ -110,6 +124,8 @@ class ImmichAsset {
createdAt.hashCode ^
modifiedAt.hashCode ^
isFavorite.hashCode ^
duration.hashCode;
duration.hashCode ^
originalPath.hashCode ^
resizePath.hashCode;
}
}

View File

@@ -73,7 +73,7 @@ class BackupService {
});
// Build thumbnail multipart data
var thumbnailData = await entity.thumbDataWithSize(1280, 720);
var thumbnailData = await entity.thumbnailDataWithSize(const ThumbnailSize(720, 1280));
if (thumbnailData != null) {
thumbnailUploadData = MultipartFile.fromBytes(
List.from(thumbnailData),

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
@@ -25,16 +26,36 @@ class NetworkService {
}
}
Future<dynamic> getRequest({required String url}) async {
Future<dynamic> getRequest({required String url, bool isByteResponse = false, bool isStreamReponse = false}) async {
try {
var dio = Dio();
dio.interceptors.add(AuthenticatedRequestInterceptor());
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
Response res = await dio.get('$savedEndpoint/$url');
if (res.statusCode == 200) {
return res;
if (isByteResponse) {
Response<List<int>> res = await dio.get<List<int>>(
'$savedEndpoint/$url',
options: Options(responseType: ResponseType.bytes),
);
if (res.statusCode == 200) {
return res;
}
} else if (isStreamReponse) {
Response<ResponseBody> res = await dio.get<ResponseBody>(
'$savedEndpoint/$url',
options: Options(responseType: ResponseType.stream),
);
if (res.statusCode == 200) {
return res;
}
} else {
Response res = await dio.get('$savedEndpoint/$url');
if (res.statusCode == 200) {
return res;
}
}
} on DioError catch (e) {
debugPrint("DioError: ${e.response}");

View File

@@ -8,12 +8,24 @@ class ImmichToast {
required BuildContext context,
required String msg,
ToastType toastType = ToastType.info,
ToastGravity gravity = ToastGravity.TOP,
}) {
FToast fToast;
fToast = FToast();
fToast.init(context);
_getColor(ToastType type, BuildContext context) {
switch (type) {
case ToastType.info:
return Theme.of(context).primaryColor;
case ToastType.success:
return const Color.fromARGB(255, 78, 140, 124);
case ToastType.error:
return const Color.fromARGB(255, 220, 48, 85);
}
}
fToast.showToast(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
@@ -36,8 +48,8 @@ class ImmichToast {
: Container(),
(toastType == ToastType.success)
? const Icon(
Icons.check,
color: Color.fromARGB(255, 104, 248, 140),
Icons.check_circle_rounded,
color: Color.fromARGB(255, 78, 140, 124),
)
: Container(),
(toastType == ToastType.error)
@@ -53,7 +65,7 @@ class ImmichToast {
child: Text(
msg,
style: TextStyle(
color: Theme.of(context).primaryColor,
color: _getColor(toastType, context),
fontWeight: FontWeight.bold,
fontSize: 15,
),
@@ -62,7 +74,7 @@ class ImmichToast {
],
),
),
gravity: ToastGravity.TOP,
gravity: gravity,
toastDuration: const Duration(seconds: 2),
);
}

View File

@@ -1,106 +0,0 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
class VideoViewerPage extends StatelessWidget {
final String videoUrl;
const VideoViewerPage({Key? key, required this.videoUrl}) : super(key: key);
@override
Widget build(BuildContext context) {
String jwtToken = Hive.box(userInfoBox).get(accessTokenKey);
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle.light,
backgroundColor: Colors.black,
leading: IconButton(
onPressed: () {
AutoRouter.of(context).pop();
},
icon: const Icon(Icons.arrow_back_ios)),
),
body: SafeArea(
child: VideoThumbnailPlayer(
url: videoUrl,
jwtToken: jwtToken,
),
),
);
}
}
class VideoThumbnailPlayer extends StatefulWidget {
final String url;
final String? jwtToken;
const VideoThumbnailPlayer({Key? key, required this.url, this.jwtToken}) : super(key: key);
@override
State<VideoThumbnailPlayer> createState() => _VideoThumbnailPlayerState();
}
class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
late VideoPlayerController videoPlayerController;
ChewieController? chewieController;
@override
void initState() {
super.initState();
initializePlayer();
}
Future<void> initializePlayer() async {
try {
videoPlayerController =
VideoPlayerController.network(widget.url, httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"});
await videoPlayerController.initialize();
_createChewieController();
setState(() {});
} catch (e) {
debugPrint("ERROR initialize video player");
}
}
_createChewieController() {
chewieController = ChewieController(
showOptions: true,
showControlsOnInitialize: false,
videoPlayerController: videoPlayerController,
autoPlay: true,
autoInitialize: false,
);
}
@override
void dispose() {
super.dispose();
videoPlayerController.pause();
videoPlayerController.dispose();
chewieController?.dispose();
}
@override
Widget build(BuildContext context) {
return chewieController != null && chewieController!.videoPlayerController.value.isInitialized
? SizedBox(
child: Chewie(
controller: chewieController!,
),
)
: const SizedBox(
width: 75,
height: 75,
child: CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
);
}
}