logo和名称替换

This commit is contained in:
谢宇宁
2026-10-14 11:23:36 +07:00
parent 1cb514cd02
commit c5c04a7967
478 changed files with 8990 additions and 4948 deletions
+35 -15
View File
@@ -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;
}
+57 -22
View File
@@ -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;
}
}
+15 -7
View File
@@ -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 {
// //一级评论的全部列表
+35 -14
View File
@@ -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;
+47 -21
View File
@@ -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;
}
}
+17 -8
View File
@@ -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);
+117 -51
View File
@@ -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;
}
+11 -6
View File
@@ -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,
+14 -7
View File
@@ -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;
}
}
+38 -20
View File
@@ -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);
}
+4 -3
View File
@@ -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;
}
+14 -8
View File
@@ -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 '';
}
+14 -9
View File
@@ -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);
+5 -4
View File
@@ -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();
+8 -4
View File
@@ -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;
}
+20 -17
View File
@@ -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();
+39 -15
View File
@@ -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 -1
View File
@@ -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';
+43 -21
View File
@@ -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();
}
}
+11 -5
View File
@@ -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();
+19 -13
View File
@@ -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请粘贴发给客服');
}
+11 -6
View File
@@ -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爽剧来袭」气泡永久不再出现
+8 -5
View File
@@ -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(),