Solian/lib/providers/content/attachment.dart

255 lines
7.0 KiB
Dart
Raw Normal View History

2024-05-22 15:18:01 +00:00
import 'dart:convert';
2024-06-29 12:25:29 +00:00
import 'dart:typed_data';
2024-05-22 15:18:01 +00:00
import 'package:get/get.dart';
import 'package:solian/exceptions/request.dart';
import 'package:solian/exceptions/unauthorized.dart';
2024-05-22 15:18:01 +00:00
import 'package:path/path.dart';
import 'package:solian/models/attachment.dart';
import 'package:solian/models/pagination.dart';
2024-05-22 15:18:01 +00:00
import 'package:solian/providers/auth.dart';
import 'package:solian/services.dart';
2024-05-22 15:18:01 +00:00
class AttachmentProvider extends GetConnect {
2024-06-29 12:25:29 +00:00
static Map<String, String> mimetypeOverrides = {
'mov': 'video/quicktime',
'mp4': 'video/mp4'
};
2024-06-01 13:39:28 +00:00
2024-05-22 15:18:01 +00:00
@override
void onInit() {
httpClient.baseUrl = ServiceFinder.buildUrl('files', null);
2024-05-22 15:18:01 +00:00
}
final Map<String, Attachment> _cachedResponses = {};
2024-05-27 15:07:01 +00:00
List<Attachment?> listMetadataFromCache(List<String> rid) {
if (rid.isEmpty) return List.empty();
List<Attachment?> result = List.filled(rid.length, null);
for (var idx = 0; idx < rid.length; idx++) {
if (_cachedResponses.containsKey(rid[idx])) {
result[idx] = _cachedResponses[rid[idx]];
} else {
result[idx] = null;
}
}
return result;
}
Future<List<Attachment?>> listMetadata(
List<String> rid, {
noCache = false,
}) async {
if (rid.isEmpty) return List.empty();
List<Attachment?> result = List.filled(rid.length, null);
List<String> pendingQuery = List.empty(growable: true);
if (!noCache) {
for (var idx = 0; idx < rid.length; idx++) {
if (_cachedResponses.containsKey(rid[idx])) {
result[idx] = _cachedResponses[rid[idx]];
} else {
pendingQuery.add(rid[idx]);
}
}
}
2024-10-10 16:28:09 +00:00
if (pendingQuery.isNotEmpty) {
final resp = await get(
'/attachments?take=${pendingQuery.length}&id=${pendingQuery.join(',')}',
);
if (resp.statusCode != 200) return result;
2024-10-10 16:28:09 +00:00
final rawOut = PaginationResult.fromJson(resp.body);
if (rawOut.data == null) return result;
2024-10-10 16:28:09 +00:00
final List<Attachment> out =
rawOut.data!.map((x) => Attachment.fromJson(x)).toList();
for (final item in out) {
if (item.destination != 0 && item.isAnalyzed) {
_cachedResponses[item.rid] = item;
}
}
2024-10-10 16:28:09 +00:00
for (var i = 0; i < out.length; i++) {
for (var j = 0; j < rid.length; j++) {
if (out[i].rid == rid[j]) {
result[j] = out[i];
}
}
}
}
return result;
}
Future<Attachment?> getMetadata(String rid, {noCache = false}) async {
if (!noCache && _cachedResponses.containsKey(rid)) {
return _cachedResponses[rid]!;
2024-06-01 13:39:28 +00:00
}
final resp = await get('/attachments/$rid/meta');
if (resp.statusCode == 200) {
final result = Attachment.fromJson(resp.body);
if (result.destination != 0 && result.isAnalyzed) {
_cachedResponses[rid] = result;
}
return result;
}
2024-06-01 13:39:28 +00:00
return null;
2024-06-01 13:39:28 +00:00
}
2024-05-22 15:18:01 +00:00
2024-08-20 17:53:16 +00:00
Future<Attachment> createAttachmentDirectly(
Uint8List data,
String path,
String pool,
Map<String, dynamic>? metadata,
) async {
2024-05-22 15:18:01 +00:00
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
2024-05-22 15:18:01 +00:00
2024-09-16 03:57:16 +00:00
final client = await auth.configureClient(
2024-08-20 17:53:16 +00:00
'uc',
timeout: const Duration(minutes: 3),
);
2024-08-04 13:12:35 +00:00
2024-08-20 17:53:16 +00:00
final filePayload = MultipartFile(data, filename: basename(path));
2024-06-29 12:25:29 +00:00
final fileAlt = basename(path).contains('.')
? basename(path).substring(0, basename(path).lastIndexOf('.'))
: basename(path);
final fileExt = basename(path)
.substring(basename(path).lastIndexOf('.') + 1)
2024-05-27 15:07:01 +00:00
.toLowerCase();
// Override for some files cannot be detected mimetype by server-side
String? mimetypeOverride;
if (mimetypeOverrides.keys.contains(fileExt)) {
mimetypeOverride = mimetypeOverrides[fileExt];
}
2024-08-20 17:53:16 +00:00
final payload = FormData({
2024-06-03 15:36:46 +00:00
'alt': fileAlt,
'file': filePayload,
'pool': pool,
2024-06-03 15:36:46 +00:00
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': jsonEncode(metadata),
2024-06-03 15:36:46 +00:00
});
2024-08-20 17:53:16 +00:00
final resp = await client.post('/attachments', payload);
if (resp.statusCode != 200) {
throw RequestException(resp);
2024-08-20 17:53:16 +00:00
}
return Attachment.fromJson(resp.body);
}
Future<AttachmentPlaceholder> createAttachmentMultipartPlaceholder(
int size,
String path,
String pool,
Map<String, dynamic>? metadata,
) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
2024-08-20 17:53:16 +00:00
2024-09-16 03:57:16 +00:00
final client = await auth.configureClient('uc');
2024-08-20 17:53:16 +00:00
final fileAlt = basename(path).contains('.')
? basename(path).substring(0, basename(path).lastIndexOf('.'))
: basename(path);
final fileExt = basename(path)
.substring(basename(path).lastIndexOf('.') + 1)
.toLowerCase();
// Override for some files cannot be detected mimetype by server-side
String? mimetypeOverride;
if (mimetypeOverrides.keys.contains(fileExt)) {
mimetypeOverride = mimetypeOverrides[fileExt];
}
final resp = await client.post('/attachments/multipart', {
'alt': fileAlt,
'name': basename(path),
'size': size,
'pool': pool,
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': metadata,
});
if (resp.statusCode != 200) {
throw RequestException(resp);
2024-08-20 17:53:16 +00:00
}
return AttachmentPlaceholder.fromJson(resp.body);
}
Future<Attachment> uploadAttachmentMultipartChunk(
Uint8List data,
String name,
String rid,
String cid,
) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
2024-08-20 17:53:16 +00:00
2024-09-16 03:57:16 +00:00
final client = await auth.configureClient(
2024-08-20 17:53:16 +00:00
'uc',
timeout: const Duration(minutes: 3),
2024-08-01 14:13:08 +00:00
);
2024-08-20 17:53:16 +00:00
final payload = FormData({
'file': MultipartFile(data, filename: name),
});
final resp = await client.post('/attachments/multipart/$rid/$cid', payload);
2024-05-27 15:07:01 +00:00
if (resp.statusCode != 200) {
throw RequestException(resp);
2024-05-22 15:18:01 +00:00
}
2024-08-20 17:53:16 +00:00
return Attachment.fromJson(resp.body);
2024-05-22 15:18:01 +00:00
}
Future<Response> updateAttachment(
int id,
String alt, {
required Map<String, dynamic> metadata,
2024-05-22 15:18:01 +00:00
bool isMature = false,
}) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
2024-05-22 15:18:01 +00:00
2024-09-16 03:57:16 +00:00
final client = await auth.configureClient('files');
2024-05-22 15:18:01 +00:00
var resp = await client.put('/attachments/$id', {
2024-05-22 15:18:01 +00:00
'alt': alt,
'metadata': metadata,
2024-05-22 15:18:01 +00:00
'is_mature': isMature,
});
if (resp.statusCode != 200) {
throw RequestException(resp);
2024-05-22 15:18:01 +00:00
}
2024-05-27 15:07:01 +00:00
return resp;
2024-05-22 15:18:01 +00:00
}
Future<Response> deleteAttachment(int id) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
2024-05-22 15:18:01 +00:00
2024-09-16 03:57:16 +00:00
final client = await auth.configureClient('files');
2024-05-22 15:18:01 +00:00
var resp = await client.delete('/attachments/$id');
2024-05-22 15:18:01 +00:00
if (resp.statusCode != 200) {
throw RequestException(resp);
2024-05-22 15:18:01 +00:00
}
return resp;
}
2024-06-01 13:39:28 +00:00
void clearCache({String? id}) {
2024-06-01 13:39:28 +00:00
if (id != null) {
_cachedResponses.remove(id);
} else {
_cachedResponses.clear();
}
}
2024-05-22 15:18:01 +00:00
}