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';
|
2024-08-21 07:39:29 +00:00
|
|
|
import 'package:solian/exceptions/request.dart';
|
2024-08-21 07:25:50 +00:00
|
|
|
import 'package:solian/exceptions/unauthorized.dart';
|
2024-05-22 15:18:01 +00:00
|
|
|
import 'package:path/path.dart';
|
2024-07-29 09:56:36 +00:00
|
|
|
import 'package:solian/models/attachment.dart';
|
2024-08-02 14:46:48 +00:00
|
|
|
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-06-30 09:43:36 +00:00
|
|
|
|
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() {
|
2024-07-16 11:46:53 +00:00
|
|
|
httpClient.baseUrl = ServiceFinder.buildUrl('files', null);
|
2024-05-22 15:18:01 +00:00
|
|
|
}
|
|
|
|
|
2024-08-18 14:51:52 +00:00
|
|
|
final Map<String, Attachment> _cachedResponses = {};
|
2024-05-27 15:07:01 +00:00
|
|
|
|
2024-08-02 14:46:48 +00:00
|
|
|
Future<List<Attachment?>> listMetadata(
|
2024-08-18 14:51:52 +00:00
|
|
|
List<String> rid, {
|
2024-08-02 14:46:48 +00:00
|
|
|
noCache = false,
|
|
|
|
}) async {
|
2024-08-18 14:51:52 +00:00
|
|
|
if (rid.isEmpty) return List.empty();
|
2024-08-03 13:29:48 +00:00
|
|
|
|
2024-08-18 14:51:52 +00:00
|
|
|
List<Attachment?> result = List.filled(rid.length, null);
|
|
|
|
List<String> pendingQuery = List.empty(growable: true);
|
2024-08-02 14:46:48 +00:00
|
|
|
if (!noCache) {
|
2024-08-18 14:51:52 +00:00
|
|
|
for (var idx = 0; idx < rid.length; idx++) {
|
|
|
|
if (_cachedResponses.containsKey(rid[idx])) {
|
|
|
|
result[idx] = _cachedResponses[rid[idx]];
|
2024-08-02 14:46:48 +00:00
|
|
|
} else {
|
2024-08-18 14:51:52 +00:00
|
|
|
pendingQuery.add(rid[idx]);
|
2024-08-02 14:46:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final resp = await get(
|
|
|
|
'/attachments?take=${pendingQuery.length}&id=${pendingQuery.join(',')}',
|
|
|
|
);
|
|
|
|
if (resp.statusCode != 200) return result;
|
|
|
|
|
|
|
|
final rawOut = PaginationResult.fromJson(resp.body);
|
|
|
|
if (rawOut.data == null) return result;
|
|
|
|
|
|
|
|
final List<Attachment> out =
|
|
|
|
rawOut.data!.map((x) => Attachment.fromJson(x)).toList();
|
|
|
|
for (final item in out) {
|
|
|
|
if (item.destination != 0 && item.isAnalyzed) {
|
2024-08-18 14:51:52 +00:00
|
|
|
_cachedResponses[item.rid] = item;
|
2024-08-02 14:46:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var i = 0; i < out.length; i++) {
|
2024-08-18 14:51:52 +00:00
|
|
|
for (var j = 0; j < rid.length; j++) {
|
|
|
|
if (out[i].rid == rid[j]) {
|
2024-08-02 14:46:48 +00:00
|
|
|
result[j] = out[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-08-18 14:51:52 +00:00
|
|
|
Future<Attachment?> getMetadata(String rid, {noCache = false}) async {
|
|
|
|
if (!noCache && _cachedResponses.containsKey(rid)) {
|
|
|
|
return _cachedResponses[rid]!;
|
2024-06-01 13:39:28 +00:00
|
|
|
}
|
|
|
|
|
2024-08-18 14:51:52 +00:00
|
|
|
final resp = await get('/attachments/$rid/meta');
|
2024-07-29 09:56:36 +00:00
|
|
|
if (resp.statusCode == 200) {
|
|
|
|
final result = Attachment.fromJson(resp.body);
|
|
|
|
if (result.destination != 0 && result.isAnalyzed) {
|
2024-08-18 14:51:52 +00:00
|
|
|
_cachedResponses[rid] = result;
|
2024-07-29 09:56:36 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2024-06-01 13:39:28 +00:00
|
|
|
|
2024-07-29 09:56:36 +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();
|
2024-08-21 07:39:29 +00:00
|
|
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
2024-05-22 15:18:01 +00:00
|
|
|
|
2024-08-20 17:53:16 +00:00
|
|
|
final client = auth.configureClient(
|
|
|
|
'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,
|
2024-08-18 14:51:52 +00:00
|
|
|
'pool': pool,
|
2024-06-03 15:36:46 +00:00
|
|
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
2024-07-19 15:56:59 +00:00
|
|
|
'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) {
|
2024-08-21 07:39:29 +00:00
|
|
|
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();
|
2024-08-21 07:39:29 +00:00
|
|
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
2024-08-20 17:53:16 +00:00
|
|
|
|
|
|
|
final client = auth.configureClient('uc');
|
|
|
|
|
|
|
|
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) {
|
2024-08-21 07:39:29 +00:00
|
|
|
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();
|
2024-08-21 07:39:29 +00:00
|
|
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
2024-08-20 17:53:16 +00:00
|
|
|
|
|
|
|
final client = auth.configureClient(
|
|
|
|
'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) {
|
2024-08-21 07:39:29 +00:00
|
|
|
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,
|
2024-08-18 14:51:52 +00:00
|
|
|
String alt, {
|
2024-05-22 15:18:01 +00:00
|
|
|
bool isMature = false,
|
|
|
|
}) async {
|
|
|
|
final AuthProvider auth = Get.find();
|
2024-08-21 07:39:29 +00:00
|
|
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
2024-05-22 15:18:01 +00:00
|
|
|
|
2024-07-16 11:46:53 +00:00
|
|
|
final client = auth.configureClient('files');
|
2024-05-22 15:18:01 +00:00
|
|
|
|
2024-07-16 11:46:53 +00:00
|
|
|
var resp = await client.put('/attachments/$id', {
|
2024-05-22 15:18:01 +00:00
|
|
|
'alt': alt,
|
|
|
|
'is_mature': isMature,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (resp.statusCode != 200) {
|
2024-08-21 07:39:29 +00:00
|
|
|
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();
|
2024-08-21 07:39:29 +00:00
|
|
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
2024-05-22 15:18:01 +00:00
|
|
|
|
2024-07-16 11:46:53 +00:00
|
|
|
final client = auth.configureClient('files');
|
2024-05-22 15:18:01 +00:00
|
|
|
|
2024-07-16 11:46:53 +00:00
|
|
|
var resp = await client.delete('/attachments/$id');
|
2024-05-22 15:18:01 +00:00
|
|
|
if (resp.statusCode != 200) {
|
2024-08-21 07:39:29 +00:00
|
|
|
throw RequestException(resp);
|
2024-05-22 15:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return resp;
|
|
|
|
}
|
2024-06-01 13:39:28 +00:00
|
|
|
|
2024-08-18 16:14:09 +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
|
|
|
}
|