logo和名称替换
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
//acg模块网络请求
|
||||
|
||||
import 'package:hgmj/hj_model/acg/comic_chapters_model.dart';
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:hgmj/hj_model/video_model.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/acg/comic_chapters_model.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_model/video_model.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
import '../../hj_model/acg/cartoon_more_list.dart';
|
||||
import '../../hj_model/cartoon_media_info.dart';
|
||||
@@ -38,7 +38,8 @@ class ACGService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
"/tag/media/list",
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -56,8 +57,8 @@ class ACGService {
|
||||
/// 获取标签详情
|
||||
static Future<TagsBean?> fetchMediaTag(String tagId) async {
|
||||
final param = {'id': tagId};
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/media_tag/info', param: param, jsonTransformation: (json) => TagsBean.fromMap(json));
|
||||
final result = await httpManager.fetchResponseByGET('/media_tag/info',
|
||||
param: param, jsonTransformation: (json) => TagsBean.fromMap(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -87,7 +88,8 @@ class ACGService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/hot',
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -146,14 +148,17 @@ class ACGService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/ranking',
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
//获取动漫内容章节列表 sortType: 0-正序 1-倒序
|
||||
|
||||
static Future<ListBaseModel<ComicChapterInfo>?> getChapterList(String mediaId, int pageNumber, int pageSize, {int? sortType = 0}) async {
|
||||
static Future<ListBaseModel<ComicChapterInfo>?> getChapterList(
|
||||
String mediaId, int pageNumber, int pageSize,
|
||||
{int? sortType = 0}) async {
|
||||
final param = {
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize,
|
||||
@@ -163,7 +168,8 @@ class ACGService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media_content/list',
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<ComicChapterInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<ComicChapterInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -187,7 +193,8 @@ class ACGService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/recommend',
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -195,8 +202,20 @@ class ACGService {
|
||||
//kind:1动漫 2漫画 3小说 4短剧
|
||||
//sortType:1-最新上架 2-最多观看
|
||||
static Future<MediaSearchListModel?> mediaSearch(
|
||||
{int page = 1, int size = 20, int? kind, String? tagName, int? sortType, String? keyword}) async {
|
||||
final param = {'pageNumber': page, 'pageSize': size, 'kind': kind, 'tagName': tagName, 'sortType': sortType, 'keyword': keyword};
|
||||
{int page = 1,
|
||||
int size = 20,
|
||||
int? kind,
|
||||
String? tagName,
|
||||
int? sortType,
|
||||
String? keyword}) async {
|
||||
final param = {
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
'kind': kind,
|
||||
'tagName': tagName,
|
||||
'sortType': sortType,
|
||||
'keyword': keyword
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/search',
|
||||
@@ -244,7 +263,8 @@ class ACGService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/topic',
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<CartoonMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//AI相关接口
|
||||
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:hgmj/hj_page/ai/ai_sub_type/ai_function_logic.dart';
|
||||
import 'package:hgmj/tools_base/debug_log.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_page/ai/ai_sub_type/ai_function_logic.dart';
|
||||
import 'package:wydj/tools_base/debug_log.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
import '../../hj_page/ai/models/ai_girl_resp_model.dart';
|
||||
import '../../hj_page/ai/models/ai_mod_list_model.dart';
|
||||
@@ -18,7 +18,11 @@ class AIService {
|
||||
int? pageSize,
|
||||
int? status,
|
||||
) async {
|
||||
final param = {'pageNumber': pageNumber, 'pageSize': pageSize, 'status': status};
|
||||
final param = {
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize,
|
||||
'status': status
|
||||
};
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/ai/undress/list',
|
||||
param: param,
|
||||
@@ -68,8 +72,13 @@ class AIService {
|
||||
}
|
||||
|
||||
//生成AI脱衣记录
|
||||
static Future<bool> generateUndress(List<String>? originPics, bool shareStatus, String? shareTitle) async {
|
||||
final param = {'originPic': originPics, 'shareStatus': shareStatus ? 1 : 0, 'shareTitle': shareTitle}..removeWhere((k, v) => v == null);
|
||||
static Future<bool> generateUndress(
|
||||
List<String>? originPics, bool shareStatus, String? shareTitle) async {
|
||||
final param = {
|
||||
'originPic': originPics,
|
||||
'shareStatus': shareStatus ? 1 : 0,
|
||||
'shareTitle': shareTitle
|
||||
}..removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/ai/undress/generate',
|
||||
param: param,
|
||||
@@ -93,12 +102,15 @@ class AIService {
|
||||
'shareTitle': shareTitle
|
||||
}..removeWhere((k, v) => v == null);
|
||||
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/changeface/generate', param: param);
|
||||
final result = await httpManager
|
||||
.fetchResponseByPOST('/ai/changeface/generate', param: param);
|
||||
return result.isSuccess && result.data == 'success';
|
||||
}
|
||||
|
||||
//生成图片AI换脸
|
||||
static Future<bool> generateImg(String picture, String imgModId, bool shareStatus, String? shareTitle, {String? discount}) async {
|
||||
static Future<bool> generateImg(
|
||||
String picture, String imgModId, bool shareStatus, String? shareTitle,
|
||||
{String? discount}) async {
|
||||
final param = {
|
||||
'originPic': picture,
|
||||
'mId': imgModId,
|
||||
@@ -129,7 +141,8 @@ class AIService {
|
||||
String? categoryId, //模版分类id,如果为空,则默认第一个模版分类
|
||||
}) async {
|
||||
//接口只认 type=0/1,枚举在这一层换回去
|
||||
final param = {'type': type.isVideoFace ? 1 : 0, 'categoryId': categoryId}..removeWhere((k, v) => v == null);
|
||||
final param = {'type': type.isVideoFace ? 1 : 0, 'categoryId': categoryId}
|
||||
..removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/ai/mod/v2/list',
|
||||
param: param,
|
||||
@@ -141,21 +154,24 @@ class AIService {
|
||||
///删除AI订单脱衣
|
||||
static Future<bool> deleteUndress(String? id) async {
|
||||
final param = {'id': id};
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/undress/hide', param: param);
|
||||
final result =
|
||||
await httpManager.fetchResponseByPOST('/ai/undress/hide', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
///删除AI换脸Video订单
|
||||
static Future<bool> deleteChangeFace(String? id) async {
|
||||
final param = {'id': id};
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/changeface/hide', param: param);
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/changeface/hide',
|
||||
param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
///删除AI换脸 Img订单
|
||||
static Future<bool> deleteImg(String? id) async {
|
||||
final param = {'id': id};
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/img/hide', param: param);
|
||||
final result =
|
||||
await httpManager.fetchResponseByPOST('/ai/img/hide', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
@@ -200,9 +216,12 @@ class AIService {
|
||||
}
|
||||
|
||||
///获取AI广场列表
|
||||
static Future<ListBaseModel<AISquareItemModel>?> getPlazaList(int pageNumber, {int pageSize = 10}) async {
|
||||
static Future<ListBaseModel<AISquareItemModel>?> getPlazaList(int pageNumber,
|
||||
{int pageSize = 10}) async {
|
||||
final result = await httpManager.fetchResponseByGET('/aiplaza/list',
|
||||
param: {'pageNumber': pageNumber, 'pageSize': pageSize}, jsonTransformation: (json) => ListBaseModel<AISquareItemModel>.fromJson(json));
|
||||
param: {'pageNumber': pageNumber, 'pageSize': pageSize},
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<AISquareItemModel>.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -210,7 +229,8 @@ class AIService {
|
||||
static Future<TemplateModel?> getModelInfo(String? id, int? type) async {
|
||||
final param = {'id': id, 'type': type};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final resultData = await httpManager.fetchResponseByGET('/ai/mod/info', param: param);
|
||||
final resultData =
|
||||
await httpManager.fetchResponseByGET('/ai/mod/info', param: param);
|
||||
if (resultData.isSuccess) {
|
||||
final data = resultData.data;
|
||||
if (data is Map) {
|
||||
@@ -234,7 +254,11 @@ class AIService {
|
||||
int? pageSize,
|
||||
int? status,
|
||||
) async {
|
||||
final param = {'pageNumber': pageNumber, 'pageSize': pageSize, 'status': status};
|
||||
final param = {
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize,
|
||||
'status': status
|
||||
};
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/ai/text_to_image/list',
|
||||
param: param,
|
||||
@@ -246,7 +270,8 @@ class AIService {
|
||||
///删除文字绘图
|
||||
static Future<bool> deleteTextToImage(String? id) async {
|
||||
final param = {'id': id};
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/text_to_image/hide', param: param);
|
||||
final result = await httpManager
|
||||
.fetchResponseByPOST('/ai/text_to_image/hide', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
@@ -257,7 +282,11 @@ class AIService {
|
||||
int? pageSize,
|
||||
int? status,
|
||||
) async {
|
||||
final param = {'pageNumber': pageNumber, 'pageSize': pageSize, 'status': status};
|
||||
final param = {
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize,
|
||||
'status': status
|
||||
};
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/ai/imagetovideo/list',
|
||||
param: param,
|
||||
@@ -269,7 +298,8 @@ class AIService {
|
||||
///删除AI图生视频
|
||||
static Future<bool> deleteImgVideo(String? id) async {
|
||||
final param = {'id': id};
|
||||
final result = await httpManager.fetchResponseByPOST('/ai/imagetovideo/hide', param: param);
|
||||
final result = await httpManager
|
||||
.fetchResponseByPOST('/ai/imagetovideo/hide', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
@@ -345,7 +375,11 @@ class AIService {
|
||||
int? pageSize,
|
||||
int? status,
|
||||
) async {
|
||||
final param = {'pageNumber': pageNumber, 'pageSize': pageSize, 'status': status};
|
||||
final param = {
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize,
|
||||
'status': status
|
||||
};
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/ai_text_to_novel/list',
|
||||
param: param,
|
||||
@@ -357,7 +391,8 @@ class AIService {
|
||||
///删除AI文生图
|
||||
static Future<bool> deleteNovel(String? id) async {
|
||||
final param = {'id': id};
|
||||
final result = await httpManager.fetchResponseByPOST('/ai_text_to_novel/hide', param: param);
|
||||
final result = await httpManager
|
||||
.fetchResponseByPOST('/ai_text_to_novel/hide', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hgmj/tools_base/net/base_resp_bean.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/tools_base/net/base_resp_bean.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
/// 购买相关接口
|
||||
@@ -56,7 +56,8 @@ class BuyService {
|
||||
///[source] 来源页面标识
|
||||
///[experimentId]/[experimentVariant]/[sessionId] VIP 卡皮 A/B:与 /mine/topay 对齐回传
|
||||
///[requestId] 幂等键,见 [_idempotent]
|
||||
static Future<BaseRespBean> buyVip(int? productType, String? productID, String? productName, int? discountedPrice,
|
||||
static Future<BaseRespBean> buyVip(int? productType, String? productID,
|
||||
String? productName, int? discountedPrice,
|
||||
{String? couponId = "",
|
||||
bool? finalPayStatus,
|
||||
String? source,
|
||||
@@ -80,11 +81,18 @@ class BuyService {
|
||||
'contentId': contentId,
|
||||
'checkoutContextId': checkoutContextId,
|
||||
// 空值不传,避免参数错误(DISABLED / 无实验时上游不填)
|
||||
'experimentId': (experimentId != null && experimentId.isNotEmpty) ? experimentId : null,
|
||||
'experimentVariant': (experimentVariant != null && experimentVariant.isNotEmpty) ? experimentVariant : null,
|
||||
'sessionId': (sessionId != null && sessionId.isNotEmpty) ? sessionId : null,
|
||||
'experimentId': (experimentId != null && experimentId.isNotEmpty)
|
||||
? experimentId
|
||||
: null,
|
||||
'experimentVariant':
|
||||
(experimentVariant != null && experimentVariant.isNotEmpty)
|
||||
? experimentVariant
|
||||
: null,
|
||||
'sessionId':
|
||||
(sessionId != null && sessionId.isNotEmpty) ? sessionId : null,
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
return httpManager.fetchResponseByPOST('/product/buy', param: param, options: _idempotent(requestId));
|
||||
return httpManager.fetchResponseByPOST('/product/buy',
|
||||
param: param, options: _idempotent(requestId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
//评论相关接口
|
||||
|
||||
import 'package:hgmj/hj_model/comment/comment_list_res.dart';
|
||||
import 'package:hgmj/hj_model/comment/comment_model.dart';
|
||||
import 'package:hgmj/hj_model/comment/reply_model.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/comment/comment_list_res.dart';
|
||||
import 'package:wydj/hj_model/comment/comment_model.dart';
|
||||
import 'package:wydj/hj_model/comment/reply_model.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
class CommentService {
|
||||
// //一级评论的全部列表
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
//公共服务api接口
|
||||
import 'package:hgmj/hj_model/home/video_list_model.dart';
|
||||
import 'package:hgmj/tools_base/event_bus/events.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/home/video_list_model.dart';
|
||||
import 'package:wydj/tools_base/event_bus/events.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
import '../../hj_model/banner/comment_top_banner_model.dart';
|
||||
import '../../hj_model/home/plate_model.dart';
|
||||
@@ -64,14 +64,18 @@ class CommonService {
|
||||
}
|
||||
|
||||
/// 按场景拉取 Banner 列表(如评论置顶 COMMENT_TOP)
|
||||
static Future<List<CommentTopBannerModel>> fetchBannerList({required String scene}) async {
|
||||
static Future<List<CommentTopBannerModel>> fetchBannerList(
|
||||
{required String scene}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/banner/list',
|
||||
param: {'scene': scene},
|
||||
jsonTransformation: (json) {
|
||||
final list = json['list'] as List?;
|
||||
if (list == null) return <CommentTopBannerModel>[];
|
||||
final items = list.map((e) => CommentTopBannerModel.fromJson(e is Map ? Map<String, dynamic>.from(e) : null)).toList();
|
||||
final items = list
|
||||
.map((e) => CommentTopBannerModel.fromJson(
|
||||
e is Map ? Map<String, dynamic>.from(e) : null))
|
||||
.toList();
|
||||
items.sort((a, b) => (b.sort ?? 0).compareTo(a.sort ?? 0));
|
||||
return items;
|
||||
},
|
||||
@@ -100,14 +104,16 @@ class CommonService {
|
||||
param: param,
|
||||
);
|
||||
if (result.isSuccess) {
|
||||
eventBus.emit(CollectStatusModel(id: objID, isLiked: true, type: type, likeCountDelta: 1));
|
||||
eventBus.emit(CollectStatusModel(
|
||||
id: objID, isLiked: true, type: type, likeCountDelta: 1));
|
||||
}
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
//取消点赞
|
||||
//SP:长视频 SHORT:短视频 COVER:图文帖子 PIC:图集帖子 SEED_LINK:种子/黄油帖子 TAG:标签 COMMENT:评论 video:动漫 image:漫画 text:小说
|
||||
static Future<bool> cancelLike(String? objID, String? type, {List<String>? objIDArr}) async {
|
||||
static Future<bool> cancelLike(String? objID, String? type,
|
||||
{List<String>? objIDArr}) async {
|
||||
assert((type != null && type.isNotEmpty), 'cancelLike type 不能为空');
|
||||
List<String> ids = [];
|
||||
if (objIDArr?.isNotEmpty == true) {
|
||||
@@ -121,18 +127,31 @@ class CommonService {
|
||||
param: param,
|
||||
);
|
||||
if (result.isSuccess) {
|
||||
eventBus.emit(CollectStatusModel(id: objID, isLiked: false, type: type, likeCountDelta: -1));
|
||||
eventBus.emit(CollectStatusModel(
|
||||
id: objID, isLiked: false, type: type, likeCountDelta: -1));
|
||||
}
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
/// 获取我关注用户发的视频
|
||||
/// type 为0 获取用户所有动态, 1为获取用户全部视频
|
||||
static Future<VideoListResp?> fetchFollowDynamics({int page = 1, int size = 20, int? uid, int? type, String? newsType}) async {
|
||||
final param = <String, dynamic>{'pageSize': size, 'pageNumber': page, 'uid': uid, 'type': type, 'newsType': newsType};
|
||||
static Future<VideoListResp?> fetchFollowDynamics(
|
||||
{int page = 1,
|
||||
int size = 20,
|
||||
int? uid,
|
||||
int? type,
|
||||
String? newsType}) async {
|
||||
final param = <String, dynamic>{
|
||||
'pageSize': size,
|
||||
'pageNumber': page,
|
||||
'uid': uid,
|
||||
'type': type,
|
||||
'newsType': newsType
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/follow/dynamics/list',
|
||||
param: param, jsonTransformation: (json) => VideoListResp.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) => VideoListResp.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -150,7 +169,8 @@ class CommonService {
|
||||
'type': followUserType,
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/follow/list', param: param, jsonTransformation: (json) {
|
||||
final result = await httpManager.fetchResponseByGET('/follow/list',
|
||||
param: param, jsonTransformation: (json) {
|
||||
return ListBaseModel<FollowUserModel>.fromJson(json);
|
||||
});
|
||||
return result.isSuccess ? result.data : null;
|
||||
@@ -198,7 +218,8 @@ class CommonService {
|
||||
'newsType': newsType,
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/vid/recommend/list', param: param, jsonTransformation: (json) {
|
||||
final result = await httpManager.fetchResponseByGET('/vid/recommend/list',
|
||||
param: param, jsonTransformation: (json) {
|
||||
return VideoListResp.fromJson(json);
|
||||
});
|
||||
return result.isSuccess ? result.data : null;
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hgmj/hj_model/drama/drama_models.dart';
|
||||
import 'package:hgmj/hj_model/drama_media_info.dart';
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:hgmj/hj_model/media_content.dart';
|
||||
import 'package:hgmj/tools_base/net/base_resp_bean.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/drama/drama_models.dart';
|
||||
import 'package:wydj/hj_model/drama_media_info.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_model/media_content.dart';
|
||||
import 'package:wydj/tools_base/net/base_resp_bean.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
/// 短剧接口。金币解锁已并入 PayManager/BuyService,这里只剩读操作 + 两个计数型写操作:
|
||||
/// 信息流翻页去重、分享互动计数,都必须带 `X-Request-ID`——同一次业务的重试要复用同一个 id,
|
||||
/// 换新 id 就会重复计数。
|
||||
class DramaService {
|
||||
static Options _idempotent(String requestId) => Options(headers: {'X-Request-ID': requestId});
|
||||
static Options _idempotent(String requestId) =>
|
||||
Options(headers: {'X-Request-ID': requestId});
|
||||
|
||||
/// AI短剧信息流:一条 = 一部剧 + 该剧第 1 集。
|
||||
/// 排序、去重、已看沉底全在服务端,客户端不得二次排序,也不分页页码——每次请求都是下一批
|
||||
static Future<ListBaseModel<DramaFeedItem>?> fetchFeed({int size = 20, String? requestId}) async {
|
||||
static Future<ListBaseModel<DramaFeedItem>?> fetchFeed(
|
||||
{int size = 20, String? requestId}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/drama/feed',
|
||||
param: {'pageSize': size},
|
||||
@@ -29,7 +31,10 @@ class DramaService {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/drama/topics',
|
||||
jsonTransformation: (json) =>
|
||||
(json['list'] as List?)?.map((e) => DramaTopic.fromJson(e)).toList() ?? <DramaTopic>[],
|
||||
(json['list'] as List?)
|
||||
?.map((e) => DramaTopic.fromJson(e))
|
||||
.toList() ??
|
||||
<DramaTopic>[],
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -48,7 +53,8 @@ class DramaService {
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
},
|
||||
jsonTransformation: (json) => ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -104,17 +110,24 @@ class DramaService {
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/media/library/search',
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 分集列表(选集面板用) [sortType] 0-正序 1-倒序
|
||||
static Future<ListBaseModel<MediaContent>?> fetchEpisodeList(String dramaId, int page, int size) async {
|
||||
static Future<ListBaseModel<MediaContent>?> fetchEpisodeList(
|
||||
String dramaId, int page, int size) async {
|
||||
if (dramaId.isEmpty) return null; // 与 fetchDetail 一致:没有剧 id 就别发空请求
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media_content/list',
|
||||
param: {'mediaId': dramaId, 'pageNumber': page, 'pageSize': size, 'sortType': 0},
|
||||
param: {
|
||||
'mediaId': dramaId,
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
'sortType': 0
|
||||
},
|
||||
jsonTransformation: (json) => ListBaseModel<MediaContent>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
@@ -133,31 +146,42 @@ class DramaService {
|
||||
}
|
||||
|
||||
/// 我的喜欢-短剧
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchLikes(int page, {int size = 20}) async {
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchLikes(int page,
|
||||
{int size = 20}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/mine/like',
|
||||
param: {'likeType': 'drama', 'pageNumber': page, 'pageSize': size},
|
||||
jsonTransformation: (json) => ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 我的收藏-短剧 [sortType] 0-最新收藏 1-最近更新
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchFavorites(int page, {int size = 20, int sortType = 0}) async {
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchFavorites(int page,
|
||||
{int size = 20, int sortType = 0}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media_bookshelf/list',
|
||||
param: {'type': 'drama', 'sortType': sortType, 'pageNumber': page, 'pageSize': size},
|
||||
jsonTransformation: (json) => ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
param: {
|
||||
'type': 'drama',
|
||||
'sortType': sortType,
|
||||
'pageNumber': page,
|
||||
'pageSize': size
|
||||
},
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 我的已购-短剧(按剧去重,点进去看每集的 hasBuy)
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchPurchased(int page, {int size = 20}) async {
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchPurchased(int page,
|
||||
{int size = 20}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/my_buy',
|
||||
param: {'mediaType': 'drama', 'pageNumber': page, 'pageSize': size},
|
||||
jsonTransformation: (json) => ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
@@ -198,6 +222,8 @@ class DramaService {
|
||||
},
|
||||
options: _idempotent(eventId),
|
||||
);
|
||||
return result.isSuccess ? (result.data is Map ? result.data['qrCode'] : null) : null;
|
||||
return result.isSuccess
|
||||
? (result.data is Map ? result.data['qrCode'] : null)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,43 @@
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
import 'package:hgmj/tools_base/net/base_resp_bean.dart';
|
||||
import 'package:wydj/tools_base/net/base_resp_bean.dart';
|
||||
import '../../hj_page/community/group_chat/model/im_group_list_model.dart';
|
||||
import '../../hj_page/community/group_chat/model/im_message_resp.dart';
|
||||
|
||||
class GroupService {
|
||||
///获取im群组列表
|
||||
static Future<IMGroupListModel?> getList(int pageNumber, {int pageSize = 10}) async {
|
||||
static Future<IMGroupListModel?> getList(int pageNumber,
|
||||
{int pageSize = 10}) async {
|
||||
final result = await httpManager.fetchResponseByGET('/imgroup/list',
|
||||
param: {'pageNumber': pageNumber, 'pageSize': pageSize}, jsonTransformation: (json) => IMGroupListModel.fromJson(json));
|
||||
param: {'pageNumber': pageNumber, 'pageSize': pageSize},
|
||||
jsonTransformation: (json) => IMGroupListModel.fromJson(json));
|
||||
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
///获取是否加入群组详情
|
||||
static Future<BaseRespBean> getHasJoin(int? groupId) async {
|
||||
return httpManager.fetchResponseByGET('/imgroup/hasjoin', param: {'groupId': groupId});
|
||||
return httpManager
|
||||
.fetchResponseByGET('/imgroup/hasjoin', param: {'groupId': groupId});
|
||||
}
|
||||
|
||||
///获取im消息列表
|
||||
static Future<IMMessageResp> getMessages(int? groupId, int pageNumber, int pageSize) async {
|
||||
static Future<IMMessageResp> getMessages(
|
||||
int? groupId, int pageNumber, int pageSize) async {
|
||||
final result = await httpManager.fetchResponseByGET('/immessage/list',
|
||||
param: {'groupId': groupId, 'pageNumber': pageNumber, 'pageSize': pageSize},
|
||||
param: {
|
||||
'groupId': groupId,
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize
|
||||
},
|
||||
jsonTransformation: (json) => IMMessageResp.fromJson(json));
|
||||
//返回类型非空,给不了 null:失败时这里抛,由调用方 try/catch 兜底(改可空要连调用方一起改)
|
||||
return result.data;
|
||||
}
|
||||
|
||||
///发送im消息
|
||||
static Future<BaseRespBean> sendMessage(int? groupId, String? content, String? image) async {
|
||||
static Future<BaseRespBean> sendMessage(
|
||||
int? groupId, String? content, String? image) async {
|
||||
final data = {'groupId': groupId, 'content': content, 'image': image};
|
||||
data.removeWhere((k, v) => v == null);
|
||||
return httpManager.fetchResponseByPOST('/immessage/send', param: data);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:hgmj/hj_model/mine/exchange_record_model.dart';
|
||||
import 'package:hgmj/hj_model/user/user_income_info_model.dart';
|
||||
import 'package:hgmj/hj_model/user/user_info_model.dart';
|
||||
import 'package:hgmj/hj_model/video_model.dart';
|
||||
import 'package:hgmj/hj_page/main_page/provider/msg_provider.dart';
|
||||
import 'package:hgmj/tools_base/event_bus/event_bus_util.dart';
|
||||
import 'package:hgmj/tools_base/event_bus/events.dart';
|
||||
import 'package:hgmj/tools_base/global_store/store.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_model/mine/exchange_record_model.dart';
|
||||
import 'package:wydj/hj_model/user/user_income_info_model.dart';
|
||||
import 'package:wydj/hj_model/user/user_info_model.dart';
|
||||
import 'package:wydj/hj_model/video_model.dart';
|
||||
import 'package:wydj/hj_page/main_page/provider/msg_provider.dart';
|
||||
import 'package:wydj/tools_base/event_bus/event_bus_util.dart';
|
||||
import 'package:wydj/tools_base/event_bus/events.dart';
|
||||
import 'package:wydj/tools_base/global_store/store.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
import '../../hj_model/cartoon_media_info.dart';
|
||||
import '../../hj_model/message/message_dynamic_list.dart';
|
||||
@@ -79,7 +79,9 @@ class MineService {
|
||||
}
|
||||
|
||||
static Future<bool> reduceDownloadCount() async {
|
||||
return (await httpManager.fetchResponseByPOST('/mine/download/use', param: <String, dynamic>{})).isSuccess;
|
||||
return (await httpManager.fetchResponseByPOST('/mine/download/use',
|
||||
param: <String, dynamic>{}))
|
||||
.isSuccess;
|
||||
}
|
||||
|
||||
/// 获取用户信息
|
||||
@@ -96,7 +98,8 @@ class MineService {
|
||||
}
|
||||
|
||||
/// 提现明细
|
||||
static Future<WithdrawDetailsModel?> getWithdrawDetails({int pageNumber = 1, int pageSize = 10}) async {
|
||||
static Future<WithdrawDetailsModel?> getWithdrawDetails(
|
||||
{int pageNumber = 1, int pageSize = 10}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/withdraw/order',
|
||||
param: {
|
||||
@@ -159,7 +162,13 @@ class MineService {
|
||||
}
|
||||
|
||||
static Future<UserInfoModel?> mobileLogin(
|
||||
String? mobile, String? code, String? devID, String? devType, String? sysType, String? ver, String? buildID,
|
||||
String? mobile,
|
||||
String? code,
|
||||
String? devID,
|
||||
String? devType,
|
||||
String? sysType,
|
||||
String? ver,
|
||||
String? buildID,
|
||||
[String? cutInfos = ""]) async {
|
||||
final param = {
|
||||
'mobile': mobile,
|
||||
@@ -172,34 +181,45 @@ class MineService {
|
||||
'cutInfos': cutInfos
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST('/mine/mobileLoginOnly',
|
||||
param: param, jsonTransformation: (json) => UserInfoModel.fromJson(json['userInfo']));
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/mine/mobileLoginOnly',
|
||||
param: param,
|
||||
jsonTransformation: (json) => UserInfoModel.fromJson(json['userInfo']));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 积分兑换商品
|
||||
static Future<bool> integralExchange(String id, {String? name, String? tel, String? address}) async {
|
||||
final param = <String, dynamic>{"id": id, "name": name, "tel": tel, "address": address};
|
||||
static Future<bool> integralExchange(String id,
|
||||
{String? name, String? tel, String? address}) async {
|
||||
final param = <String, dynamic>{
|
||||
"id": id,
|
||||
"name": name,
|
||||
"tel": tel,
|
||||
"address": address
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST('/integral/exchangeIntegral', param: param);
|
||||
final result = await httpManager
|
||||
.fetchResponseByPOST('/integral/exchangeIntegral', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
//收藏类型 SP:长视频 SHORT:短视频 COVER:图文帖子 PIC:图集帖子 SEED_LINK:种子/黄油帖子
|
||||
static Future<bool> postCollect(String? objID, String? type, bool? isCollect) async {
|
||||
static Future<bool> postCollect(
|
||||
String? objID, String? type, bool? isCollect) async {
|
||||
final param = {'objID': objID, 'type': type, 'isCollect': isCollect};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/mine/collect',
|
||||
param: param,
|
||||
);
|
||||
eventBus.emit(CollectStatusModel(id: objID, isCollected: isCollect, type: type));
|
||||
eventBus.emit(
|
||||
CollectStatusModel(id: objID, isCollected: isCollect, type: type));
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
static Future<WalletModel?> fetchWalletData() async {
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/mine/wallet', param: {}, jsonTransformation: (json) => WalletModel.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByGET('/mine/wallet',
|
||||
param: {}, jsonTransformation: (json) => WalletModel.fromJson(json));
|
||||
if (result.data is WalletModel) {
|
||||
globalStore.wallet = result.data;
|
||||
}
|
||||
@@ -218,7 +238,8 @@ class MineService {
|
||||
}
|
||||
|
||||
//推广记录
|
||||
static Future<ListBaseModel<Promotion>?> getBindRecord(int? pageSize, int? pageNumber) async {
|
||||
static Future<ListBaseModel<Promotion>?> getBindRecord(
|
||||
int? pageSize, int? pageNumber) async {
|
||||
final param = {'pageSize': pageSize, 'pageNumber': pageNumber};
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/userinvite/userlist',
|
||||
@@ -234,18 +255,22 @@ class MineService {
|
||||
// newsType: SEED_LINK 游戏, MOVIE 视频,抖音:SP,图文:COVER
|
||||
|
||||
static Future<UserIncomeModel?> fetchIncomeInfo() async {
|
||||
final result =
|
||||
await httpManager.fetchResponseByPOST('/userinvite/info', param: {}, jsonTransformation: (json) => UserIncomeModel.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByPOST('/userinvite/info',
|
||||
param: {},
|
||||
jsonTransformation: (json) => UserIncomeModel.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 获取用户分享列表
|
||||
|
||||
static Future<InviteIncomeModel?> fetchIncomeList({int page = 1, int size = 10}) async {
|
||||
static Future<InviteIncomeModel?> fetchIncomeList(
|
||||
{int page = 1, int size = 10}) async {
|
||||
final param = <String, dynamic>{'pageSize': page, 'pageNumber': size};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST('/userinvite/incomelist',
|
||||
param: param, jsonTransformation: (json) => InviteIncomeModel.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/userinvite/incomelist',
|
||||
param: param,
|
||||
jsonTransformation: (json) => InviteIncomeModel.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -294,7 +319,8 @@ class MineService {
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/mine/buyVid',
|
||||
param: param, jsonTransformation: (json) => ListBaseModel<VideoModel>.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<VideoModel>.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -312,8 +338,10 @@ class MineService {
|
||||
'month': DateTime.now().month
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/mine/zhangdan', param: param, jsonTransformation: (json) => ListBaseModel<BillItemModel>.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByGET('/mine/zhangdan',
|
||||
param: param,
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<BillItemModel>.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -327,7 +355,8 @@ class MineService {
|
||||
'pageSize': pageSize,
|
||||
};
|
||||
final result = await httpManager.fetchResponseByPOST('/mine/transaction',
|
||||
param: param, jsonTransformation: (json) => WithdrawDetailsModel.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) => WithdrawDetailsModel.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -374,7 +403,8 @@ class MineService {
|
||||
static Future<RechargeListModel?> getChatRechargeTypes(int? type) async {
|
||||
final param = {'type': type};
|
||||
final result = await httpManager.fetchResponseByGET('/mine/currencys',
|
||||
param: param, jsonTransformation: (json) => RechargeListModel.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) => RechargeListModel.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -418,7 +448,8 @@ class MineService {
|
||||
'contentId': track?.contentId,
|
||||
'checkoutContextId': track?.checkoutContextId,
|
||||
'experimentId': (expId != null && expId.isNotEmpty) ? expId : null,
|
||||
'experimentVariant': (expVariant != null && expVariant.isNotEmpty) ? expVariant : null,
|
||||
'experimentVariant':
|
||||
(expVariant != null && expVariant.isNotEmpty) ? expVariant : null,
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
@@ -457,7 +488,8 @@ class MineService {
|
||||
}
|
||||
|
||||
//积分兑换记录
|
||||
static Future<List<CreditRecordModel>> getCreditRecords({int pageNumber = 1, int pageSize = 20}) async {
|
||||
static Future<List<CreditRecordModel>> getCreditRecords(
|
||||
{int pageNumber = 1, int pageSize = 20}) async {
|
||||
final param = {'pageNumber': pageNumber, 'pageSize': pageSize};
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/integral/record/list',
|
||||
@@ -466,7 +498,8 @@ class MineService {
|
||||
final List<CreditRecordModel> list = [];
|
||||
if (result.isSuccess) {
|
||||
List? values = result.data as List?;
|
||||
list.addAll(values?.map((e) => CreditRecordModel.fromJson(e)).toList() ?? []);
|
||||
list.addAll(
|
||||
values?.map((e) => CreditRecordModel.fromJson(e)).toList() ?? []);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -484,7 +517,8 @@ class MineService {
|
||||
}
|
||||
|
||||
/// VIP 卡片统计事件批量上报
|
||||
static Future<bool> reportAnalyticsEvents(List<VipCardAnalyticsEvent> events) async {
|
||||
static Future<bool> reportAnalyticsEvents(
|
||||
List<VipCardAnalyticsEvent> events) async {
|
||||
if (events.isEmpty) return true;
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/analytics/events',
|
||||
@@ -504,7 +538,8 @@ class MineService {
|
||||
final List<IntegralExchangeModel> list = [];
|
||||
if (result.isSuccess) {
|
||||
List? values = result.data as List?;
|
||||
list.addAll(values?.map((e) => IntegralExchangeModel.fromJson(e)).toList() ?? []);
|
||||
list.addAll(
|
||||
values?.map((e) => IntegralExchangeModel.fromJson(e)).toList() ?? []);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -521,7 +556,8 @@ class MineService {
|
||||
final List<OfficialListItemModel> list = [];
|
||||
if (result.isSuccess) {
|
||||
List? values = result.data as List?;
|
||||
list.addAll(values?.map((e) => OfficialListItemModel.fromJson(e)).toList() ?? []);
|
||||
list.addAll(
|
||||
values?.map((e) => OfficialListItemModel.fromJson(e)).toList() ?? []);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -571,7 +607,12 @@ class MineService {
|
||||
|
||||
//绑定手机
|
||||
static Future<bool> bindPhone(String phone, String code,
|
||||
{String? devID, String? smsId, String? sysType, String? ver, String? devType, String? applicationID}) async {
|
||||
{String? devID,
|
||||
String? smsId,
|
||||
String? sysType,
|
||||
String? ver,
|
||||
String? devType,
|
||||
String? applicationID}) async {
|
||||
final param = {
|
||||
'mobile': "+86$phone",
|
||||
'code': code,
|
||||
@@ -591,7 +632,8 @@ class MineService {
|
||||
}
|
||||
|
||||
//动态、收益列表
|
||||
static Future<ListBaseModel<MessageDynamicList>?> getDynamicList({int pageNumber = 1, int pageSize = 20, int? msgType}) async {
|
||||
static Future<ListBaseModel<MessageDynamicList>?> getDynamicList(
|
||||
{int pageNumber = 1, int pageSize = 20, int? msgType}) async {
|
||||
final param = {
|
||||
'pageNumber': pageNumber,
|
||||
'pageSize': pageSize,
|
||||
@@ -599,7 +641,9 @@ class MineService {
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/msg/dynamic/list',
|
||||
param: param, jsonTransformation: (json) => ListBaseModel<MessageDynamicList>.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<MessageDynamicList>.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -614,7 +658,8 @@ class MineService {
|
||||
}
|
||||
|
||||
/// 黑料社获取用户优惠券
|
||||
static Future<List<CouponModel>> fetchUserCoupons(int type, {int page = 1, int size = 0}) async {
|
||||
static Future<List<CouponModel>> fetchUserCoupons(int type,
|
||||
{int page = 1, int size = 0}) async {
|
||||
final param = <String, dynamic>{
|
||||
'type': type,
|
||||
'pageNumber': page,
|
||||
@@ -622,7 +667,12 @@ class MineService {
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/coupon/list',
|
||||
param: param, jsonTransformation: (json) => (json['list'] as List?)?.map((e) => CouponModel.fromJson(e)).toList() ?? []);
|
||||
param: param,
|
||||
jsonTransformation: (json) =>
|
||||
(json['list'] as List?)
|
||||
?.map((e) => CouponModel.fromJson(e))
|
||||
.toList() ??
|
||||
[]);
|
||||
//返回类型非空,给不了 null:失败时这里抛,由调用方 try/catch 兜底(改可空要连调用方一起改)
|
||||
return result.data;
|
||||
}
|
||||
@@ -639,7 +689,8 @@ class MineService {
|
||||
|
||||
static Future<ApBankListModel?> getBankCards() async {
|
||||
final result = await httpManager.fetchResponseByGET('/mine/txnact/yh/get',
|
||||
param: {}, jsonTransformation: (json) => ApBankListModel.fromJson(json));
|
||||
param: {},
|
||||
jsonTransformation: (json) => ApBankListModel.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -649,7 +700,12 @@ class MineService {
|
||||
String? bankCode,
|
||||
String? cardType,
|
||||
) async {
|
||||
final param = <String, dynamic>{'act': act, 'actName': actName, 'bankCode': bankCode, 'cardType': cardType};
|
||||
final param = <String, dynamic>{
|
||||
'act': act,
|
||||
'actName': actName,
|
||||
'bankCode': bankCode,
|
||||
'cardType': cardType
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/mine/txnact/yh/add',
|
||||
@@ -659,7 +715,8 @@ class MineService {
|
||||
}
|
||||
|
||||
//获取兑换码记录
|
||||
static Future<ExchangeRecordList?> getExchangeRecord(int pageNumber, int pageSize) async {
|
||||
static Future<ExchangeRecordList?> getExchangeRecord(
|
||||
int pageNumber, int pageSize) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/code/userRecord',
|
||||
param: {
|
||||
@@ -725,10 +782,17 @@ class MineService {
|
||||
String? newsType,
|
||||
int? uid,
|
||||
}) async {
|
||||
final param = <String, dynamic>{'pageNumber': page, 'pageSize': size, 'status': status, 'uid': uid, 'newsType': newsType};
|
||||
final param = <String, dynamic>{
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
'status': status,
|
||||
'uid': uid,
|
||||
'newsType': newsType
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/mine/publish',
|
||||
param: param, jsonTransformation: (json) => ListBaseModel<VideoModel>.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<VideoModel>.fromJson(json));
|
||||
//返回类型非空,给不了 null:失败时这里抛,由调用方 try/catch 兜底(改可空要连调用方一起改)
|
||||
return result.data;
|
||||
}
|
||||
@@ -737,7 +801,8 @@ class MineService {
|
||||
List<String>? ids,
|
||||
}) async {
|
||||
final param = <String, dynamic>{'ids': ids};
|
||||
final result = await httpManager.fetchResponseByPOST('/mine/publish/delete', param: param);
|
||||
final result = await httpManager.fetchResponseByPOST('/mine/publish/delete',
|
||||
param: param);
|
||||
if (result.isSuccess) {
|
||||
return '';
|
||||
}
|
||||
@@ -800,7 +865,8 @@ class MineService {
|
||||
final List<ExtraSignRewardItem> list = [];
|
||||
if (result.isSuccess) {
|
||||
List? values = result.data as List?;
|
||||
list.addAll(values?.map((e) => ExtraSignRewardItem.fromJson(e)).toList() ?? []);
|
||||
list.addAll(
|
||||
values?.map((e) => ExtraSignRewardItem.fromJson(e)).toList() ?? []);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
//搜索相关业务
|
||||
|
||||
import 'package:hgmj/hj_model/home/video_list_model.dart';
|
||||
import 'package:hgmj/hj_model/video_model.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/home/video_list_model.dart';
|
||||
import 'package:wydj/hj_model/video_model.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
class SearchService {
|
||||
/// 获取交友发布tag
|
||||
static Future<List<TagsBean>> fetchPublishTags() async {
|
||||
final result = await httpManager.fetchResponseByGET('/search/publisher/list',
|
||||
param: <String, dynamic>{}, jsonTransformation: (json) => (json['list'] as List?)?.map((e) => TagsBean.fromMap(e)).toList() ?? []);
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/search/publisher/list',
|
||||
param: <String, dynamic>{},
|
||||
jsonTransformation: (json) =>
|
||||
(json['list'] as List?)?.map((e) => TagsBean.fromMap(e)).toList() ??
|
||||
[]);
|
||||
//返回类型非空,给不了 null:失败时这里抛,由调用方 try/catch 兜底(改可空要连调用方一起改)
|
||||
return result.data;
|
||||
}
|
||||
|
||||
//猜你想要 type: 0v本周, 1本月,2上月 3.最多收藏
|
||||
static Future<VideoListResp?> getHotVideos(int pageNumber, int pageSize, int sortType) async {
|
||||
static Future<VideoListResp?> getHotVideos(
|
||||
int pageNumber, int pageSize, int sortType) async {
|
||||
final param = {
|
||||
"pageNumber": pageNumber,
|
||||
"pageSize": pageSize,
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import 'package:hgmj/hj_model/home/video_list_model.dart';
|
||||
import 'package:hgmj/hj_model/video_model.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_model/home/video_list_model.dart';
|
||||
import 'package:wydj/hj_model/video_model.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
|
||||
class TagService {
|
||||
static Future<TagsBean?> fetchInfo(String? tagId) async {
|
||||
final param = <String, dynamic>{'tagID': tagId};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByGET('/tag/info', param: param, jsonTransformation: (json) => TagsBean.fromMap(json));
|
||||
final result = await httpManager.fetchResponseByGET('/tag/info',
|
||||
param: param, jsonTransformation: (json) => TagsBean.fromMap(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 标签详情拉去视频数据
|
||||
///sortType 1、推荐,2、最新,3、最热 4、精华,5、视频
|
||||
static Future<VideoListResp?> fetchList(String? tagId,
|
||||
{int? sortType, int page = 1, int size = 10, String? newsType, String? sortBy, int? videoType}) async {
|
||||
{int? sortType,
|
||||
int page = 1,
|
||||
int size = 10,
|
||||
String? newsType,
|
||||
String? sortBy,
|
||||
int? videoType}) async {
|
||||
final param = <String, dynamic>{
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
@@ -24,8 +30,9 @@ class TagService {
|
||||
'videoType': videoType
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/tag/vid/list', param: param, jsonTransformation: (json) => VideoListResp.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByGET('/tag/vid/list',
|
||||
param: param,
|
||||
jsonTransformation: (json) => VideoListResp.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:hgmj/hj_model/list_base_model.dart';
|
||||
import 'package:hgmj/hj_model/video_model.dart';
|
||||
import 'package:hgmj/hj_utils/codec_support.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:hgmj/tools_base/toast.dart';
|
||||
import 'package:wydj/hj_model/list_base_model.dart';
|
||||
import 'package:wydj/hj_model/video_model.dart';
|
||||
import 'package:wydj/hj_utils/codec_support.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/tools_base/toast.dart';
|
||||
|
||||
import '../../config/config.dart';
|
||||
import '../../hj_model/home/video_library_model.dart';
|
||||
@@ -18,8 +18,8 @@ class VidService {
|
||||
static Future<WatchCount?> fetchWatchCount({String? vid}) async {
|
||||
final param = <String, dynamic>{'vid': vid};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/vid/user/count', param: param, jsonTransformation: (json) => WatchCount.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByGET('/vid/user/count',
|
||||
param: param, jsonTransformation: (json) => WatchCount.fromJson(json));
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ class VidService {
|
||||
'via': via,
|
||||
'tagID': tagID,
|
||||
};
|
||||
final result = await httpManager.fetchResponseByPOST('/vid/play', param: param);
|
||||
final result =
|
||||
await httpManager.fetchResponseByPOST('/vid/play', param: param);
|
||||
return result.isSuccess;
|
||||
}
|
||||
|
||||
@@ -122,7 +123,8 @@ class VidService {
|
||||
'bountyPoint': bountyPoint,
|
||||
};
|
||||
param.removeWhere((k, v) => v == null);
|
||||
final result = await httpManager.fetchResponseByPOST('/vid/submit', param: param);
|
||||
final result =
|
||||
await httpManager.fetchResponseByPOST('/vid/submit', param: param);
|
||||
if (!result.isSuccess) {
|
||||
showToast(result.toast);
|
||||
}
|
||||
@@ -133,7 +135,11 @@ class VidService {
|
||||
/// [moduleSort] 默认视频排序 1、最新,2、最热,3、最多播放量或者推荐,4、十分钟以上视频,5、精华,6、视频 9 最新热评
|
||||
/// [showType]0 默认为hj
|
||||
static Future<ModuleDetailModel?> getModuleDetail(String? subModuleID,
|
||||
{int? moduleSort, int pageNumber = 1, int pageSize = 10, int? showType, String? tagId}) async {
|
||||
{int? moduleSort,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10,
|
||||
int? showType,
|
||||
String? tagId}) async {
|
||||
final param = <String, dynamic>{
|
||||
'pageSize': pageSize,
|
||||
'pageNumber': pageNumber,
|
||||
@@ -154,7 +160,9 @@ class VidService {
|
||||
/// 只回 allVideoInfo,不含专题/精选等结构,调用方别整个替换 dataSource
|
||||
/// [refreshToken] 每次用户主动下拉换新 UUID;同一 token 重取顺序不变,所以网络层自动重试是安全的
|
||||
static Future<ModuleDetailModel?> refreshRandomModule(String? subModuleID,
|
||||
{int? moduleSort, required String refreshToken, int pageSize = 30}) async {
|
||||
{int? moduleSort,
|
||||
required String refreshToken,
|
||||
int pageSize = 30}) async {
|
||||
final param = <String, dynamic>{
|
||||
'moduleSort': moduleSort,
|
||||
'pageSize': pageSize,
|
||||
@@ -170,7 +178,8 @@ class VidService {
|
||||
}
|
||||
|
||||
///sortType 1:最新 2:本周热门 4:本月最热 5:年度最热
|
||||
static Future<VideoListResp?> getNewestModule({int? sortType, int pageNumber = 1, int pageSize = 10}) async {
|
||||
static Future<VideoListResp?> getNewestModule(
|
||||
{int? sortType, int pageNumber = 1, int pageSize = 10}) async {
|
||||
final param = <String, dynamic>{
|
||||
'pageSize': pageSize,
|
||||
'pageNumber': pageNumber,
|
||||
@@ -186,7 +195,8 @@ class VidService {
|
||||
}
|
||||
|
||||
static Future<dynamic> getShopAddress() async {
|
||||
final result = await httpManager.fetchResponseByGET('/ping/store_url', param: {});
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/ping/store_url', param: {});
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
@@ -290,14 +300,18 @@ class VidService {
|
||||
/// 获取福利任务下面的视频
|
||||
static Future<List<WareDiscountAreaData>?> fetchFreeSource() async {
|
||||
final result = await httpManager.fetchResponseByGET('/vid/discount/area',
|
||||
param: {}, jsonTransformation: (json) => (json['List'] as List?)?.map((e) => WareDiscountAreaData.fromJson(e)).toList());
|
||||
param: {},
|
||||
jsonTransformation: (json) => (json['List'] as List?)
|
||||
?.map((e) => WareDiscountAreaData.fromJson(e))
|
||||
.toList());
|
||||
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 获取免费福利视频数据
|
||||
/// [sortType] 0 = 福利任务下面 1 最新 2最热
|
||||
static Future<ListBaseModel<VideoModel>> fetchFreeSourceList(String id, {int page = 1, int size = 10, required int sortType}) async {
|
||||
static Future<ListBaseModel<VideoModel>> fetchFreeSourceList(String id,
|
||||
{int page = 1, int size = 10, required int sortType}) async {
|
||||
final param = {
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
@@ -305,7 +319,8 @@ class VidService {
|
||||
'sortType': sortType,
|
||||
};
|
||||
final result = await httpManager.fetchResponseByGET('/vid/discount/list',
|
||||
param: param, jsonTransformation: (json) => ListBaseModel<VideoModel>.fromJson(json));
|
||||
param: param,
|
||||
jsonTransformation: (json) => ListBaseModel<VideoModel>.fromJson(json));
|
||||
//返回类型非空,给不了 null:失败时这里抛,由调用方 try/catch 兜底(改可空要连调用方一起改)
|
||||
return result.data;
|
||||
}
|
||||
@@ -313,7 +328,8 @@ class VidService {
|
||||
/// 获取跑马灯数据
|
||||
static Future<List<MarqueeModel>?> fetchAnnounce(int type) async {
|
||||
final param = <String, dynamic>{"type": type};
|
||||
final result = await httpManager.fetchResponseByGET('/mine/announce/list', param: param);
|
||||
final result = await httpManager.fetchResponseByGET('/mine/announce/list',
|
||||
param: param);
|
||||
|
||||
final List<MarqueeModel> list = [];
|
||||
if (result.isSuccess) {
|
||||
@@ -368,15 +384,17 @@ class VidService {
|
||||
|
||||
/// 获取片库筛选条件
|
||||
static Future<HomeVideoLibrary> fetchLibrary() async {
|
||||
final result =
|
||||
await httpManager.fetchResponseByGET('/vid/library', param: {}, jsonTransformation: (json) => HomeVideoLibrary.fromJson(json));
|
||||
final result = await httpManager.fetchResponseByGET('/vid/library',
|
||||
param: {},
|
||||
jsonTransformation: (json) => HomeVideoLibrary.fromJson(json));
|
||||
//返回类型非空,给不了 null:失败时这里抛,由调用方 try/catch 兜底(改可空要连调用方一起改)
|
||||
return result.data;
|
||||
}
|
||||
|
||||
///视频专题样式-更换一批
|
||||
static Future<VideoListResp> sectionVideoExchange(String? sectionID) async {
|
||||
final result = await httpManager.fetchResponseByGET('/vid/section/changeVideo/$sectionID', param: {});
|
||||
final result = await httpManager
|
||||
.fetchResponseByGET('/vid/section/changeVideo/$sectionID', param: {});
|
||||
//失败时 data 是原始结构,别拿它当模型解析;fromJson 内部 json ??= {},传 null 就是空列表
|
||||
return VideoListResp.fromJson(result.isSuccess ? result.data : null);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hgmj/hj_utils/store_keys.dart';
|
||||
import 'package:hgmj/track_event_manager/device_service.dart';
|
||||
import 'package:wydj/hj_utils/store_keys.dart';
|
||||
import 'package:wydj/track_event_manager/device_service.dart';
|
||||
|
||||
import 'light_model.dart';
|
||||
|
||||
@@ -34,7 +34,8 @@ class CodecSupport {
|
||||
static Future<void> init() async {
|
||||
_deviceDisabled = await lightKV.getBool(StoreKeys.H265_DISABLED) ?? false;
|
||||
try {
|
||||
_hwSupport = await _channel.invokeMethod<bool>('isH265HardwareSupported') ?? false;
|
||||
_hwSupport =
|
||||
await _channel.invokeMethod<bool>('isH265HardwareSupported') ?? false;
|
||||
} catch (e) {
|
||||
_hwSupport = false;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:hgmj/hj_utils/text_util.dart';
|
||||
import 'package:hgmj/tools_base/net/net_manager.dart';
|
||||
import 'package:wydj/hj_utils/text_util.dart';
|
||||
import 'package:wydj/tools_base/net/net_manager.dart';
|
||||
|
||||
class DateTimeUtil {
|
||||
/// 计算当前时间差值
|
||||
static int calTime3(String? date) {
|
||||
if (TextUtil.isEmpty(date) || date?.contains("0001-01-01") == true) return -1;
|
||||
if (TextUtil.isEmpty(date) || date?.contains("0001-01-01") == true)
|
||||
return -1;
|
||||
final time = DateTime.parse(utc2iso(date)).toLocal();
|
||||
return time.difference(DateTime.now()).inSeconds;
|
||||
}
|
||||
@@ -63,7 +64,8 @@ class DateTimeUtil {
|
||||
}
|
||||
|
||||
/// utc 转 年月日
|
||||
static String utcTurnYear(String? date, {String? char, bool? isChina, bool showHM = false}) {
|
||||
static String utcTurnYear(String? date,
|
||||
{String? char, bool? isChina, bool showHM = false}) {
|
||||
if (date == null || date.isEmpty) return '';
|
||||
final gap = char ?? '-';
|
||||
final dt = DateTime.parse(date).toLocal();
|
||||
@@ -106,7 +108,8 @@ class DateTimeUtil {
|
||||
}
|
||||
|
||||
/// 时长 → MM:SS / HH:MM:SS(统一走 [formatHMS])
|
||||
static String? formatDuration(Duration position) => formatHMS(position.inSeconds);
|
||||
static String? formatDuration(Duration position) =>
|
||||
formatHMS(position.inSeconds);
|
||||
|
||||
/// 秒 → (时, 分, 秒) 各两位补零字符串;时为总小时数(不取模 24),供分段倒计时 UI 用
|
||||
static (String, String, String) hms(int seconds) => (
|
||||
@@ -165,7 +168,8 @@ bool _isSameDay(DateTime a, DateTime b) =>
|
||||
a.year == b.year && a.month == b.month && a.day == b.day;
|
||||
|
||||
/// 1 小时内 →「X分钟前」;当天 →「HH:mm」;其余 →「MM-DD HH:mm」;非正常时间 →「—」
|
||||
String formatTime(String? utcTime, {String gap = '-', bool hasTimeGap = false}) {
|
||||
String formatTime(String? utcTime,
|
||||
{String gap = '-', bool hasTimeGap = false}) {
|
||||
final date = _parseFormatTimeDate(utcTime);
|
||||
if (date == null) return kFormatTimeFallback;
|
||||
|
||||
@@ -189,7 +193,8 @@ String formatTimeTwo(String? utcTime) {
|
||||
if (utcTime?.isNotEmpty != true) return '近期';
|
||||
final now = DateTime.now();
|
||||
final date = DateTime.parse(utcTime!).toLocal();
|
||||
final changeTime = (now.millisecondsSinceEpoch - date.millisecondsSinceEpoch) ~/ 1000;
|
||||
final changeTime =
|
||||
(now.millisecondsSinceEpoch - date.millisecondsSinceEpoch) ~/ 1000;
|
||||
|
||||
final s = changeTime;
|
||||
if (s >= 0 && s < 60) return '刚刚';
|
||||
@@ -207,6 +212,7 @@ String formatTimeTwo(String? utcTime) {
|
||||
if (month > 0 && month < 12) return DateTimeUtil.utc2isoMD(utcTime);
|
||||
|
||||
final year = month ~/ 12;
|
||||
if (year > 0 && year < 12) return DateTimeUtil.utcTurnYear(utcTime, isChina: true);
|
||||
if (year > 0 && year < 12)
|
||||
return DateTimeUtil.utcTurnYear(utcTime, isChina: true);
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hgmj/config/config.dart';
|
||||
import 'package:hgmj/hj_utils/dns_solve/dnsolve.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:hgmj/hj_utils/light_model.dart';
|
||||
import 'package:hgmj/hj_utils/store_keys.dart';
|
||||
import 'package:hgmj/hj_utils/text_util.dart';
|
||||
import 'package:hgmj/tools_base/debug_log.dart';
|
||||
import 'package:wydj/config/config.dart';
|
||||
import 'package:wydj/hj_utils/dns_solve/dnsolve.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/hj_utils/light_model.dart';
|
||||
import 'package:wydj/hj_utils/store_keys.dart';
|
||||
import 'package:wydj/hj_utils/text_util.dart';
|
||||
import 'package:wydj/tools_base/debug_log.dart';
|
||||
|
||||
//选线管理
|
||||
class DetectLineManager {
|
||||
@@ -96,7 +96,8 @@ class DetectLineManager {
|
||||
),
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
debugLog("ping", "pingCheck()...line:$line cost ${DateTime.now().difference(startTime).inMilliseconds} milSeconds");
|
||||
debugLog("ping",
|
||||
"pingCheck()...line:$line cost ${DateTime.now().difference(startTime).inMilliseconds} milSeconds");
|
||||
if (TextUtil.isNotEmpty(resp.time)) {
|
||||
httpManager.setServerTime(resp.time);
|
||||
}
|
||||
@@ -122,7 +123,11 @@ class DetectLineManager {
|
||||
if (record.data.isEmpty) continue;
|
||||
debugLog("selectLine", "dnsSolve ==== ${record.data}");
|
||||
//DNS TXT 值带引号,去掉后按 _ 拆成多条线路
|
||||
final lines = record.data.split('_').map((e) => e.replaceAll("\"", "")).where(TextUtil.isNotEmpty).toList();
|
||||
final lines = record.data
|
||||
.split('_')
|
||||
.map((e) => e.replaceAll("\"", ""))
|
||||
.where(TextUtil.isNotEmpty)
|
||||
.toList();
|
||||
if (lines.isEmpty) continue; // 这条记录没解析出线路,接着看下一条,别直接放弃
|
||||
lightKV.setStringList(StoreKeys.DETECT_LINE, lines);
|
||||
return await _pingCheckBatch(lines);
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hgmj/extension/extensions.dart';
|
||||
import 'package:hgmj/hj_utils/text_util.dart';
|
||||
import 'package:hgmj/tools_base/debug_log.dart';
|
||||
import 'package:wydj/extension/extensions.dart';
|
||||
import 'package:wydj/hj_utils/text_util.dart';
|
||||
import 'package:wydj/tools_base/debug_log.dart';
|
||||
|
||||
const KB_SIZE = 1024;
|
||||
const MB_SIZE = 1024 * KB_SIZE;
|
||||
@@ -34,7 +34,8 @@ class FileUtil {
|
||||
/// [offset] 起始偏移位置
|
||||
/// [blockSize] 分块大小
|
||||
/// [file] 文件
|
||||
static Future<Uint8List> getFileBlock(int offset, int blockSize, File file) async {
|
||||
static Future<Uint8List> getFileBlock(
|
||||
int offset, int blockSize, File file) async {
|
||||
RandomAccessFile? accessFile;
|
||||
try {
|
||||
accessFile = await file.open();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:hgmj/tools_base/debug_log.dart';
|
||||
import 'package:wydj/tools_base/debug_log.dart';
|
||||
|
||||
import '../hj_model/splash/watch_count_model.dart';
|
||||
import '../hj_model/video_model.dart';
|
||||
@@ -32,7 +32,8 @@ class FreePlayManager {
|
||||
/// 故这些阶段推迟到帧末再通知;空闲/帧末阶段则立即通知。
|
||||
void _bumpRevision() {
|
||||
final phase = SchedulerBinding.instance.schedulerPhase;
|
||||
final safeNow = phase == SchedulerPhase.idle || phase == SchedulerPhase.postFrameCallbacks;
|
||||
final safeNow = phase == SchedulerPhase.idle ||
|
||||
phase == SchedulerPhase.postFrameCallbacks;
|
||||
if (safeNow) {
|
||||
revision.value++;
|
||||
return;
|
||||
@@ -49,7 +50,9 @@ class FreePlayManager {
|
||||
/// 合并去重而不是清空重填:两个 await 期间可能有视频正好扣了次数 add 进来,清掉会导致它被重复扣
|
||||
Future<void> refresh() async {
|
||||
remain = await VidService.fetchWatchCount();
|
||||
final saved = await lightKV.getStringList(StoreKeys.NEW_FREE_WATCH_VIDEOS) ?? const <String>[];
|
||||
final saved =
|
||||
await lightKV.getStringList(StoreKeys.NEW_FREE_WATCH_VIDEOS) ??
|
||||
const <String>[];
|
||||
usedIds.addAll(saved.where((e) => !usedIds.contains(e)));
|
||||
_bumpRevision();
|
||||
}
|
||||
@@ -100,7 +103,8 @@ class FreePlayManager {
|
||||
// 后台上报,成功才落本地缓存;失败只记日志、不影响本次放行
|
||||
// 注:上报是异步的,异常只能用 catchError 接(try-catch 包不住 then 里的异步错误)
|
||||
VidService.fetchWatchCount(vid: video.subid ?? id).then<void>((value) {
|
||||
if (value != null) lightKV.setStringList(StoreKeys.NEW_FREE_WATCH_VIDEOS, usedIds);
|
||||
if (value != null)
|
||||
lightKV.setStringList(StoreKeys.NEW_FREE_WATCH_VIDEOS, usedIds);
|
||||
}).catchError((Object e) => debugLog('免费次数上报失败', e));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:hgmj/hj_model/home/collection_model.dart';
|
||||
import 'package:hgmj/hj_model/video_model.dart';
|
||||
import 'package:hgmj/hj_utils/const.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/base_history_record_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/cartoon_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/comics_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/community_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/game_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/novel_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/pic_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/search_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/short_video_history_store.dart';
|
||||
import 'package:hgmj/tools_base/cache/history/video_history_store.dart';
|
||||
import 'package:hgmj/tools_base/debug_log.dart';
|
||||
import 'package:wydj/hj_model/home/collection_model.dart';
|
||||
import 'package:wydj/hj_model/video_model.dart';
|
||||
import 'package:wydj/hj_utils/const.dart';
|
||||
import 'package:wydj/tools_base/cache/history/base_history_record_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/cartoon_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/comics_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/community_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/game_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/novel_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/pic_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/search_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/short_video_history_store.dart';
|
||||
import 'package:wydj/tools_base/cache/history/video_history_store.dart';
|
||||
import 'package:wydj/tools_base/debug_log.dart';
|
||||
|
||||
import '../hj_model/cartoon_media_info.dart';
|
||||
|
||||
@@ -74,7 +74,8 @@ class HistoryUtil {
|
||||
}
|
||||
|
||||
/// 指定类型的浏览历史(按时间倒序,分页)。解析不出的条目直接丢弃,不让一条脏数据废掉整页
|
||||
static Future<List<T>> fetch<T>(MediaStyle type, {int page = 1, int pageSize = 20}) async {
|
||||
static Future<List<T>> fetch<T>(MediaStyle type,
|
||||
{int page = 1, int pageSize = 20}) async {
|
||||
try {
|
||||
final store = _storeOf(type);
|
||||
if (store == null) return <T>[];
|
||||
@@ -112,10 +113,12 @@ class HistoryUtil {
|
||||
}
|
||||
|
||||
//搜索历史(按时间倒序)
|
||||
static Future<List<String>> searchHistories() => SearchHistoryStore().fetchAll();
|
||||
static Future<List<String>> searchHistories() =>
|
||||
SearchHistoryStore().fetchAll();
|
||||
|
||||
//新增一条搜索历史(已存在则去重提到最前,超上限自动删最旧)
|
||||
static Future<void> addSearch(String keyword) => SearchHistoryStore().save(keyword);
|
||||
static Future<void> addSearch(String keyword) =>
|
||||
SearchHistoryStore().save(keyword);
|
||||
|
||||
//清空搜索历史
|
||||
static Future<void> clearSearch() => SearchHistoryStore().clean();
|
||||
|
||||
@@ -10,8 +10,8 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_native_image/flutter_native_image.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:hgmj/tools_base/debug_log.dart';
|
||||
import 'package:hgmj/tools_base/toast.dart';
|
||||
import 'package:wydj/tools_base/debug_log.dart';
|
||||
import 'package:wydj/tools_base/toast.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:zxing2/qrcode.dart';
|
||||
@@ -23,7 +23,8 @@ class ImageUtil {
|
||||
static Future<String?> decodeQr(String path) async {
|
||||
var decodePath = path;
|
||||
try {
|
||||
final converted = await FlutterNativeImage.compressImage(path, percentage: 100, quality: 100);
|
||||
final converted = await FlutterNativeImage.compressImage(path,
|
||||
percentage: 100, quality: 100);
|
||||
decodePath = converted.path;
|
||||
} catch (_) {
|
||||
// 转换失败就拿原图碰运气(本就是 jpg/png 时不影响)
|
||||
@@ -39,7 +40,11 @@ class ImageUtil {
|
||||
final source = RGBLuminanceSource(
|
||||
image.width,
|
||||
image.height,
|
||||
image.convert(numChannels: 4).getBytes(order: img.ChannelOrder.rgba).buffer.asInt32List(),
|
||||
image
|
||||
.convert(numChannels: 4)
|
||||
.getBytes(order: img.ChannelOrder.rgba)
|
||||
.buffer
|
||||
.asInt32List(),
|
||||
);
|
||||
final bitmap = BinaryBitmap(HybridBinarizer(source));
|
||||
// tryHarder:相册照片有边框/轻微旋转/噪点,放开更费时但识别率更高
|
||||
@@ -54,7 +59,8 @@ class ImageUtil {
|
||||
/// 注:boundary 必须完整渲染在屏幕内,被滚动容器裁剪会截不全甚至失败
|
||||
static Future<bool> saveWidgetToAlbum(GlobalKey boundaryKey) async {
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
final boundary = boundaryKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
|
||||
final boundary = boundaryKey.currentContext?.findRenderObject()
|
||||
as RenderRepaintBoundary?;
|
||||
if (boundary == null) return false;
|
||||
|
||||
Image? image;
|
||||
@@ -112,7 +118,8 @@ class ImageUtil {
|
||||
if (decoder == null) return null;
|
||||
decoder.startDecode(bytes); //只解头/块信息,不解像素,很便宜
|
||||
final total = decoder.numFrames();
|
||||
debugLog('saveToAlbum', '${decoder.runtimeType} frames=$total size=${bytes.length}');
|
||||
debugLog('saveToAlbum',
|
||||
'${decoder.runtimeType} frames=$total size=${bytes.length}');
|
||||
if (total <= 1) return null;
|
||||
|
||||
// 必须整体 decode:animated webp 的每帧只是个局部矩形,靠 dispose/blend 往画布上叠,
|
||||
@@ -133,11 +140,16 @@ class ImageUtil {
|
||||
var delay = 0;
|
||||
for (var i = 0; i < src.numFrames; i += step) {
|
||||
//frames[0] 就是 src 本身、挂着整条动画,copyResize 会连整条一起缩放,得先取单帧副本
|
||||
final f = i == 0 ? img.Image.from(src.frames[0], noAnimation: true) : src.frames[i];
|
||||
final f = i == 0
|
||||
? img.Image.from(src.frames[0], noAnimation: true)
|
||||
: src.frames[i];
|
||||
//必须指定 average:copyResize 默认是 nearest,降采样直接丢像素,出来全是锯齿
|
||||
final out = (f.width == outW && f.height == outH)
|
||||
? f
|
||||
: img.copyResize(f, width: outW, height: outH, interpolation: img.Interpolation.average);
|
||||
: img.copyResize(f,
|
||||
width: outW,
|
||||
height: outH,
|
||||
interpolation: img.Interpolation.average);
|
||||
sink.writeFromSync(out.getBytes(order: img.ChannelOrder.rgba));
|
||||
if (delay == 0 && f.frameDuration > 0) delay = f.frameDuration;
|
||||
count++;
|
||||
@@ -145,9 +157,15 @@ class ImageUtil {
|
||||
sink.closeSync();
|
||||
if (count < 2) return null;
|
||||
|
||||
debugLog('saveToAlbum', 'raw ${outW}x$outH frames=$count step=$step delay=$delay');
|
||||
debugLog('saveToAlbum',
|
||||
'raw ${outW}x$outH frames=$count step=$step delay=$delay');
|
||||
//抽帧后帧间隔要乘上 step,整体播放速度才不变;源没写时长就兜 80ms
|
||||
return {'w': outW, 'h': outH, 'n': count, 'delay': (delay > 0 ? delay : 80) * step};
|
||||
return {
|
||||
'w': outW,
|
||||
'h': outH,
|
||||
'n': count,
|
||||
'delay': (delay > 0 ? delay : 80) * step
|
||||
};
|
||||
} catch (e) {
|
||||
debugLog('saveToAlbum', 'decodeToRaw failed: $e');
|
||||
return null;
|
||||
@@ -155,17 +173,20 @@ class ImageUtil {
|
||||
}
|
||||
|
||||
/// RGBA 裸流 → H.264 mp4(x264 在 -gpl 变体里才有)
|
||||
static Future<Uint8List?> _rawToMp4(String rawPath, Map<String, int> meta) async {
|
||||
static Future<Uint8List?> _rawToMp4(
|
||||
String rawPath, Map<String, int> meta) async {
|
||||
final outPath = rawPath.replaceFirst(RegExp(r'\.rgba$'), '.mp4');
|
||||
final fps = (1000 / (meta['delay'] ?? 80)).clamp(1.0, 60.0);
|
||||
final cmd = '-y -f rawvideo -pixel_format rgba -video_size ${meta['w']}x${meta['h']} '
|
||||
final cmd =
|
||||
'-y -f rawvideo -pixel_format rgba -video_size ${meta['w']}x${meta['h']} '
|
||||
'-framerate ${fps.toStringAsFixed(2)} -i "$rawPath" '
|
||||
'-c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p -movflags +faststart "$outPath"';
|
||||
final out = File(outPath);
|
||||
try {
|
||||
final session = await FFmpegKit.execute(cmd);
|
||||
if (!ReturnCode.isSuccess(await session.getReturnCode())) {
|
||||
debugLog('saveToAlbum', 'ffmpeg failed: ${await session.getAllLogsAsString()}');
|
||||
debugLog('saveToAlbum',
|
||||
'ffmpeg failed: ${await session.getAllLogsAsString()}');
|
||||
return null;
|
||||
}
|
||||
return await out.readAsBytes();
|
||||
@@ -196,10 +217,13 @@ class ImageUtil {
|
||||
// 而那条分支用 URL(string:) 解析,裸路径没 scheme 会被 PhotoKit 拒,得传 file:// 形式
|
||||
final isVideo = ext == 'mp4';
|
||||
final result = await ImageGallerySaver.saveFile(
|
||||
(Platform.isIOS && !isVideo) ? Uri.file(file.path).toString() : file.path,
|
||||
(Platform.isIOS && !isVideo)
|
||||
? Uri.file(file.path).toString()
|
||||
: file.path,
|
||||
name: fileName,
|
||||
isReturnPathOfIOS: !isVideo,
|
||||
).timeout(const Duration(seconds: 20), onTimeout: () => null); //插件在拿不到 fullSizeImageURL 时不回调,兜底防卡死
|
||||
).timeout(const Duration(seconds: 20),
|
||||
onTimeout: () => null); //插件在拿不到 fullSizeImageURL 时不回调,兜底防卡死
|
||||
return result is Map && result["isSuccess"] == true;
|
||||
} catch (e) {
|
||||
debugLog('saveRawToAlbum 写入相册失败', e);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:hgmj/hj_utils/text_util.dart';
|
||||
import 'package:wydj/hj_utils/text_util.dart';
|
||||
import 'package:mmkv/mmkv.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:hgmj/extension/extensions.dart';
|
||||
import 'package:hgmj/hj_utils/text_util.dart';
|
||||
import 'package:hgmj/hj_utils/video_cache_manager.dart';
|
||||
import 'package:hgmj/tools_base/toast.dart';
|
||||
import 'package:wydj/extension/extensions.dart';
|
||||
import 'package:wydj/hj_utils/text_util.dart';
|
||||
import 'package:wydj/hj_utils/video_cache_manager.dart';
|
||||
import 'package:wydj/tools_base/toast.dart';
|
||||
|
||||
import '../tools_base/cache/cancel_token_manager.dart';
|
||||
import '../tools_base/net/load_apk/dio_cli.dart';
|
||||
@@ -61,7 +61,8 @@ class ReqFilter {
|
||||
final int? port; // null or 12345
|
||||
final String pathPrefix; //m3u8 是api/app/vid/m3u8
|
||||
final HeadersBuilder? headersBuilder;
|
||||
ReqFilter(this.reg, this.schema, this.host, {this.port, this.pathPrefix = "", this.headersBuilder})
|
||||
ReqFilter(this.reg, this.schema, this.host,
|
||||
{this.port, this.pathPrefix = "", this.headersBuilder})
|
||||
: assert(TextUtil.isNotEmpty(reg)),
|
||||
assert(TextUtil.isNotEmpty(host));
|
||||
|
||||
@@ -192,7 +193,8 @@ class CacheServer {
|
||||
await stop(true);
|
||||
_server = await _bindAlways();
|
||||
csPrint("_startInner()...begin listen:$serverPort");
|
||||
_server?.listen(_onRequest, onDone: _onServerDone, onError: _onServerError, cancelOnError: true);
|
||||
_server?.listen(_onRequest,
|
||||
onDone: _onServerDone, onError: _onServerError, cancelOnError: true);
|
||||
|
||||
csPrint("_bind success, start listen address:$localServerUri");
|
||||
_startCompleter?.complete();
|
||||
@@ -230,7 +232,8 @@ class CacheServer {
|
||||
/// 请求转发过滤器
|
||||
/// 根据文件后缀转发
|
||||
/// [reg] 正则表达式fileExtension需要带上. ".m3u8" ".ts"
|
||||
void addReqFilter(String reg, String forwardUrl, {bool force = false, String pathPrefix = "", HeadersBuilder? hb}) {
|
||||
void addReqFilter(String reg, String forwardUrl,
|
||||
{bool force = false, String pathPrefix = "", HeadersBuilder? hb}) {
|
||||
assert(reg.startsWith("."));
|
||||
if (TextUtil.isEmpty(forwardUrl)) {
|
||||
showToast("CDN地址为空");
|
||||
@@ -241,7 +244,8 @@ class CacheServer {
|
||||
return;
|
||||
}
|
||||
// 转发结构体
|
||||
final reqFilter = ReqFilter(reg, uri.scheme, uri.host, port: uri.port, pathPrefix: pathPrefix, headersBuilder: hb);
|
||||
final reqFilter = ReqFilter(reg, uri.scheme, uri.host,
|
||||
port: uri.port, pathPrefix: pathPrefix, headersBuilder: hb);
|
||||
if (force) {
|
||||
// 不同的转发域,对应的转发函数
|
||||
_reqFilterMap[reg] = reqFilter;
|
||||
@@ -251,7 +255,8 @@ class CacheServer {
|
||||
}
|
||||
|
||||
/// 添加外部系统拦截调用,对于localserver一般是ttl
|
||||
void addCustomResponse(String path, CustomResponse response, {bool force = false}) {
|
||||
void addCustomResponse(String path, CustomResponse response,
|
||||
{bool force = false}) {
|
||||
if (force) {
|
||||
_customResponse[path] = response;
|
||||
} else {
|
||||
@@ -260,7 +265,8 @@ class CacheServer {
|
||||
}
|
||||
|
||||
/// 获取localserver的信息
|
||||
Uri get localServerUri => Uri(scheme: "http", host: _server?.address.host, port: _server?.port);
|
||||
Uri get localServerUri =>
|
||||
Uri(scheme: "http", host: _server?.address.host, port: _server?.port);
|
||||
|
||||
/// localserver的请求拦截
|
||||
void _onRequest(HttpRequest request) {
|
||||
@@ -331,7 +337,8 @@ class CacheServer {
|
||||
|
||||
// 根据文件扩展名字获取请求过滤
|
||||
final fileExtension = localReq.uri.path.substring(dot);
|
||||
var reqFilter = _reqFilterMap[fileExtension] ?? _reqFilterMap[LOCAL_ALL_FILTER];
|
||||
var reqFilter =
|
||||
_reqFilterMap[fileExtension] ?? _reqFilterMap[LOCAL_ALL_FILTER];
|
||||
if (reqFilter == null) {
|
||||
//l.e(local_server_tag, "_handleGet path not find ${localReq.uri} reqFilter:$fileExtension");
|
||||
localReq.response.statusCode = HttpStatus.badRequest;
|
||||
@@ -353,7 +360,8 @@ class CacheServer {
|
||||
scheme: reqFilter.schema,
|
||||
host: reqFilter.host,
|
||||
port: reqFilter.port,
|
||||
queryParameters: fileExtension == LOCAL_TS_FILTER ? {} : queryParameters);
|
||||
queryParameters:
|
||||
fileExtension == LOCAL_TS_FILTER ? {} : queryParameters);
|
||||
var remoteUriStr = remoteUri.toString();
|
||||
if (remoteUriStr.endsWith("?")) {
|
||||
remoteUri = Uri.parse(remoteUriStr.replaceAll("?", ""));
|
||||
@@ -471,7 +479,9 @@ class CacheServer {
|
||||
|
||||
var cacheManager = _getCacheManager(remoteUri.toString());
|
||||
var s = cacheManager
|
||||
.getFileStream(remoteUri.toString(), headers: localReqHeaders, withProgress: fileExtension != LOCAL_M3U8_FILTER)
|
||||
.getFileStream(remoteUri.toString(),
|
||||
headers: localReqHeaders,
|
||||
withProgress: fileExtension != LOCAL_M3U8_FILTER)
|
||||
.handleError(
|
||||
(e) {
|
||||
if (!isPreCache) {
|
||||
@@ -479,7 +489,9 @@ class CacheServer {
|
||||
}
|
||||
// TODO 是否需要处理
|
||||
//l.e(local_server_tag, "getFileStream()...开始处理远端请求$remoteUri 错误");
|
||||
if (TextUtil.isNotEmpty(cacheKey) && cacheKey.contains(LOCAL_M3U8_FILTER) && !failedM3u8List.contains(cacheKey)) {
|
||||
if (TextUtil.isNotEmpty(cacheKey) &&
|
||||
cacheKey.contains(LOCAL_M3U8_FILTER) &&
|
||||
!failedM3u8List.contains(cacheKey)) {
|
||||
//l.e(local_server_tag, "getFileStream()...添加到失败错误列表$cacheKey");
|
||||
failedM3u8List.add(cacheKey);
|
||||
}
|
||||
@@ -513,10 +525,12 @@ class CacheServer {
|
||||
}
|
||||
|
||||
/// 处理M3u8
|
||||
_handleM3u8(Uri remoteUri, HttpRequest localReq, Stream<FileResponse> s, BaseCacheManager cacheManager) async {
|
||||
_handleM3u8(Uri remoteUri, HttpRequest localReq, Stream<FileResponse> s,
|
||||
BaseCacheManager cacheManager) async {
|
||||
final sc = s.listen((f) {
|
||||
if (f is FileInfo) {
|
||||
var len = localReq.response.headers.value(HttpHeaders.contentLengthHeader);
|
||||
var len =
|
||||
localReq.response.headers.value(HttpHeaders.contentLengthHeader);
|
||||
if (null != len) {
|
||||
// 避���文件30天过期之后重新请求再次发送
|
||||
//l.e(local_server_tag, "handleM3u8()...$remoteUri already send resp to user now skip");
|
||||
@@ -548,11 +562,14 @@ class CacheServer {
|
||||
// .add(HttpHeaders.contentLengthHeader, afterSize);
|
||||
// localReq.response.headers
|
||||
// .add(HttpHeaders.contentTypeHeader, "application/octet-stream");
|
||||
localReq.response.headers.add(HttpHeaders.contentLengthHeader, afterSize);
|
||||
localReq.response.headers.add(HttpHeaders.contentTypeHeader, "application/octet-stream");
|
||||
localReq.response.headers
|
||||
.add(HttpHeaders.contentLengthHeader, afterSize);
|
||||
localReq.response.headers
|
||||
.add(HttpHeaders.contentTypeHeader, "application/octet-stream");
|
||||
// localReq.response.contentLength = source.length;
|
||||
localReq.response.add(source.codeUnits);
|
||||
csPrint("handleM3u8()...本地请求$remoteUri 完成:sourceSize:$sourceSize afterSize:$afterSize");
|
||||
csPrint(
|
||||
"handleM3u8()...本地请求$remoteUri 完成:sourceSize:$sourceSize afterSize:$afterSize");
|
||||
localReq.response.close();
|
||||
} else {
|
||||
//l.e(local_server_tag, "handleM3u8()...本地请求$remoteUri 完成但是���件不存在");
|
||||
@@ -681,7 +698,11 @@ class CacheServer {
|
||||
} else {
|
||||
//这里替换local127.0.0.1
|
||||
var localUrl = remoteUri
|
||||
.replace(scheme: localServerUri.scheme, host: localServerUri.host, port: localServerUri.port, queryParameters: query)
|
||||
.replace(
|
||||
scheme: localServerUri.scheme,
|
||||
host: localServerUri.host,
|
||||
port: localServerUri.port,
|
||||
queryParameters: query)
|
||||
.toString();
|
||||
return localUrl;
|
||||
}
|
||||
@@ -707,7 +728,8 @@ class CacheServer {
|
||||
}
|
||||
var uri = Uri.parse(remotePath);
|
||||
//这里替换远程
|
||||
final remoteUri = uri.replace(scheme: reqFilter.schema, host: reqFilter.host, port: reqFilter.port);
|
||||
final remoteUri = uri.replace(
|
||||
scheme: reqFilter.schema, host: reqFilter.host, port: reqFilter.port);
|
||||
return remoteUri.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:hgmj/tools_base/widget/common_alert.dart';
|
||||
import 'package:wydj/tools_base/widget/common_alert.dart';
|
||||
|
||||
import '../tools_base/toast.dart';
|
||||
|
||||
@@ -15,13 +15,17 @@ class PermissionUtil {
|
||||
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||
// 按 API 级别(sdkInt)判断:Android 13(API 33)起用照片权限,以下用存储权限。
|
||||
// 不能用 version.release(营销版本号字符串,ROM 可能写成 "12L" 等致解析跑偏)
|
||||
permission = androidInfo.version.sdkInt < 33 ? Permission.storage : Permission.photos;
|
||||
permission = androidInfo.version.sdkInt < 33
|
||||
? Permission.storage
|
||||
: Permission.photos;
|
||||
}
|
||||
return _ensure(permission, '需要相册权限才能继续');
|
||||
}
|
||||
|
||||
static Future<bool> checkPhotoAddOnlyPermission() async {
|
||||
final permission = (Platform.isIOS || Platform.isMacOS) ? Permission.photosAddOnly : Permission.storage;
|
||||
final permission = (Platform.isIOS || Platform.isMacOS)
|
||||
? Permission.photosAddOnly
|
||||
: Permission.storage;
|
||||
return _ensure(permission, '需要相册权限才能继续');
|
||||
}
|
||||
|
||||
@@ -39,7 +43,8 @@ class PermissionUtil {
|
||||
// 会误判成永久拒绝、不弹框直接去设置。所以 Android 一律先 request(),
|
||||
// 由系统决定弹框还是立即返回结果;iOS/macOS 的 status 可靠,永久拒绝/受限直接去设置。
|
||||
// 前提:申请的权限必须已在 AndroidManifest 声明,否则 request() 会立即返回 denied 不弹框。
|
||||
if ((Platform.isIOS || Platform.isMacOS) && (status.isPermanentlyDenied || status.isRestricted)) {
|
||||
if ((Platform.isIOS || Platform.isMacOS) &&
|
||||
(status.isPermanentlyDenied || status.isRestricted)) {
|
||||
_toSettings();
|
||||
return false;
|
||||
}
|
||||
@@ -49,7 +54,8 @@ class PermissionUtil {
|
||||
|
||||
/// 处理 request() 后的结果:授权放行;永久拒绝引导去设置;普通拒绝弹 toast,
|
||||
/// 下次仍会弹系统授权框,避免误报"已禁止权限"
|
||||
static bool _handleStatus(PermissionStatus status, {String denyTip = '未授权,无法继续'}) {
|
||||
static bool _handleStatus(PermissionStatus status,
|
||||
{String denyTip = '未授权,无法继续'}) {
|
||||
if (status.isGranted || status.isLimited) return true;
|
||||
if (status.isPermanentlyDenied || status.isRestricted) {
|
||||
_toSettings();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hgmj/config/config.dart';
|
||||
import 'package:hgmj/hj_utils/codec_support.dart';
|
||||
import 'package:hgmj/hj_utils/video_view_type.dart';
|
||||
import 'package:hgmj/tools_base/global_store/store.dart';
|
||||
import 'package:hgmj/tools_base/net/http_manager.dart';
|
||||
import 'package:hgmj/tools_base/toast.dart';
|
||||
import 'package:hgmj/track_event_manager/device_service.dart';
|
||||
import 'package:wydj/config/config.dart';
|
||||
import 'package:wydj/hj_utils/codec_support.dart';
|
||||
import 'package:wydj/hj_utils/video_view_type.dart';
|
||||
import 'package:wydj/tools_base/global_store/store.dart';
|
||||
import 'package:wydj/tools_base/net/http_manager.dart';
|
||||
import 'package:wydj/tools_base/toast.dart';
|
||||
import 'package:wydj/track_event_manager/device_service.dart';
|
||||
|
||||
/// 播放失败诊断(**测试包专用**)。
|
||||
///
|
||||
@@ -39,14 +39,19 @@ class PlayDiagnose {
|
||||
..writeln('【$scene】${DateTime.now()}')
|
||||
..writeln('视频: ${videoTitle ?? '-'} (id=${videoId ?? '-'})')
|
||||
..writeln('地址: ${url ?? '-'}')
|
||||
..writeln('本次选流: ${isH265 == true ? 'H265' : 'H264'} 已回退264=${forceH264 ?? false}')
|
||||
..writeln(
|
||||
'本次选流: ${isH265 == true ? 'H265' : 'H264'} 已回退264=${forceH264 ?? false}')
|
||||
..writeln('--- 编解码 ---')
|
||||
..writeln('芯片硬解265=${CodecSupport.hwSupport} 本机已禁265=${CodecSupport.deviceDisabled} 最终用265=${CodecSupport.useH265}')
|
||||
..writeln('渲染方式=${needPlatformView ? 'platformView' : 'textureView'} 已持久化=$platformViewPersisted')
|
||||
..writeln(
|
||||
'芯片硬解265=${CodecSupport.hwSupport} 本机已禁265=${CodecSupport.deviceDisabled} 最终用265=${CodecSupport.useH265}')
|
||||
..writeln(
|
||||
'渲染方式=${needPlatformView ? 'platformView' : 'textureView'} 已持久化=$platformViewPersisted')
|
||||
..writeln('判定为解码错误=${isDecoderError(error)}')
|
||||
..writeln('--- 环境 ---')
|
||||
..writeln('机型: ${DeviceInfoService.brand} ${DeviceInfoService.model} 系统: ${DeviceInfoService.deviceOS} ${DeviceInfoService.systemVersion}')
|
||||
..writeln('版本: ${Config.innerVersion} 用户: ${globalStore.meInfo?.uid ?? '-'} 设备: ${DeviceInfoService.deviceId}')
|
||||
..writeln(
|
||||
'机型: ${DeviceInfoService.brand} ${DeviceInfoService.model} 系统: ${DeviceInfoService.deviceOS} ${DeviceInfoService.systemVersion}')
|
||||
..writeln(
|
||||
'版本: ${Config.innerVersion} 用户: ${globalStore.meInfo?.uid ?? '-'} 设备: ${DeviceInfoService.deviceId}')
|
||||
..writeln('网络: $net 线路: ${httpManager.baseUrl}')
|
||||
..writeln('--- 错误原文 ---')
|
||||
..writeln('$error');
|
||||
@@ -54,7 +59,8 @@ class PlayDiagnose {
|
||||
_records.add(buffer.toString());
|
||||
if (_records.length > _maxRecords) _records.removeAt(0);
|
||||
|
||||
await Clipboard.setData(ClipboardData(text: _records.join('\n${'=' * 30}\n')));
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: _records.join('\n${'=' * 30}\n')));
|
||||
showToast('失败信息已复制(${_records.length}条)\n请粘贴发给客服');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'package:hgmj/config/config.dart';
|
||||
import 'package:wydj/config/config.dart';
|
||||
|
||||
/// lightKV 本地存储 key 集中管理
|
||||
class StoreKeys {
|
||||
@@ -61,7 +61,8 @@ class StoreKeys {
|
||||
static const String DRAMA_CACHE_LIST = 'AppPlayDramaCacheVideoListKey';
|
||||
|
||||
/// 短剧下载授权的幂等键表:contentId -> X-Request-ID
|
||||
static const String DRAMA_DOWNLOAD_REQUEST_ID = 'AppDramaDownloadRequestIdKey';
|
||||
static const String DRAMA_DOWNLOAD_REQUEST_ID =
|
||||
'AppDramaDownloadRequestIdKey';
|
||||
|
||||
/// 免费观看视频列表
|
||||
static const String NEW_FREE_WATCH_VIDEOS = 'new_free_watch_videos';
|
||||
@@ -71,20 +72,24 @@ class StoreKeys {
|
||||
static const String TRACK_SESSION_SID = 'track_session_current_sid';
|
||||
|
||||
/// 最近一次事件时间戳
|
||||
static const String TRACK_SESSION_LAST_EVENT_TS = 'track_session_last_event_ts';
|
||||
static const String TRACK_SESSION_LAST_EVENT_TS =
|
||||
'track_session_last_event_ts';
|
||||
|
||||
/// 进入后台的时间戳
|
||||
static const String TRACK_SESSION_BACKGROUND_TS = 'track_session_background_ts';
|
||||
static const String TRACK_SESSION_BACKGROUND_TS =
|
||||
'track_session_background_ts';
|
||||
|
||||
// ===== 首页更新红点 =====
|
||||
/// 「最新」Tab 最后查看的服务端更新时间(ISO8601)
|
||||
static const String HOME_LATEST_LAST_VIEW_AT = 'home_latest_last_view_at';
|
||||
|
||||
/// 「今日最新」最后查看的服务端更新时间(ISO8601)
|
||||
static const String HOME_TODAY_LATEST_LAST_VIEW_AT = 'home_today_latest_last_view_at';
|
||||
static const String HOME_TODAY_LATEST_LAST_VIEW_AT =
|
||||
'home_today_latest_last_view_at';
|
||||
|
||||
/// 首页模块最后查看时间前缀,完整 key = 前缀 + uid + '_' + moduleId
|
||||
static const String HOME_MODULE_LAST_VIEW_AT_PREFIX = 'home_module_last_view_at_';
|
||||
static const String HOME_MODULE_LAST_VIEW_AT_PREFIX =
|
||||
'home_module_last_view_at_';
|
||||
|
||||
// ===== 短剧引导气泡 =====
|
||||
/// 已进过短剧 tab,「AI爽剧来袭」气泡永久不再出现
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hgmj/hj_utils/light_model.dart';
|
||||
import 'package:hgmj/hj_utils/store_keys.dart';
|
||||
import 'package:wydj/hj_utils/light_model.dart';
|
||||
import 'package:wydj/hj_utils/store_keys.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
/// 本机是否用 platformView 渲染(内存态,构造 controller 时同步读)。
|
||||
@@ -22,7 +22,8 @@ bool _persisted = false;
|
||||
/// [confirmPlatformView] 落本地,之后该机所有视频与下次启动自动改用 platformView。
|
||||
/// 健康机型永远 textureView,零影响。
|
||||
Future<void> initVideoViewType() async {
|
||||
_needPlatformView = await lightKV.getBool(StoreKeys.NEED_PLATFORM_VIEW) ?? false;
|
||||
_needPlatformView =
|
||||
await lightKV.getBool(StoreKeys.NEED_PLATFORM_VIEW) ?? false;
|
||||
_persisted = _needPlatformView; // 本地已有 = 已确认,后续不再重复写
|
||||
}
|
||||
|
||||
@@ -77,7 +78,8 @@ bool isDecoderError(Object? error) {
|
||||
/// m3u8 的 ts 切片;iOS 的 AVPlayer 对 HLS 切片不可靠,需本地代理另解。
|
||||
class PlayerFactory {
|
||||
/// 网络视频(m3u8/mp4)。
|
||||
static VideoPlayerController network(String? url, {VideoPlayerOptions? options}) {
|
||||
static VideoPlayerController network(String? url,
|
||||
{VideoPlayerOptions? options}) {
|
||||
return VideoPlayerController.networkUrl(
|
||||
Uri.parse(url ?? ''),
|
||||
viewType: resolveVideoViewType(),
|
||||
@@ -86,7 +88,8 @@ class PlayerFactory {
|
||||
}
|
||||
|
||||
/// 本地缓存视频。
|
||||
static VideoPlayerController file(String? path, {VideoPlayerOptions? options}) {
|
||||
static VideoPlayerController file(String? path,
|
||||
{VideoPlayerOptions? options}) {
|
||||
return VideoPlayerController.file(
|
||||
File(path ?? ''),
|
||||
viewType: resolveVideoViewType(),
|
||||
|
||||
Reference in New Issue
Block a user