Files
Prisma/lib/backend/providers/danbooru_provider.dart
T

171 lines
4.9 KiB
Dart

import 'package:dio/dio.dart';
import '../../core/http/dio_client.dart';
import '../mappers/danbooru_mapper.dart';
import '../models/post.dart';
import '../models/post_comment.dart';
import '../models/provider_health.dart';
import '../models/top_period_filter.dart';
import 'content_provider.dart';
class DanbooruProvider implements ContentProvider, CommentProvider {
DanbooruProvider({
required this.id,
required this.name,
required this.baseUrl,
required DioClient dioClient,
Map<String, String> queryParameters = const {},
}) : _queryParameters = queryParameters,
_dio = dioClient.dio;
@override
final String id;
@override
final String name;
@override
final String baseUrl;
final Dio _dio;
final Map<String, String> _queryParameters;
@override
Future<List<Post>> searchPosts({
required List<String> tags,
required int page,
int limit = 50,
String? rating,
TopPeriodFilter topPeriod = TopPeriodFilter.none,
}) async {
final queryTags = [
...tags,
if (rating != null && rating.isNotEmpty) 'rating:$rating',
..._topTags(topPeriod),
];
final response = await _dio.get<dynamic>(
'/posts.json',
queryParameters: {
'page': page + 1,
'limit': limit,
'tags': queryTags.join(' '),
..._queryParameters,
},
);
return DanbooruMapper.postsFromResponse(
response.data,
providerId: id,
providerName: name,
);
}
@override
Future<Post?> getPost(String id) async {
final response = await _dio.get<dynamic>(
'/posts/$id.json',
queryParameters: _queryParameters,
);
final posts = DanbooruMapper.postsFromResponse(
[response.data],
providerId: this.id,
providerName: name,
);
return posts.isEmpty ? null : posts.first;
}
@override
Future<ProviderHealth> checkHealth() async {
final startedAt = DateTime.now();
try {
await _dio.get<dynamic>(
'/posts.json',
queryParameters: {'limit': 1, ..._queryParameters},
);
return ProviderHealth(
providerId: id,
status: ProviderStatus.online,
pingMs: DateTime.now().difference(startedAt).inMilliseconds,
lastCheckedAt: DateTime.now(),
apiVersion: 'danbooru',
);
} catch (error) {
return ProviderHealth(
providerId: id,
status: ProviderStatus.offline,
pingMs: DateTime.now().difference(startedAt).inMilliseconds,
lastCheckedAt: DateTime.now(),
errorMessage: error.toString(),
apiVersion: 'danbooru',
);
}
}
@override
Future<List<PostComment>> getComments(String postId) async {
final response = await _dio.get<dynamic>(
'/comments.json',
queryParameters: {
'search[post_id]': postId,
'limit': 50,
..._queryParameters,
},
);
final items = _commentItems(response.data);
return items
.whereType<Map>()
.map((item) {
final json = Map<String, dynamic>.from(item);
return PostComment(
id: (json['id'] ?? '').toString(),
postId: (json['post_id'] ?? postId).toString(),
providerId: id,
authorName: (json['creator_name'] ??
json['creator_id'] ??
json['updater_name'] ??
'user')
.toString(),
body: (json['body'] ?? json['text'] ?? json['comment'] ?? '')
.toString(),
createdAt:
DateTime.tryParse((json['created_at'] ?? '').toString()) ??
DateTime.fromMillisecondsSinceEpoch(0),
);
})
.where((comment) => comment.body.trim().isNotEmpty)
.toList();
}
List<dynamic> _commentItems(dynamic data) {
if (data is List) return data;
if (data is! Map) return const [];
final json = Map<String, dynamic>.from(data);
final comments = json['comments'];
if (comments is List) return comments;
final records = json['records'];
if (records is List) return records;
final comment = json['comment'];
if (comment is List) return comment;
if (comment is Map) return [comment];
return const [];
}
List<String> _topTags(TopPeriodFilter period) {
final now = DateTime.now();
return switch (period) {
TopPeriodFilter.none => const [],
TopPeriodFilter.month => [
'order:score',
'date:>${_date(now.subtract(const Duration(days: 31)))}',
],
TopPeriodFilter.year => [
'order:score',
'date:>${_date(DateTime(now.year - 1, now.month, now.day))}',
],
TopPeriodFilter.allTime => const ['order:score'],
};
}
String _date(DateTime value) {
return '${value.year.toString().padLeft(4, '0')}-'
'${value.month.toString().padLeft(2, '0')}-'
'${value.day.toString().padLeft(2, '0')}';
}
}