Files
PiliPlus/lib/http/sponsor_block.dart
My-Responsitories 9d94c72e95 tweaks (#2426)
* opt: danmaku weight

* opt: cache clean

* opt: level img

* opt: play icon

* opt: svg big-vip

* opt: webview ua

* opt: simple dialog

* feat: export vtt

* tweak

* opt: mapIndexed

* feat: more subtitle

* refa: settings page

* feat: codec list options

* drawPath

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

* custom dialog option

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

* update

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

* Revert "drawPath"

This reverts commit e8a4b19f0f.

* opt: _initStreamIndex

* fix: avoid gap

* fix: scale [skip ci]

* fix: hide repost menu not login

* tweaks

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

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-06-26 10:51:41 +08:00

233 lines
6.9 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/build_config.dart';
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';
import 'package:flutter/foundation.dart' show kDebugMode;
/// https://github.com/hanydd/BilibiliSponsorBlock/wiki/API
abstract final class SponsorBlock {
static String get blockServer => Pref.blockServer;
static final options = Options(
followRedirects: true,
// https://github.com/hanydd/BilibiliSponsorBlock/wiki/API#1-%E5%85%AC%E7%94%A8%E5%8F%82%E6%95%B0
headers: kDebugMode
? null
: {
'origin': Constants.appName,
'x-ext-version': BuildConfig.versionName,
},
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 final List list) {
return Success(list.map((i) => SegmentItemModel.fromJson(i)).toList());
}
}
return getErrMsg(res);
}
static Future<LoadingState<void>> 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<void>> 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<void>> 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': kDebugMode
? Constants.userAgent
: '${Constants.appName}/${BuildConfig.versionName}',
'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 final 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 final 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 final Map<String, dynamic> data) {
if (data['UUID'] case String uuid) {
return Success(uuid);
}
}
}
return getErrMsg(res);
}
}