mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
900 lines
26 KiB
Dart
900 lines
26 KiB
Dart
import 'dart:convert';
|
||
import 'dart:developer';
|
||
|
||
import 'package:PiliPlus/common/constants.dart';
|
||
import 'package:PiliPlus/http/api.dart';
|
||
import 'package:PiliPlus/http/init.dart';
|
||
import 'package:PiliPlus/http/loading_state.dart';
|
||
import 'package:PiliPlus/http/login.dart';
|
||
import 'package:PiliPlus/models/common/account_type.dart';
|
||
import 'package:PiliPlus/models/home/rcmd/result.dart';
|
||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||
import 'package:PiliPlus/models/model_rec_video_item.dart';
|
||
import 'package:PiliPlus/models/pgc_lcf.dart';
|
||
import 'package:PiliPlus/models/video/play/url.dart';
|
||
import 'package:PiliPlus/models_new/pgc/pgc_rank/pgc_rank_item_model.dart';
|
||
import 'package:PiliPlus/models_new/triple/pgc_triple.dart';
|
||
import 'package:PiliPlus/models_new/triple/ugc_triple.dart';
|
||
import 'package:PiliPlus/models_new/video/video_ai_conclusion/data.dart';
|
||
import 'package:PiliPlus/models_new/video/video_detail/video_detail_response.dart';
|
||
import 'package:PiliPlus/models_new/video/video_note_list/data.dart';
|
||
import 'package:PiliPlus/models_new/video/video_play_info/data.dart';
|
||
import 'package:PiliPlus/models_new/video/video_relation/data.dart';
|
||
import 'package:PiliPlus/utils/accounts.dart';
|
||
import 'package:PiliPlus/utils/extension.dart';
|
||
import 'package:PiliPlus/utils/global_data.dart';
|
||
import 'package:PiliPlus/utils/id_utils.dart';
|
||
import 'package:PiliPlus/utils/recommend_filter.dart';
|
||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||
import 'package:PiliPlus/utils/wbi_sign.dart';
|
||
import 'package:dio/dio.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
|
||
/// view层根据 status 判断渲染逻辑
|
||
class VideoHttp {
|
||
static bool p1080 = Pref.p1080;
|
||
// static bool enableRcmdDynamic = Pref.enableRcmdDynamic;
|
||
static RegExp zoneRegExp = RegExp(Pref.banWordForZone, caseSensitive: false);
|
||
static bool enableFilter = zoneRegExp.pattern.isNotEmpty;
|
||
|
||
// 首页推荐视频
|
||
static Future<LoadingState> rcmdVideoList(
|
||
{required int ps, required int freshIdx}) async {
|
||
var res = await Request().get(
|
||
Api.recommendListWeb,
|
||
queryParameters: {
|
||
'version': 1,
|
||
'feed_version': 'V8',
|
||
'homepage_ver': 1,
|
||
'ps': ps,
|
||
'fresh_idx': freshIdx,
|
||
'brush': freshIdx,
|
||
'fresh_type': 4
|
||
},
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
List<RecVideoItemModel> list = <RecVideoItemModel>[];
|
||
for (var i in res.data['data']['item']) {
|
||
//过滤掉live与ad,以及拉黑用户
|
||
if (i['goto'] == 'av' &&
|
||
(i['owner'] != null &&
|
||
!GlobalData().blackMids.contains(i['owner']['mid']))) {
|
||
RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i);
|
||
if (!RecommendFilter.filter(videoItem)) {
|
||
list.add(videoItem);
|
||
}
|
||
}
|
||
}
|
||
return Success(list);
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
// 添加额外的loginState变量模拟未登录状态
|
||
static Future<LoadingState> rcmdVideoListApp({required int freshIdx}) async {
|
||
Map<String, String> data = {
|
||
'build': '2001100',
|
||
'c_locale': 'zh_CN',
|
||
'channel': 'master',
|
||
'column': '4',
|
||
'device': 'pad',
|
||
'device_name': 'android',
|
||
'device_type': '0',
|
||
'disable_rcmd': '0',
|
||
'flush': '5',
|
||
'fnval': '976',
|
||
'fnver': '0',
|
||
'force_host': '2', //使用https
|
||
'fourk': '1',
|
||
'guidance': '0',
|
||
'https_url_req': '0',
|
||
'idx': freshIdx.toString(),
|
||
'mobi_app': 'android_hd',
|
||
'network': 'wifi',
|
||
'platform': 'android',
|
||
'player_net': '1',
|
||
'pull': freshIdx == 0 ? 'true' : 'false',
|
||
'qn': '32',
|
||
'recsys_mode': '0',
|
||
's_locale': 'zh_CN',
|
||
'splash_id': '',
|
||
'statistics': Constants.statistics,
|
||
'voice_balance': '0'
|
||
};
|
||
var res = await Request().get(
|
||
Api.recommendListApp,
|
||
queryParameters: data,
|
||
options: Options(headers: {
|
||
'buvid': LoginHttp.buvid,
|
||
'fp_local':
|
||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||
'fp_remote':
|
||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||
'session_id': '11111111',
|
||
'env': 'prod',
|
||
'app-key': 'android_hd',
|
||
'User-Agent': Constants.userAgent,
|
||
'x-bili-trace-id': Constants.traceId,
|
||
'x-bili-aurora-eid': '',
|
||
'x-bili-aurora-zone': '',
|
||
'bili-http-engine': 'cronet',
|
||
}),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
List<RecVideoItemAppModel> list = <RecVideoItemAppModel>[];
|
||
for (var i in res.data['data']['items']) {
|
||
// 屏蔽推广和拉黑用户
|
||
if (i['card_goto'] != 'ad_av' &&
|
||
i['card_goto'] != 'ad_web_s' &&
|
||
i['ad_info'] == null &&
|
||
// (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) &&
|
||
(i['args'] != null &&
|
||
!GlobalData().blackMids.contains(i['args']['up_id']))) {
|
||
if (enableFilter &&
|
||
i['args']?['tname'] != null &&
|
||
zoneRegExp.hasMatch(i['args']['tname'])) {
|
||
continue;
|
||
}
|
||
RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i);
|
||
if (!RecommendFilter.filter(videoItem)) {
|
||
list.add(videoItem);
|
||
}
|
||
}
|
||
}
|
||
return Success(list);
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
// 最热视频
|
||
static Future<LoadingState<List<HotVideoItemModel>>> hotVideoList(
|
||
{required int pn, required int ps}) async {
|
||
var res = await Request().get(
|
||
Api.hotList,
|
||
queryParameters: {'pn': pn, 'ps': ps},
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
List<HotVideoItemModel> list = <HotVideoItemModel>[];
|
||
for (var i in res.data['data']['list']) {
|
||
if (!GlobalData().blackMids.contains(i['owner']['mid']) &&
|
||
!RecommendFilter.filterTitle(i['title']) &&
|
||
!RecommendFilter.filterLikeRatio(
|
||
i['stat']['like'], i['stat']['view'])) {
|
||
if (enableFilter &&
|
||
i['tname'] != null &&
|
||
zoneRegExp.hasMatch(i['tname'])) {
|
||
continue;
|
||
}
|
||
list.add(HotVideoItemModel.fromJson(i));
|
||
}
|
||
}
|
||
return Success(list);
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
// 视频流
|
||
static Future videoUrl({
|
||
int? avid,
|
||
String? bvid,
|
||
required int cid,
|
||
int? qn,
|
||
dynamic epid,
|
||
dynamic seasonId,
|
||
bool? forcePgcApi,
|
||
}) async {
|
||
final params = await WbiSign.makSign({
|
||
if (avid != null) 'avid': avid,
|
||
if (bvid != null) 'bvid': bvid,
|
||
if (epid != null) 'ep_id': epid,
|
||
if (seasonId != null) 'season_id': seasonId,
|
||
'cid': cid,
|
||
'qn': qn ?? 80,
|
||
// 获取所有格式的视频
|
||
'fnval': 4048,
|
||
'fourk': 1,
|
||
'fnver': 0,
|
||
'voice_balance': 1,
|
||
'gaia_source': 'pre-load',
|
||
'isGaiaAvoided': true,
|
||
'web_location': 1315873,
|
||
// 免登录查看1080p
|
||
if (!Accounts.get(AccountType.video).isLogin && p1080) 'try_look': 1,
|
||
});
|
||
|
||
late final usePgcApi =
|
||
forcePgcApi == true || Accounts.get(AccountType.video).isLogin;
|
||
|
||
try {
|
||
var res = await Request().get(
|
||
epid != null && usePgcApi ? Api.pgcUrl : Api.ugcUrl,
|
||
queryParameters: params,
|
||
);
|
||
|
||
if (res.data['code'] == 0) {
|
||
late PlayUrlModel data;
|
||
if (epid != null && usePgcApi) {
|
||
data = PlayUrlModel.fromJson(res.data['result']['video_info'])
|
||
..lastPlayTime = res.data['result']['play_view_business_info']
|
||
['user_status']['watch_progress']['current_watch_progress'];
|
||
} else {
|
||
data = PlayUrlModel.fromJson(res.data['data']);
|
||
}
|
||
return {
|
||
'status': true,
|
||
'data': data,
|
||
};
|
||
} else {
|
||
if (epid != null && !usePgcApi && forcePgcApi != true) {
|
||
return videoUrl(
|
||
avid: avid,
|
||
bvid: bvid,
|
||
cid: cid,
|
||
qn: qn,
|
||
epid: epid,
|
||
seasonId: seasonId,
|
||
forcePgcApi: true,
|
||
);
|
||
}
|
||
return {
|
||
'status': false,
|
||
'code': res.data['code'],
|
||
'msg': res.data['message'],
|
||
};
|
||
}
|
||
} catch (err) {
|
||
return {'status': false, 'msg': err.toString()};
|
||
}
|
||
}
|
||
|
||
// 视频信息 标题、简介
|
||
static Future videoIntro({required String bvid}) async {
|
||
var res =
|
||
await Request().get(Api.videoIntro, queryParameters: {'bvid': bvid});
|
||
VideoDetailResponse result = VideoDetailResponse.fromJson(res.data);
|
||
if (result.code == 0) {
|
||
return {
|
||
'status': true,
|
||
'data': result.data!,
|
||
};
|
||
} else {
|
||
// Map errMap = {
|
||
// -400: '请求错误',
|
||
// -403: '权限不足',
|
||
// -404: '视频资源失效',
|
||
// 62002: '稿件不可见',
|
||
// 62004: '稿件审核中',
|
||
// };
|
||
return {
|
||
'status': false,
|
||
'data': result.data,
|
||
'code': result.code,
|
||
'msg': result.message,
|
||
// 'msg': errMap[result.code] ?? '请求异常',
|
||
};
|
||
}
|
||
}
|
||
|
||
static Future videoRelation({required dynamic bvid}) async {
|
||
var res = await Request().get(
|
||
Api.videoRelation,
|
||
queryParameters: {
|
||
'aid': IdUtils.bv2av(bvid),
|
||
'bvid': bvid,
|
||
},
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {
|
||
'status': true,
|
||
'data': VideoRelation.fromJson(res.data['data']),
|
||
};
|
||
} else {
|
||
return {
|
||
'status': false,
|
||
'msg': res.data['message'],
|
||
};
|
||
}
|
||
}
|
||
|
||
// 相关视频
|
||
static Future<LoadingState<List<HotVideoItemModel>?>> relatedVideoList(
|
||
{required String bvid}) async {
|
||
var res =
|
||
await Request().get(Api.relatedList, queryParameters: {'bvid': bvid});
|
||
if (res.data['code'] == 0) {
|
||
final items = (res.data['data'] as List?)
|
||
?.map((i) => HotVideoItemModel.fromJson(i));
|
||
final list = RecommendFilter.applyFilterToRelatedVideos
|
||
? items?.where((i) => !RecommendFilter.filterAll(i)).toList()
|
||
: items?.toList();
|
||
return Success(list);
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
// 获取点赞/投币/收藏状态 pgc
|
||
static Future pgcLikeCoinFav({dynamic epId}) async {
|
||
var res = await Request().get(
|
||
Api.pgcLikeCoinFav,
|
||
queryParameters: {'ep_id': epId},
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': PgcLCF.fromJson(res.data['data'])};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// 投币
|
||
static Future coinVideo({
|
||
required String bvid,
|
||
required int multiply,
|
||
int selectLike = 0,
|
||
}) async {
|
||
var res = await Request().post(
|
||
Api.coinVideo,
|
||
data: {
|
||
'aid': IdUtils.bv2av(bvid).toString(),
|
||
// 'bvid': bvid,
|
||
'multiply': multiply.toString(),
|
||
'select_like': selectLike.toString(),
|
||
// 'csrf': Accounts.main.csrf,
|
||
},
|
||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': res.data['data']};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// 一键三连 pgc
|
||
static Future triple({dynamic epId, required dynamic seasonId}) async {
|
||
var res = await Request().post(
|
||
Api.triple,
|
||
data: {
|
||
'ep_id': epId,
|
||
'csrf': Accounts.main.csrf,
|
||
},
|
||
options: Options(
|
||
contentType: Headers.formUrlEncodedContentType,
|
||
headers: {
|
||
'origin': 'https://www.bilibili.com',
|
||
'referer': 'https://www.bilibili.com/bangumi/play/ss$seasonId',
|
||
'user-agent': Request.headerUa(type: 'pc'),
|
||
},
|
||
),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': PgcTriple.fromJson(res.data['data'])};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// 一键三连
|
||
static Future oneThree({required String bvid}) async {
|
||
var res = await Request().post(
|
||
Api.oneThree,
|
||
data: {
|
||
'aid': IdUtils.bv2av(bvid),
|
||
'eab_x': 2,
|
||
'ramval': 0,
|
||
'source': 'web_normal',
|
||
'ga': 1,
|
||
'csrf': Accounts.main.csrf,
|
||
},
|
||
options: Options(
|
||
contentType: Headers.formUrlEncodedContentType,
|
||
headers: {
|
||
'origin': 'https://www.bilibili.com',
|
||
'referer': 'https://www.bilibili.com/video/$bvid',
|
||
'user-agent': Request.headerUa(type: 'pc'),
|
||
},
|
||
),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': UgcTriple.fromJson(res.data['data'])};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// (取消)点赞
|
||
static Future likeVideo({required String bvid, required bool type}) async {
|
||
var res = await Request().post(
|
||
Api.likeVideo,
|
||
data: {
|
||
'aid': IdUtils.bv2av(bvid).toString(),
|
||
'like': type ? '0' : '1',
|
||
},
|
||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': res.data['data']};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// (取消)点踩
|
||
static Future dislikeVideo({required String bvid, required bool type}) async {
|
||
if (Accounts.main.accessKey.isNullOrEmpty) {
|
||
return {'status': false, 'msg': "请退出账号后重新登录"};
|
||
}
|
||
var res = await Request().post(
|
||
Api.dislikeVideo,
|
||
data: {
|
||
'aid': IdUtils.bv2av(bvid).toString(),
|
||
'dislike': type ? '0' : '1',
|
||
},
|
||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||
);
|
||
if (res.data is! String && res.data['code'] == 0) {
|
||
return {'status': true};
|
||
} else {
|
||
return {
|
||
'status': false,
|
||
'msg': res.data is String ? res.data : res.data['message']
|
||
};
|
||
}
|
||
}
|
||
|
||
// 推送不感兴趣反馈
|
||
static Future feedDislike(
|
||
{required String goto,
|
||
required int id,
|
||
int? reasonId,
|
||
int? feedbackId}) async {
|
||
if (Accounts.get(AccountType.recommend).accessKey.isNullOrEmpty) {
|
||
return {'status': false, 'msg': "请退出账号后重新登录"};
|
||
}
|
||
assert((reasonId != null) ^ (feedbackId != null));
|
||
var res = await Request().get(Api.feedDislike, queryParameters: {
|
||
'goto': goto,
|
||
'id': id,
|
||
if (reasonId != null) 'reason_id': reasonId,
|
||
if (feedbackId != null) 'feedback_id': feedbackId,
|
||
'build': '1',
|
||
'mobi_app': 'android',
|
||
});
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// 推送不感兴趣取消
|
||
static Future feedDislikeCancel(
|
||
{required String goto,
|
||
required int id,
|
||
int? reasonId,
|
||
int? feedbackId}) async {
|
||
if (Accounts.get(AccountType.recommend).accessKey.isNullOrEmpty) {
|
||
return {'status': false, 'msg': "请退出账号后重新登录"};
|
||
}
|
||
var res = await Request().get(Api.feedDislikeCancel, queryParameters: {
|
||
'goto': goto,
|
||
'id': id,
|
||
if (reasonId != null) 'reason_id': reasonId,
|
||
if (feedbackId != null) 'feedback_id': feedbackId,
|
||
'build': '1',
|
||
'mobi_app': 'android',
|
||
});
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// 发表评论 replyAdd
|
||
|
||
// type num 评论区类型代码 必要 类型代码见表
|
||
// oid num 目标评论区id 必要
|
||
// root num 根评论rpid 非必要 二级评论以上使用
|
||
// parent num 父评论rpid 非必要 二级评论同根评论id 大于二级评论为要回复的评论id
|
||
// message str 发送评论内容 必要 最大1000字符
|
||
// plat num 发送平台标识 非必要 1:web端 2:安卓客户端 3:ios客户端 4:wp客户端
|
||
static Future replyAdd({
|
||
required int type,
|
||
required int oid,
|
||
required String message,
|
||
int? root,
|
||
int? parent,
|
||
List? pictures,
|
||
bool? syncToDynamic,
|
||
Map<String, int>? atNameToMid,
|
||
}) async {
|
||
if (message == '') {
|
||
return {'status': false, 'msg': '请输入评论内容'};
|
||
}
|
||
Map<String, dynamic> data = {
|
||
'type': type,
|
||
'oid': oid,
|
||
if (root != null && root != 0) 'root': root,
|
||
if (parent != null && parent != 0) 'parent': parent,
|
||
'message': message,
|
||
if (atNameToMid?.isNotEmpty == true)
|
||
'at_name_to_mid': jsonEncode(atNameToMid), // {"name":uid}
|
||
if (pictures != null) 'pictures': jsonEncode(pictures),
|
||
if (syncToDynamic == true) 'sync_to_dynamic': 1,
|
||
'csrf': Accounts.main.csrf,
|
||
};
|
||
var res = await Request().post(
|
||
Api.replyAdd,
|
||
data: data,
|
||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||
);
|
||
log(res.toString());
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': res.data['data']};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
static Future replyDel({
|
||
required int type, //replyType
|
||
required int oid,
|
||
required int rpid,
|
||
}) async {
|
||
var res = await Request().post(Api.replyDel, queryParameters: {
|
||
'type': type, //type.index
|
||
'oid': oid,
|
||
'rpid': rpid,
|
||
'csrf': Accounts.main.csrf,
|
||
});
|
||
log(res.toString());
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
// 操作用户关系
|
||
static Future relationMod(
|
||
{required int mid, required int act, required int reSrc}) async {
|
||
var res = await Request().post(
|
||
Api.relationMod,
|
||
queryParameters: {
|
||
'statistics': '{"appId":100,"platform":5}',
|
||
'x-bili-device-req-json':
|
||
'{"platform":"web","device":"pc","spmid":"333.1387"}',
|
||
},
|
||
data: {
|
||
'fid': mid,
|
||
'act': act,
|
||
're_src': reSrc,
|
||
'gaia_source': 'web_main',
|
||
'spmid': '333.1387',
|
||
'extend_content': jsonEncode({
|
||
"entity": "user",
|
||
"entity_id": mid,
|
||
'fp': Request.headerUa(type: 'pc'),
|
||
}),
|
||
'csrf': Accounts.main.csrf,
|
||
},
|
||
options: Options(
|
||
contentType: Headers.formUrlEncodedContentType,
|
||
headers: {
|
||
'origin': 'https://space.bilibili.com',
|
||
'referer': 'https://space.bilibili.com/$mid/dynamic',
|
||
'user-agent': Request.headerUa(type: 'pc'),
|
||
},
|
||
),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
static Future roomEntryAction({
|
||
roomId,
|
||
}) async {
|
||
await Request().post(
|
||
Api.roomEntryAction,
|
||
queryParameters: {
|
||
'csrf': Accounts.main.csrf,
|
||
},
|
||
data: {
|
||
'room_id': roomId,
|
||
'platform': 'pc',
|
||
},
|
||
);
|
||
}
|
||
|
||
static Future historyReport({
|
||
aid,
|
||
type,
|
||
}) async {
|
||
await Request().post(Api.historyReport, queryParameters: {
|
||
if (aid != null) 'aid': aid,
|
||
if (type != null) 'type': type,
|
||
'csrf': Accounts.main.csrf,
|
||
});
|
||
}
|
||
|
||
// 视频播放进度
|
||
static Future heartBeat({
|
||
bvid,
|
||
cid,
|
||
progress,
|
||
epid,
|
||
seasonId,
|
||
subType,
|
||
}) async {
|
||
await Request().post(Api.heartBeat, queryParameters: {
|
||
'bvid': bvid,
|
||
'cid': cid,
|
||
if (epid != null) 'epid': epid,
|
||
if (seasonId != null) 'sid': seasonId,
|
||
if (epid != null) 'type': 4,
|
||
if (subType != null) 'sub_type': subType,
|
||
'played_time': progress,
|
||
'csrf': Accounts.main.csrf,
|
||
});
|
||
}
|
||
|
||
static Future medialistHistory({
|
||
required int desc,
|
||
required dynamic oid,
|
||
required dynamic upperMid,
|
||
}) async {
|
||
await Request().post(Api.mediaListHistory, queryParameters: {
|
||
'desc': desc,
|
||
'oid': oid,
|
||
'upper_mid': upperMid,
|
||
'csrf': Accounts.main.csrf,
|
||
});
|
||
}
|
||
|
||
// 添加追番
|
||
static Future pgcAdd({int? seasonId}) async {
|
||
var res = await Request().post(Api.pgcAdd, queryParameters: {
|
||
'season_id': seasonId,
|
||
'csrf': Accounts.main.csrf,
|
||
});
|
||
if (res.data['code'] == 0) {
|
||
return {
|
||
'status': true,
|
||
'msg':
|
||
res.data['result'] == null ? 'failed' : res.data['result']['toast']
|
||
};
|
||
} else {
|
||
return {
|
||
'status': false,
|
||
'msg':
|
||
res.data['result'] == null ? 'failed' : res.data['result']['toast']
|
||
};
|
||
}
|
||
}
|
||
|
||
// 取消追番
|
||
static Future pgcDel({int? seasonId}) async {
|
||
var res = await Request().post(Api.pgcDel, queryParameters: {
|
||
'season_id': seasonId,
|
||
'csrf': Accounts.main.csrf,
|
||
});
|
||
if (res.data['code'] == 0) {
|
||
return {
|
||
'status': true,
|
||
'msg':
|
||
res.data['result'] == null ? 'failed' : res.data['result']['toast']
|
||
};
|
||
} else {
|
||
return {
|
||
'status': false,
|
||
'msg':
|
||
res.data['result'] == null ? 'failed' : res.data['result']['toast']
|
||
};
|
||
}
|
||
}
|
||
|
||
static Future pgcUpdate({
|
||
required List seasonId,
|
||
required dynamic status,
|
||
}) async {
|
||
var res = await Request().post(
|
||
Api.pgcUpdate,
|
||
data: {
|
||
'season_id': seasonId.join(','),
|
||
'status': status,
|
||
'csrf': Accounts.main.csrf,
|
||
},
|
||
options: Options(
|
||
contentType: Headers.formUrlEncodedContentType,
|
||
),
|
||
);
|
||
return {
|
||
'status': res.data['code'] == 0,
|
||
'msg': res.data['result'] == null ? 'failed' : res.data['result']['toast']
|
||
};
|
||
}
|
||
|
||
// 查看视频同时在看人数
|
||
static Future onlineTotal({int? aid, String? bvid, int? cid}) async {
|
||
var res = await Request().get(Api.onlineTotal, queryParameters: {
|
||
'aid': aid,
|
||
'bvid': bvid,
|
||
'cid': cid,
|
||
});
|
||
if (res.data['code'] == 0) {
|
||
return {'status': true, 'data': res.data['data']['total']};
|
||
} else {
|
||
return {'status': false, 'data': null, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
static Future aiConclusion({
|
||
String? bvid,
|
||
int? cid,
|
||
int? upMid,
|
||
}) async {
|
||
final params = await WbiSign.makSign({
|
||
'bvid': bvid,
|
||
'cid': cid,
|
||
'up_mid': upMid,
|
||
});
|
||
var res = await Request().get(Api.aiConclusion, queryParameters: params);
|
||
if (res.data['code'] == 0 && res.data['data']['code'] == 0) {
|
||
return {
|
||
'status': true,
|
||
'data': AiConclusionData.fromJson(res.data['data']),
|
||
};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
static Future playInfo({String? aid, String? bvid, required int cid}) async {
|
||
assert(aid != null || bvid != null);
|
||
var res = await Request().get(
|
||
Api.playInfo,
|
||
queryParameters: {
|
||
if (aid != null) 'aid': aid,
|
||
if (bvid != null) 'bvid': bvid,
|
||
'cid': cid,
|
||
},
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return {
|
||
'status': true,
|
||
'data': PlayInfoData.fromJson(res.data['data']),
|
||
};
|
||
} else {
|
||
return {'status': false, 'msg': res.data['message']};
|
||
}
|
||
}
|
||
|
||
static Future vttSubtitles(String subtitleUrl) async {
|
||
String subtitleTimecode(num seconds) {
|
||
int h = seconds ~/ 3600;
|
||
seconds %= 3600;
|
||
int m = seconds ~/ 60;
|
||
seconds %= 60;
|
||
String sms = seconds.toStringAsFixed(3).padLeft(6, '0');
|
||
return h == 0
|
||
? "${m.toString().padLeft(2, '0')}:$sms"
|
||
: "${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}:$sms";
|
||
}
|
||
|
||
String processList(List list) {
|
||
final sb = StringBuffer('WEBVTT\n\n')
|
||
..writeAll(
|
||
list.map((item) =>
|
||
'${item?['sid'] ?? 0}\n${subtitleTimecode(item['from'])} --> ${subtitleTimecode(item['to'])}\n${item['content'].trim()}'),
|
||
'\n\n');
|
||
return sb.toString();
|
||
}
|
||
|
||
var res = await Request().get("https:$subtitleUrl");
|
||
|
||
if (res.data?['body'] is List) {
|
||
return await compute(processList, res.data['body'] as List);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 视频排行
|
||
static Future<LoadingState<List<HotVideoItemModel>>> getRankVideoList(
|
||
int rid) async {
|
||
var res = await Request().get(
|
||
Api.getRankApi,
|
||
queryParameters: await WbiSign.makSign({
|
||
'rid': rid,
|
||
'type': 'all',
|
||
}),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
List<HotVideoItemModel> list = <HotVideoItemModel>[];
|
||
for (var i in res.data['data']['list']) {
|
||
if (!GlobalData().blackMids.contains(i['owner']['mid']) &&
|
||
!RecommendFilter.filterTitle(i['title']) &&
|
||
!RecommendFilter.filterLikeRatio(
|
||
i['stat']['like'], i['stat']['view'])) {
|
||
if (enableFilter &&
|
||
i['tname'] != null &&
|
||
zoneRegExp.hasMatch(i['tname'])) {
|
||
continue;
|
||
}
|
||
list.add(HotVideoItemModel.fromJson(i));
|
||
}
|
||
}
|
||
return Success(list);
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
// pgc 排行
|
||
static Future<LoadingState> pgcRankList(
|
||
{int day = 3, required int seasonType}) async {
|
||
var res = await Request().get(
|
||
Api.pgcRank,
|
||
queryParameters: await WbiSign.makSign({
|
||
'day': day,
|
||
'season_type': seasonType,
|
||
}),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return Success((res.data['result']?['list'] as List?)
|
||
?.map((e) => PgcRankItemModel.fromJson(e))
|
||
.toList());
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
// pgc season 排行
|
||
static Future<LoadingState> pgcSeasonRankList(
|
||
{int day = 3, required int seasonType}) async {
|
||
var res = await Request().get(
|
||
Api.pgcSeasonRank,
|
||
queryParameters: await WbiSign.makSign({
|
||
'day': day,
|
||
'season_type': seasonType,
|
||
}),
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return Success((res.data['data']?['list'] as List?)
|
||
?.map((e) => PgcRankItemModel.fromJson(e))
|
||
.toList());
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
|
||
static Future<LoadingState<VideoNoteData>> getVideoNoteList({
|
||
dynamic oid,
|
||
dynamic uperMid,
|
||
required int page,
|
||
}) async {
|
||
var res = await Request().get(
|
||
Api.archiveNoteList,
|
||
queryParameters: {
|
||
'csrf': Accounts.main.csrf,
|
||
'oid': oid,
|
||
'oid_type': 0,
|
||
'pn': page,
|
||
'ps': 10,
|
||
if (uperMid != null) 'uper_mid': uperMid,
|
||
},
|
||
);
|
||
if (res.data['code'] == 0) {
|
||
return Success(VideoNoteData.fromJson(res.data['data']));
|
||
} else {
|
||
return Error(res.data['message']);
|
||
}
|
||
}
|
||
}
|