Files
PiliPlus/lib/http/sponsor_block.dart
My-Responsitories 521ca3ad18 tweaks (#1788)
* tweak

* opt: show bar

* opt: crc32

* opt: appsign

* opt: Get

* opt: compress only if large

* opt: wbi

* tweak

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

---------

Signed-off-by: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com>
Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-12-23 12:57:19 +08:00

222 lines
6.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/sponsor_block_api.dart';
import 'package:PiliPlus/models/common/sponsor_block/post_segment_model.dart';
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
import 'package:PiliPlus/models_new/sponsor_block/segment_item.dart';
import 'package:PiliPlus/models_new/sponsor_block/user_info.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:dio/dio.dart';
/// https://github.com/hanydd/BilibiliSponsorBlock/wiki/API
abstract final class SponsorBlock {
static String get blockServer => Pref.blockServer;
static final options = Options(
followRedirects: true,
validateStatus: (status) => true,
);
static Error getErrMsg(Response res) {
String statusMessage = switch (res.statusCode) {
200 => '意料之外的响应',
400 => '参数错误',
403 => '被自动审核机制拒绝',
404 => '未找到数据',
409 => '重复提交',
429 => '提交太快(触发速率控制)',
500 => '服务器无法获取信息',
-1 => res.data['message'].toString(), // DioException
_ => res.statusMessage ?? res.statusCode.toString(),
};
if (res.statusCode != null && res.statusCode != -1) {
final data = res.data;
if (res.statusCode == 200 ||
(data is String && data.isNotEmpty && data.length < 200)) {
statusMessage = '$statusMessage$data';
}
}
return Error(statusMessage, code: res.statusCode);
}
static String _api(String url) => '$blockServer/api/$url';
static Future<LoadingState<List<SegmentItemModel>>> getSkipSegments({
required String bvid,
required int cid,
}) async {
final res = await Request().get(
_api(SponsorBlockApi.skipSegments),
queryParameters: {
'videoID': bvid,
'cid': cid,
},
options: options,
);
if (res.statusCode == 200) {
if (res.data case List list) {
return Success(list.map((i) => SegmentItemModel.fromJson(i)).toList());
}
}
return getErrMsg(res);
}
static Future<LoadingState<Null>> voteOnSponsorTime({
required String uuid,
int? type,
SegmentType? category,
}) async {
assert((type == null) == (category == null));
final res = await Request().post(
_api(SponsorBlockApi.voteOnSponsorTime),
queryParameters: {
'UUID': uuid,
'type': ?type,
'category': ?category?.name,
'userID': Pref.blockUserID,
},
options: options,
);
return res.statusCode == 200 ? const Success(null) : getErrMsg(res);
}
static Future<LoadingState<Null>> viewedVideoSponsorTime(String uuid) async {
final res = await Request().post(
_api(SponsorBlockApi.viewedVideoSponsorTime),
data: {'UUID': uuid},
options: options,
);
return res.statusCode == 200 ? const Success(null) : getErrMsg(res);
}
static Future<LoadingState<Null>> uptimeStatus() async {
final res = await Request().get(
_api(SponsorBlockApi.uptimeStatus),
options: options,
);
if (res.statusCode == 200 &&
res.data is String &&
Utils.isStringNumeric(res.data)) {
return const Success(null);
}
return getErrMsg(res);
}
static Future<LoadingState<UserInfo>> userInfo(
List<String> query, {
String? userId,
}) async {
final res = await Request().get(
_api(SponsorBlockApi.userInfo),
queryParameters: {
'userID': userId ?? Pref.blockUserID,
'values': jsonEncode(query),
},
options: options,
);
if (res.statusCode == 200) {
return Success(UserInfo.fromJson(res.data));
}
return getErrMsg(res);
}
static Future<LoadingState<List<SegmentItemModel>>> postSkipSegments({
required String bvid,
required int cid,
required double videoDuration,
required List<PostSegmentModel> segments,
}) async {
final res = await Request().post(
_api(SponsorBlockApi.skipSegments),
data: {
'videoID': bvid,
'cid': cid.toString(),
'userID': Pref.blockUserID,
'userAgent': Constants.userAgent,
'videoDuration': videoDuration,
'segments': segments
.map(
(item) => {
'segment': [item.segment.first, item.segment.second],
'category': item.category.name,
'actionType': item.actionType.name,
},
)
.toList(),
},
options: options,
);
if (res.statusCode == 200) {
if (res.data case List list) {
return Success(list.map((i) => SegmentItemModel.fromJson(i)).toList());
}
}
return getErrMsg(res);
}
/// {
/// "bvID": string, // B站视频BVID
/// "cid": string, // 视频CID
/// "ytbID": string, // YouTube视频ID
/// "UUID": string, // 绑定记录的UUID不是视频中片段的UUID是绑定记录本身的UUID
/// "votes": int, // 绑定记录的投票数
/// "locked": int, // 绑定记录是否锁定
/// }
/// TODO: show port video info dialog
static Future<LoadingState<String>> getPortVideo({
required String bvid,
required int cid,
}) async {
final res = await Request().get(
_api(SponsorBlockApi.portVideo),
queryParameters: {
'videoID': bvid,
'cid': cid.toString(),
},
options: options,
);
if (res.statusCode == 200) {
if (res.data case Map<String, dynamic> data) {
if (data['ytbID'] case String ytbId) {
return Success(ytbId);
}
}
}
return getErrMsg(res);
}
static Future<LoadingState<String>> postPortVideo({
required String bvid,
required int cid,
required String ytbId,
required int videoDuration,
}) async {
final res = await Request().post(
_api(SponsorBlockApi.portVideo),
data: {
'bvID': bvid,
'cid': cid.toString(),
'ytbID': ytbId,
'userID': Pref.blockUserID,
'biliDuration': videoDuration,
},
options: options,
);
if (res.statusCode == 200) {
if (res.data case Map<String, dynamic> data) {
if (data['UUID'] case String uuid) {
return Success(uuid);
}
}
}
return getErrMsg(res);
}
}