♻️ Improved image analyzer in attachments

This commit is contained in:
LittleSheep 2024-07-19 23:56:59 +08:00
parent 5a7432e330
commit 0573ee456e
3 changed files with 50 additions and 41 deletions

View File

@ -31,23 +31,27 @@ Future<String> calculateFileSha256(File file) async {
return await calculateBytesSha256(bytes); return await calculateBytesSha256(bytes);
} }
Future<double> calculateDataAspectRatio(Uint8List data) async { Future<Map<String, dynamic>> calculateImageData(Uint8List data) async {
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
return 1; return {};
} }
final decoder = await Isolate.run(() => img.findDecoderForData(data)); final decoder = await Isolate.run(() => img.findDecoderForData(data));
if (decoder == null) return 1; if (decoder == null) return {};
final image = await Isolate.run(() => decoder.decode(data)); final image = await Isolate.run(() => decoder.decode(data));
if (image == null) return 1; if (image == null) return {};
return image.width / image.height; return {
'width': image.width,
'height': image.height,
'ratio': image.width / image.height
};
} }
Future<double> calculateFileAspectRatio(File file) async { Future<Map<String, dynamic>> calculateImageMetaFromFile(File file) async {
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
return 1; return {};
} }
final bytes = await Isolate.run(() => file.readAsBytesSync()); final bytes = await Isolate.run(() => file.readAsBytesSync());
return await calculateDataAspectRatio(bytes); return await calculateImageData(bytes);
} }
class AttachmentProvider extends GetConnect { class AttachmentProvider extends GetConnect {
@ -75,8 +79,12 @@ class AttachmentProvider extends GetConnect {
} }
Future<Response> createAttachment( Future<Response> createAttachment(
Uint8List data, String path, String hash, String usage, Uint8List data,
{double? ratio}) async { String path,
String hash,
String usage,
Map<String, dynamic>? metadata,
) async {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized'); if (!await auth.isAuthorized) throw Exception('unauthorized');
@ -104,9 +112,7 @@ class AttachmentProvider extends GetConnect {
'hash': hash, 'hash': hash,
'usage': usage, 'usage': usage,
if (mimetypeOverride != null) 'mimetype': mimetypeOverride, if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': jsonEncode({ 'metadata': jsonEncode(metadata),
if (ratio != null) 'ratio': ratio,
}),
}); });
final resp = await client.post('/attachments', payload); final resp = await client.post('/attachments', payload);
if (resp.statusCode != 200) { if (resp.statusCode != 200) {

View File

@ -87,12 +87,14 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
try { try {
final file = File(image.path); final file = File(image.path);
final hash = await calculateFileSha256(file); final hash = await calculateFileSha256(file);
final meta = await calculateImageMetaFromFile(file);
attachResp = await provider.createAttachment( attachResp = await provider.createAttachment(
await file.readAsBytes(), await file.readAsBytes(),
file.path, file.path,
hash, hash,
'p.$position', 'p.$position',
ratio: await calculateFileAspectRatio(file), {...meta},
); );
} catch (e) { } catch (e) {
setState(() => _isBusy = false); setState(() => _isBusy = false);

View File

@ -54,13 +54,14 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
for (final media in medias) { for (final media in medias) {
final file = File(media.path); final file = File(media.path);
final hash = await calculateFileSha256(file); final hash = await calculateFileSha256(file);
final meta = await calculateImageMetaFromFile(file);
try { try {
await uploadAttachment( await uploadAttachment(
await file.readAsBytes(), await file.readAsBytes(),
file.path, file.path,
hash, hash,
ratio: await calculateFileAspectRatio(file), {...meta},
); );
} catch (err) { } catch (err) {
context.showErrorDialog(err); context.showErrorDialog(err);
@ -81,11 +82,11 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
final file = File(media.path); final file = File(media.path);
final hash = await calculateFileSha256(file); final hash = await calculateFileSha256(file);
const ratio = 16 / 9;
try { try {
await uploadAttachment(await file.readAsBytes(), file.path, hash, await uploadAttachment(await file.readAsBytes(), file.path, hash, {
ratio: ratio); 'ratio': 16 / 9,
});
} catch (err) { } catch (err) {
context.showErrorDialog(err); context.showErrorDialog(err);
} }
@ -97,8 +98,9 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return; if (!await auth.isAuthorized) return;
FilePickerResult? result = FilePickerResult? result = await FilePicker.platform.pickFiles(
await FilePicker.platform.pickFiles(allowMultiple: true); allowMultiple: true,
);
if (result == null) return; if (result == null) return;
setState(() => _isBusy = true); setState(() => _isBusy = true);
@ -108,7 +110,7 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
for (final file in files) { for (final file in files) {
final hash = await calculateFileSha256(file); final hash = await calculateFileSha256(file);
try { try {
await uploadAttachment(await file.readAsBytes(), file.path, hash); await uploadAttachment(await file.readAsBytes(), file.path, hash, null);
} catch (err) { } catch (err) {
context.showErrorDialog(err); context.showErrorDialog(err);
} }
@ -131,23 +133,20 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
setState(() => _isBusy = true); setState(() => _isBusy = true);
double? ratio; Map<String, dynamic> metadata;
final file = File(media.path); final file = File(media.path);
final hash = await calculateFileSha256(file); final hash = await calculateFileSha256(file);
if (isVideo) { if (isVideo) {
ratio = 16 / 9; metadata = {'ratio': 16 / 9};
} else { } else {
ratio = await calculateFileAspectRatio(file); metadata = await calculateImageMetaFromFile(file);
} }
try { try {
await uploadAttachment( await uploadAttachment(await file.readAsBytes(), file.path, hash, {
await file.readAsBytes(), ...metadata,
file.path, });
hash,
ratio: ratio,
);
} catch (err) { } catch (err) {
context.showErrorDialog(err); context.showErrorDialog(err);
} }
@ -162,14 +161,14 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
setState(() => _isBusy = true); setState(() => _isBusy = true);
final hash = await calculateBytesSha256(data); final hash = await calculateBytesSha256(data);
final ratio = await calculateDataAspectRatio(data); final meta = await calculateImageData(data);
uploadAttachment(data, 'pasted image', hash, ratio: ratio); uploadAttachment(data, 'Pasted Image', hash, {...meta});
setState(() => _isBusy = false); setState(() => _isBusy = false);
} }
Future<void> uploadAttachment(Uint8List data, String path, String hash, Future<void> uploadAttachment(Uint8List data, String path, String hash,
{double? ratio}) async { Map<String, dynamic>? metadata) async {
final AttachmentProvider provider = Get.find(); final AttachmentProvider provider = Get.find();
try { try {
context.showSnackbar((PlatformInfo.isWeb context.showSnackbar((PlatformInfo.isWeb
@ -181,7 +180,7 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
path, path,
hash, hash,
widget.usage, widget.usage,
ratio: ratio, metadata,
); );
var result = Attachment.fromJson(resp.body); var result = Attachment.fromJson(resp.body);
setState(() => _attachments.add(result)); setState(() => _attachments.add(result));
@ -281,11 +280,14 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
for (final file in detail.files) { for (final file in detail.files) {
final data = await file.readAsBytes(); final data = await file.readAsBytes();
final hash = await calculateBytesSha256(data); final hash = await calculateBytesSha256(data);
double? ratio;
Map<String, dynamic> meta = {};
if (file.mimeType?.split('/').firstOrNull == 'image') { if (file.mimeType?.split('/').firstOrNull == 'image') {
ratio = await calculateDataAspectRatio(data); meta = await calculateImageData(data);
} }
uploadAttachment(data, file.path, hash, ratio: ratio);
uploadAttachment(data, file.path, hash, {...meta});
} }
setState(() => _isBusy = false); setState(() => _isBusy = false);
}, },
@ -348,8 +350,7 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
maxLines: 1, maxLines: 1,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontFamily: 'monospace' fontFamily: 'monospace'),
),
), ),
Text( Text(
'${fileType[0].toUpperCase()}${fileType.substring(1)} · ${formatBytes(element.size)}', '${fileType[0].toUpperCase()}${fileType.substring(1)} · ${formatBytes(element.size)}',
@ -506,7 +507,7 @@ class _AttachmentEditorDialogState extends State<AttachmentEditorDialog> {
_isMature = widget.item.isMature; _isMature = widget.item.isMature;
_altController.text = widget.item.alt; _altController.text = widget.item.alt;
if (['image', 'video'].contains(widget.item.mimetype.split('/').first)) { if (['image', 'video'].contains(widget.item.mimetype.split('/').firstOrNull)) {
_ratioController.text = _ratioController.text =
widget.item.metadata?['ratio']?.toString() ?? 1.toString(); widget.item.metadata?['ratio']?.toString() ?? 1.toString();
_hasAspectRatio = true; _hasAspectRatio = true;