mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-06-25 02:54:07 +00:00
use media id as filename
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
This commit is contained in:
parent
f14a94d639
commit
4c42d55afd
7 changed files with 111 additions and 21 deletions
|
|
@ -1,5 +1,10 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.3.3
|
||||||
|
|
||||||
|
- Fix: Multiple UI issues
|
||||||
|
- Fix: Camera initialization issue
|
||||||
|
|
||||||
## 0.3.2
|
## 0.3.2
|
||||||
|
|
||||||
- Fix: Multiple smaller performance and UI issues
|
- Fix: Multiple smaller performance and UI issues
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import 'package:twonly/src/utils/startup_guard.dart';
|
||||||
|
|
||||||
final _initMutex = Mutex();
|
final _initMutex = Mutex();
|
||||||
|
|
||||||
/// This function is used to initialized the absolute minimum so it
|
/// This function is used to initialize the absolute minimum so it
|
||||||
/// can also be used by the backend without the UI was loaded.
|
/// can also be used by the backend without the UI was loaded.
|
||||||
Future<bool> twonlyMinimumInitialization() async {
|
Future<bool> twonlyMinimumInitialization() async {
|
||||||
Log.info('twonlyMinimumInitialization: called');
|
Log.info('twonlyMinimumInitialization: called');
|
||||||
|
|
|
||||||
|
|
@ -314,11 +314,15 @@ class MediaFileService {
|
||||||
await tempPath.copy(storedPath.path);
|
await tempPath.copy(storedPath.path);
|
||||||
if (userService.currentUser.storeMediaFilesInGallery) {
|
if (userService.currentUser.storeMediaFilesInGallery) {
|
||||||
if (mediaFile.type == MediaType.video) {
|
if (mediaFile.type == MediaType.video) {
|
||||||
await saveVideoToGallery(storedPath.path);
|
await saveVideoToGallery(
|
||||||
|
storedPath.path,
|
||||||
|
name: mediaFile.mediaId,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await saveImageToGallery(
|
await saveImageToGallery(
|
||||||
storedPath.readAsBytesSync(),
|
storedPath.readAsBytesSync(),
|
||||||
createdAt: mediaFile.createdAt,
|
createdAt: mediaFile.createdAt,
|
||||||
|
name: mediaFile.mediaId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import 'package:gal/gal.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
|
|
@ -36,6 +37,7 @@ extension ShortCutsExtension on BuildContext {
|
||||||
Future<String?> saveImageToGallery(
|
Future<String?> saveImageToGallery(
|
||||||
Uint8List imageBytes, {
|
Uint8List imageBytes, {
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
|
String? name,
|
||||||
}) async {
|
}) async {
|
||||||
var bytesToProcess = imageBytes;
|
var bytesToProcess = imageBytes;
|
||||||
|
|
||||||
|
|
@ -75,7 +77,7 @@ Future<String?> saveImageToGallery(
|
||||||
await Gal.requestAccess(toAlbum: true);
|
await Gal.requestAccess(toAlbum: true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await Gal.putImageBytes(jpgImages, album: 'twonly');
|
await Gal.putImageBytes(jpgImages, album: 'twonly', name: name ?? 'image');
|
||||||
return null;
|
return null;
|
||||||
} on GalException catch (e) {
|
} on GalException catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
|
|
@ -83,17 +85,45 @@ Future<String?> saveImageToGallery(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> saveVideoToGallery(String videoPath) async {
|
Future<String?> saveVideoToGallery(
|
||||||
|
String videoPath, {
|
||||||
|
String? name,
|
||||||
|
}) async {
|
||||||
final hasAccess = await Gal.hasAccess(toAlbum: true);
|
final hasAccess = await Gal.hasAccess(toAlbum: true);
|
||||||
if (!hasAccess) {
|
if (!hasAccess) {
|
||||||
await Gal.requestAccess(toAlbum: true);
|
await Gal.requestAccess(toAlbum: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pathToSave = videoPath;
|
||||||
|
File? tempFile;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Gal.putVideo(videoPath, album: 'twonly');
|
if (name != null) {
|
||||||
|
final file = File(videoPath);
|
||||||
|
final extension = file.path.split('.').last;
|
||||||
|
final tempDir = Directory.systemTemp;
|
||||||
|
tempFile = File(join(tempDir.path, '$name.$extension'));
|
||||||
|
if (tempFile.existsSync()) {
|
||||||
|
try {
|
||||||
|
tempFile.deleteSync();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
file.copySync(tempFile.path);
|
||||||
|
pathToSave = tempFile.path;
|
||||||
|
}
|
||||||
|
await Gal.putVideo(pathToSave, album: 'twonly');
|
||||||
return null;
|
return null;
|
||||||
} on GalException catch (e) {
|
} on GalException catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
return e.type.message;
|
return e.type.message;
|
||||||
|
} finally {
|
||||||
|
if (tempFile != null && tempFile.existsSync()) {
|
||||||
|
try {
|
||||||
|
tempFile.deleteSync();
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Failed to delete temp video file: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -310,11 +310,18 @@ class MemoriesViewState extends State<MemoriesView> with AutomaticKeepAliveClien
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
final media = item.mediaService;
|
final media = item.mediaService;
|
||||||
if (media.mediaFile.type == MediaType.video) {
|
if (media.mediaFile.type == MediaType.video) {
|
||||||
await saveVideoToGallery(media.storedPath.path);
|
await saveVideoToGallery(
|
||||||
|
media.storedPath.path,
|
||||||
|
name: media.mediaFile.mediaId,
|
||||||
|
);
|
||||||
} else if (media.mediaFile.type == MediaType.image ||
|
} else if (media.mediaFile.type == MediaType.image ||
|
||||||
media.mediaFile.type == MediaType.gif) {
|
media.mediaFile.type == MediaType.gif) {
|
||||||
final imageBytes = await media.storedPath.readAsBytes();
|
final imageBytes = await media.storedPath.readAsBytes();
|
||||||
await saveImageToGallery(imageBytes, createdAt: media.mediaFile.createdAt);
|
await saveImageToGallery(
|
||||||
|
imageBytes,
|
||||||
|
createdAt: media.mediaFile.createdAt,
|
||||||
|
name: media.mediaFile.mediaId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setProgress((i + 1) / selectedList.length);
|
setProgress((i + 1) / selectedList.length);
|
||||||
|
|
|
||||||
|
|
@ -193,11 +193,18 @@ class _SynchronizedImageViewerScreenState
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (item.mediaFile.type == MediaType.video) {
|
if (item.mediaFile.type == MediaType.video) {
|
||||||
await saveVideoToGallery(item.storedPath.path);
|
await saveVideoToGallery(
|
||||||
|
item.storedPath.path,
|
||||||
|
name: item.mediaFile.mediaId,
|
||||||
|
);
|
||||||
} else if (item.mediaFile.type == MediaType.image ||
|
} else if (item.mediaFile.type == MediaType.image ||
|
||||||
item.mediaFile.type == MediaType.gif) {
|
item.mediaFile.type == MediaType.gif) {
|
||||||
final imageBytes = await item.storedPath.readAsBytes();
|
final imageBytes = await item.storedPath.readAsBytes();
|
||||||
await saveImageToGallery(imageBytes, createdAt: item.mediaFile.createdAt);
|
await saveImageToGallery(
|
||||||
|
imageBytes,
|
||||||
|
createdAt: item.mediaFile.createdAt,
|
||||||
|
name: item.mediaFile.mediaId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
showSnackbar(
|
showSnackbar(
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/android_photo_picker.service.dart';
|
import 'package:twonly/src/services/android_photo_picker.service.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart' show ShortCutsExtension, sha256File;
|
import 'package:twonly/src/utils/misc.dart' show ShortCutsExtension, sha256File;
|
||||||
import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart';
|
import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
|
|
@ -84,8 +85,8 @@ class _ImportFromGalleryViewState extends State<ImportFromGalleryView> {
|
||||||
|
|
||||||
final hash = Uint8List.fromList(sha256.convert(bytes).bytes);
|
final hash = Uint8List.fromList(sha256.convert(bytes).bytes);
|
||||||
|
|
||||||
final exsits = await twonlyDB.mediaFilesDao.getMediaByHash(hash);
|
final exists = await twonlyDB.mediaFilesDao.getMediaByHash(hash);
|
||||||
if (exsits.isNotEmpty) {
|
if (exists.isNotEmpty) {
|
||||||
duplicated += 1;
|
duplicated += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -345,8 +346,8 @@ class _ImportFromGalleryViewState extends State<ImportFromGalleryView> {
|
||||||
|
|
||||||
final hash = Uint8List.fromList(await sha256File(file));
|
final hash = Uint8List.fromList(await sha256File(file));
|
||||||
|
|
||||||
final exsits = await twonlyDB.mediaFilesDao.getMediaByHash(hash);
|
final exists = await twonlyDB.mediaFilesDao.getMediaByHash(hash);
|
||||||
if (exsits.isNotEmpty) {
|
if (exists.isNotEmpty) {
|
||||||
duplicated += 1;
|
duplicated += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -363,14 +364,50 @@ class _ImportFromGalleryViewState extends State<ImportFromGalleryView> {
|
||||||
type = MediaType.image;
|
type = MediaType.image;
|
||||||
}
|
}
|
||||||
|
|
||||||
final mediaFile = await twonlyDB.mediaFilesDao.insertOrUpdateMedia(
|
MediaFile? mediaFile;
|
||||||
MediaFilesCompanion(
|
var isRestored = false;
|
||||||
type: Value(type),
|
|
||||||
createdAt: Value(createdAt),
|
if (Platform.isIOS) {
|
||||||
storedFileHash: Value(hash),
|
try {
|
||||||
stored: const Value(true),
|
final assetName = await asset.titleAsync;
|
||||||
),
|
final dotIndex = assetName.lastIndexOf('.');
|
||||||
);
|
final baseName = dotIndex != -1 ? assetName.substring(0, dotIndex) : assetName;
|
||||||
|
|
||||||
|
final uuidRegex = RegExp(
|
||||||
|
r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$',
|
||||||
|
);
|
||||||
|
if (uuidRegex.hasMatch(baseName)) {
|
||||||
|
final existing = await twonlyDB.mediaFilesDao.getMediaFileById(baseName);
|
||||||
|
if (existing != null) {
|
||||||
|
final mediaService = MediaFileService(existing);
|
||||||
|
if (!mediaService.storedPath.existsSync()) {
|
||||||
|
await twonlyDB.mediaFilesDao.updateMedia(
|
||||||
|
baseName,
|
||||||
|
MediaFilesCompanion(
|
||||||
|
storedFileHash: Value(hash),
|
||||||
|
stored: const Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(baseName);
|
||||||
|
isRestored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Error checking iOS asset UUID name: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRestored) {
|
||||||
|
mediaFile = await twonlyDB.mediaFilesDao.insertOrUpdateMedia(
|
||||||
|
MediaFilesCompanion(
|
||||||
|
type: Value(type),
|
||||||
|
createdAt: Value(createdAt),
|
||||||
|
storedFileHash: Value(hash),
|
||||||
|
stored: const Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (mediaFile != null) {
|
if (mediaFile != null) {
|
||||||
final mediaService = MediaFileService(mediaFile);
|
final mediaService = MediaFileService(mediaFile);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue