feat: release v2.0.6 with full Pawchive API integration (global feed, tags, links, announcements, server media filter)

This commit is contained in:
2026-09-03 20:43:38 +03:00
parent 84eeb909a1
commit b82d87161e
14 changed files with 652 additions and 67 deletions
+16 -2
View File
@@ -1,10 +1,13 @@
import '../models/artist_announcement.dart';
import '../models/artist_link.dart';
import '../models/artist_profile.dart';
import '../models/artist_tag.dart';
import '../models/artist_work_query.dart';
import '../models/post.dart';
import '../models/post_comment.dart';
import '../models/provider_health.dart';
import '../models/tag_suggestion.dart';
import '../models/top_period_filter.dart';
import '../models/artist_profile.dart';
import '../models/artist_work_query.dart';
abstract class ContentProvider {
String get id;
@@ -72,4 +75,15 @@ abstract class ArtistProvider {
String artistId,
String postId,
);
Future<List<ArtistTag>> getArtistTags(String service, String artistId) async =>
const [];
Future<List<ArtistLink>> getArtistLinks(
String service, String artistId) async =>
const [];
Future<List<ArtistAnnouncement>> getArtistAnnouncements(
String service, String artistId) async =>
const [];
}
@@ -1,7 +1,10 @@
import 'package:dio/dio.dart';
import '../../core/http/dio_client.dart';
import '../models/artist_announcement.dart';
import '../models/artist_link.dart';
import '../models/artist_profile.dart';
import '../models/artist_tag.dart';
import '../models/artist_work_query.dart';
import '../models/post.dart';
import '../models/post_comment.dart';
@@ -214,6 +217,21 @@ class KemonoProvider
return getArtistPostComments(parts[0], parts[1], parts[2]);
}
@override
Future<List<ArtistTag>> getArtistTags(
String service, String artistId) async =>
const [];
@override
Future<List<ArtistLink>> getArtistLinks(
String service, String artistId) async =>
const [];
@override
Future<List<ArtistAnnouncement>> getArtistAnnouncements(
String service, String artistId) async =>
const [];
String _creatorPath() => _isCoomer ? '/coomer' : '/kemono';
List<ArtistProfile> _artistsFromResponse(dynamic data) {
+104 -2
View File
@@ -3,7 +3,10 @@ import 'dart:async';
import 'package:dio/dio.dart';
import '../../core/http/dio_client.dart';
import '../models/artist_announcement.dart';
import '../models/artist_link.dart';
import '../models/artist_profile.dart';
import '../models/artist_tag.dart';
import '../models/artist_work_query.dart';
import '../models/post.dart';
import '../models/post_comment.dart';
@@ -42,7 +45,34 @@ class PawchiveProvider
String? rating,
TopPeriodFilter topPeriod = TopPeriodFilter.none,
}) async {
return const [];
final offset = (page - 1).clamp(0, 999999) * 50;
final queryText = tags.join(' ').trim();
try {
final response = await _dio.get<dynamic>(
'/api/v1/posts',
queryParameters: {
if (queryText.isNotEmpty) 'q': queryText,
'o': offset,
},
);
final posts = _postItems(response.data);
final mapped = <Post>[];
for (final post in posts.whereType<Map>()) {
final json = Map<String, dynamic>.from(post);
final service = (json['service'] ?? 'fanbox').toString();
final userId = (json['user'] ?? '').toString();
final dummyQuery = ArtistWorkQuery(
providerId: id,
service: service,
artistId: userId,
artistName: userId,
);
mapped.addAll(_postsFromArtistPost(json, query: dummyQuery));
}
return mapped;
} catch (_) {
return const [];
}
}
@override
@@ -178,7 +208,13 @@ class PawchiveProvider
try {
final response = await _dio.get<dynamic>(
path,
queryParameters: {'o': offset},
queryParameters: {
'o': offset,
if (query.tagFilter != null && query.tagFilter!.isNotEmpty)
'tag': query.tagFilter,
if (query.queryText != null && query.queryText!.isNotEmpty)
'q': query.queryText,
},
);
final posts = _postItems(response.data);
final mapped = <Post>[];
@@ -216,6 +252,72 @@ class PawchiveProvider
}
}
@override
Future<List<ArtistTag>> getArtistTags(String service, String artistId) async {
try {
final response = await _dio.get<dynamic>(
'/api/v1/$service/user/$artistId/tags',
options: Options(
receiveTimeout: const Duration(seconds: 15),
sendTimeout: const Duration(seconds: 10),
),
);
final items = response.data;
if (items is List) {
return items
.whereType<Map>()
.map((item) => ArtistTag.fromJson(Map<String, dynamic>.from(item)))
.toList(growable: false);
}
} catch (_) {}
return const [];
}
@override
Future<List<ArtistLink>> getArtistLinks(
String service, String artistId) async {
try {
final response = await _dio.get<dynamic>(
'/api/v1/$service/user/$artistId/links',
options: Options(
receiveTimeout: const Duration(seconds: 15),
sendTimeout: const Duration(seconds: 10),
),
);
final items = response.data;
if (items is List) {
return items
.whereType<Map>()
.map((item) => ArtistLink.fromJson(Map<String, dynamic>.from(item)))
.toList(growable: false);
}
} catch (_) {}
return const [];
}
@override
Future<List<ArtistAnnouncement>> getArtistAnnouncements(
String service, String artistId) async {
try {
final response = await _dio.get<dynamic>(
'/api/v1/$service/user/$artistId/announcements',
options: Options(
receiveTimeout: const Duration(seconds: 15),
sendTimeout: const Duration(seconds: 10),
),
);
final items = response.data;
if (items is List) {
return items
.whereType<Map>()
.map((item) =>
ArtistAnnouncement.fromJson(Map<String, dynamic>.from(item)))
.toList(growable: false);
}
} catch (_) {}
return const [];
}
@override
Future<List<PostComment>> getComments(String postId) async {
final parts = postId.split(':');