Merge branch 'main' into design
This commit is contained in:
3
.github/workflows/beta_ci.yml
vendored
3
.github/workflows/beta_ci.yml
vendored
@ -12,7 +12,6 @@ on:
|
|||||||
- ".idea/**"
|
- ".idea/**"
|
||||||
- "!.github/workflows/**"
|
- "!.github/workflows/**"
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update_version:
|
update_version:
|
||||||
name: Read and update version
|
name: Read and update version
|
||||||
@ -96,7 +95,7 @@ jobs:
|
|||||||
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: 3.16.5
|
flutter-version: 3.19.6
|
||||||
channel: any
|
channel: any
|
||||||
|
|
||||||
- name: 下载项目依赖
|
- name: 下载项目依赖
|
||||||
|
|||||||
4
.github/workflows/release_ci.yml
vendored
4
.github/workflows/release_ci.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
|||||||
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: 3.16.5
|
flutter-version: 3.19.6
|
||||||
channel: any
|
channel: any
|
||||||
|
|
||||||
- name: 下载项目依赖
|
- name: 下载项目依赖
|
||||||
@ -98,7 +98,7 @@ jobs:
|
|||||||
uses: subosito/flutter-action@v2.10.0
|
uses: subosito/flutter-action@v2.10.0
|
||||||
with:
|
with:
|
||||||
cache: true
|
cache: true
|
||||||
flutter-version: 3.16.5
|
flutter-version: 3.19.6
|
||||||
|
|
||||||
- name: flutter build ipa
|
- name: flutter build ipa
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
39
change_log/1.0.25.1010.md
Normal file
39
change_log/1.0.25.1010.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
## 1.0.25
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
+ 直播弹幕
|
||||||
|
+ 稍后再看、收藏夹播放全部
|
||||||
|
+ 收藏夹新建、编辑
|
||||||
|
+ 评论删除
|
||||||
|
+ 评论保存为图片
|
||||||
|
+ 动态页滑动切换up
|
||||||
|
+ up投稿筛选充电视频
|
||||||
|
+ 直播tab展示关注up
|
||||||
|
+ up主页专栏展示
|
||||||
|
|
||||||
|
### 优化
|
||||||
|
+ 视频详情页一键三连
|
||||||
|
+ 动态页标识充电视频
|
||||||
|
+ 播放器亮度、音量调整百分比展示
|
||||||
|
+ 封面预览时视频标题可复制
|
||||||
|
+ 竖屏直播布局
|
||||||
|
+ 图片预览
|
||||||
|
+ 专栏渲染优化
|
||||||
|
+ 私信图片查看
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
+ 收藏夹点击异常
|
||||||
|
+ 搜索up异常
|
||||||
|
+ 系统通知已读异常
|
||||||
|
+ [赞了我的]展示错误
|
||||||
|
+ 部分up合集无法打开
|
||||||
|
+ 切换合集视频投币个数未重置
|
||||||
|
+ 搜索条件筛选面板无法滚动
|
||||||
|
+ 部分机型导航条未沉浸
|
||||||
|
+ 专栏图片渲染问题
|
||||||
|
+ 专栏浏览历史记录
|
||||||
|
+ 直播间历史记录
|
||||||
|
|
||||||
|
|
||||||
|
更多更新日志可在Github上查看
|
||||||
|
问题反馈、功能建议请查看「关于」页面。
|
||||||
@ -33,7 +33,11 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final int defaultImgQuality = GlobalDataCache().imgQuality;
|
int defaultImgQuality = 10;
|
||||||
|
try {
|
||||||
|
defaultImgQuality = GlobalDataCache().imgQuality;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
if (src == '' || src == null) {
|
if (src == '' || src == null) {
|
||||||
return placeholder(context);
|
return placeholder(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -536,7 +536,8 @@ class VideoHttp {
|
|||||||
// 获取字幕内容
|
// 获取字幕内容
|
||||||
static Future<Map<String, dynamic>> getSubtitleContent(url) async {
|
static Future<Map<String, dynamic>> getSubtitleContent(url) async {
|
||||||
var res = await Request().get('https:$url');
|
var res = await Request().get('https:$url');
|
||||||
final String content = SubTitleUtils.convertToWebVTT(res.data['body']);
|
final String content =
|
||||||
|
await SubTitleUtils.convertToWebVTT(res.data['body']);
|
||||||
final List body = res.data['body'];
|
final List body = res.data['body'];
|
||||||
return {'content': content, 'body': body};
|
return {'content': content, 'body': body};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
class MediaVideoItemModel {
|
class MediaVideoItemModel {
|
||||||
MediaVideoItemModel({
|
MediaVideoItemModel({
|
||||||
this.id,
|
this.id,
|
||||||
|
this.aid,
|
||||||
this.offset,
|
this.offset,
|
||||||
this.index,
|
this.index,
|
||||||
this.intro,
|
this.intro,
|
||||||
@ -14,12 +15,13 @@ class MediaVideoItemModel {
|
|||||||
this.likeState,
|
this.likeState,
|
||||||
this.favState,
|
this.favState,
|
||||||
this.page,
|
this.page,
|
||||||
|
this.cid,
|
||||||
this.pages,
|
this.pages,
|
||||||
this.title,
|
this.title,
|
||||||
this.type,
|
this.type,
|
||||||
this.upper,
|
this.upper,
|
||||||
this.link,
|
this.link,
|
||||||
this.bvId,
|
this.bvid,
|
||||||
this.shortLink,
|
this.shortLink,
|
||||||
this.rights,
|
this.rights,
|
||||||
this.elecInfo,
|
this.elecInfo,
|
||||||
@ -32,6 +34,7 @@ class MediaVideoItemModel {
|
|||||||
});
|
});
|
||||||
|
|
||||||
int? id;
|
int? id;
|
||||||
|
int? aid;
|
||||||
int? offset;
|
int? offset;
|
||||||
int? index;
|
int? index;
|
||||||
String? intro;
|
String? intro;
|
||||||
@ -45,12 +48,13 @@ class MediaVideoItemModel {
|
|||||||
int? likeState;
|
int? likeState;
|
||||||
int? favState;
|
int? favState;
|
||||||
int? page;
|
int? page;
|
||||||
|
int? cid;
|
||||||
List<Page>? pages;
|
List<Page>? pages;
|
||||||
String? title;
|
String? title;
|
||||||
int? type;
|
int? type;
|
||||||
Upper? upper;
|
Upper? upper;
|
||||||
String? link;
|
String? link;
|
||||||
String? bvId;
|
String? bvid;
|
||||||
String? shortLink;
|
String? shortLink;
|
||||||
Rights? rights;
|
Rights? rights;
|
||||||
dynamic elecInfo;
|
dynamic elecInfo;
|
||||||
@ -64,6 +68,7 @@ class MediaVideoItemModel {
|
|||||||
factory MediaVideoItemModel.fromJson(Map<String, dynamic> json) =>
|
factory MediaVideoItemModel.fromJson(Map<String, dynamic> json) =>
|
||||||
MediaVideoItemModel(
|
MediaVideoItemModel(
|
||||||
id: json["id"],
|
id: json["id"],
|
||||||
|
aid: json["id"],
|
||||||
offset: json["offset"],
|
offset: json["offset"],
|
||||||
index: json["index"],
|
index: json["index"],
|
||||||
intro: json["intro"],
|
intro: json["intro"],
|
||||||
@ -77,6 +82,7 @@ class MediaVideoItemModel {
|
|||||||
likeState: json["like_state"],
|
likeState: json["like_state"],
|
||||||
favState: json["fav_state"],
|
favState: json["fav_state"],
|
||||||
page: json["page"],
|
page: json["page"],
|
||||||
|
cid: json["pages"] == null ? -1 : json["pages"].first['id'],
|
||||||
// json["pages"] 可能为null
|
// json["pages"] 可能为null
|
||||||
pages: json["pages"] == null
|
pages: json["pages"] == null
|
||||||
? []
|
? []
|
||||||
@ -85,7 +91,7 @@ class MediaVideoItemModel {
|
|||||||
type: json["type"],
|
type: json["type"],
|
||||||
upper: Upper.fromJson(json["upper"]),
|
upper: Upper.fromJson(json["upper"]),
|
||||||
link: json["link"],
|
link: json["link"],
|
||||||
bvId: json["bv_id"],
|
bvid: json["bv_id"],
|
||||||
shortLink: json["short_link"],
|
shortLink: json["short_link"],
|
||||||
rights: Rights.fromJson(json["rights"]),
|
rights: Rights.fromJson(json["rights"]),
|
||||||
elecInfo: json["elec_info"],
|
elecInfo: json["elec_info"],
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class LiveRoomController extends GetxController {
|
|||||||
? liveItem.pic
|
? liveItem.pic
|
||||||
: (liveItem.cover != null && liveItem.cover != '')
|
: (liveItem.cover != null && liveItem.cover != '')
|
||||||
? liveItem.cover
|
? liveItem.cover
|
||||||
: null;
|
: '';
|
||||||
}
|
}
|
||||||
Request.getBuvid().then((value) => buvid = value);
|
Request.getBuvid().then((value) => buvid = value);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -108,6 +108,12 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final isPortrait = mediaQuery.orientation == Orientation.portrait;
|
||||||
|
final isLandscape = mediaQuery.orientation == Orientation.landscape;
|
||||||
|
|
||||||
|
final padding = mediaQuery.padding;
|
||||||
|
|
||||||
Widget videoPlayerPanel = FutureBuilder(
|
Widget videoPlayerPanel = FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
@ -187,10 +193,8 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
() => SizedBox(
|
() => SizedBox(
|
||||||
height: MediaQuery.of(context).padding.top +
|
height: padding.top +
|
||||||
(_liveRoomController.isPortrait.value ||
|
(_liveRoomController.isPortrait.value || isLandscape
|
||||||
MediaQuery.of(context).orientation ==
|
|
||||||
Orientation.landscape
|
|
||||||
? 0
|
? 0
|
||||||
: kToolbarHeight),
|
: kToolbarHeight),
|
||||||
),
|
),
|
||||||
@ -201,21 +205,18 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
if (plPlayerController.isFullScreen.value == true) {
|
if (plPlayerController.isFullScreen.value == true) {
|
||||||
plPlayerController.triggerFullScreen(status: false);
|
plPlayerController.triggerFullScreen(status: false);
|
||||||
}
|
}
|
||||||
if (MediaQuery.of(context).orientation ==
|
if (isLandscape) {
|
||||||
Orientation.landscape) {
|
|
||||||
verticalScreen();
|
verticalScreen();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => Container(
|
() => Container(
|
||||||
width: Get.size.width,
|
width: Get.size.width,
|
||||||
height: MediaQuery.of(context).orientation ==
|
height: isLandscape
|
||||||
Orientation.landscape
|
|
||||||
? Get.size.height
|
? Get.size.height
|
||||||
: !_liveRoomController.isPortrait.value
|
: !_liveRoomController.isPortrait.value
|
||||||
? Get.size.width * 9 / 16
|
? Get.size.width * 9 / 16
|
||||||
: Get.size.height -
|
: Get.size.height - padding.top,
|
||||||
MediaQuery.of(context).padding.top,
|
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||||
@ -229,7 +230,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
// 定位 快速滑动到底部
|
// 定位 快速滑动到底部
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 20,
|
right: 20,
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
bottom: padding.bottom + 80,
|
||||||
child: SlideTransition(
|
child: SlideTransition(
|
||||||
position: Tween<Offset>(
|
position: Tween<Offset>(
|
||||||
begin: const Offset(0, 4),
|
begin: const Offset(0, 4),
|
||||||
@ -262,10 +263,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
toolbarHeight:
|
toolbarHeight: isPortrait ? 56 : 0,
|
||||||
MediaQuery.of(context).orientation == Orientation.portrait
|
|
||||||
? 56
|
|
||||||
: 0,
|
|
||||||
title: FutureBuilder(
|
title: FutureBuilder(
|
||||||
future: _futureBuilder,
|
future: _futureBuilder,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -317,15 +315,20 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
),
|
),
|
||||||
// 消息列表
|
// 消息列表
|
||||||
Obx(
|
Obx(
|
||||||
() => Positioned(
|
() => Align(
|
||||||
top: MediaQuery.of(context).padding.top +
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
bottom: 90 + padding.bottom,
|
||||||
|
),
|
||||||
|
height: Get.size.height -
|
||||||
|
(padding.top +
|
||||||
kToolbarHeight +
|
kToolbarHeight +
|
||||||
(_liveRoomController.isPortrait.value
|
(_liveRoomController.isPortrait.value
|
||||||
? Get.size.width
|
? Get.size.width
|
||||||
: Get.size.width * 9 / 16),
|
: Get.size.width * 9 / 16) +
|
||||||
bottom: 90 + MediaQuery.of(context).padding.bottom,
|
100 +
|
||||||
left: 0,
|
padding.bottom),
|
||||||
right: 0,
|
|
||||||
child: buildMessageListUI(
|
child: buildMessageListUI(
|
||||||
context,
|
context,
|
||||||
_liveRoomController,
|
_liveRoomController,
|
||||||
@ -333,19 +336,17 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
// 消息输入框
|
// 消息输入框
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: MediaQuery.of(context).orientation == Orientation.portrait,
|
visible: isPortrait,
|
||||||
child: Positioned(
|
child: Positioned(
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 14,
|
left: 14, right: 14, top: 4, bottom: padding.bottom + 20),
|
||||||
right: 14,
|
|
||||||
top: 4,
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 20),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey.withOpacity(0.1),
|
color: Colors.grey.withOpacity(0.1),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
@ -421,6 +422,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
return PiPSwitcher(
|
return PiPSwitcher(
|
||||||
childWhenDisabled: childWhenDisabled,
|
childWhenDisabled: childWhenDisabled,
|
||||||
@ -438,8 +440,7 @@ Widget buildMessageListUI(
|
|||||||
LiveRoomController liveRoomController,
|
LiveRoomController liveRoomController,
|
||||||
ScrollController scrollController,
|
ScrollController scrollController,
|
||||||
) {
|
) {
|
||||||
return Expanded(
|
return Obx(
|
||||||
child: Obx(
|
|
||||||
() => MediaQuery.removePadding(
|
() => MediaQuery.removePadding(
|
||||||
context: context,
|
context: context,
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
@ -519,7 +520,6 @@ Widget buildMessageListUI(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,8 +24,8 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
final MainController _mainController = Get.put(MainController());
|
final MainController _mainController = Get.put(MainController());
|
||||||
late HomeController _homeController;
|
late HomeController _homeController;
|
||||||
RankController? _rankController;
|
RankController? _rankController;
|
||||||
DynamicsController? _dynamicController;
|
late DynamicsController _dynamicController;
|
||||||
MediaController? _mediaController;
|
late MediaController _mediaController;
|
||||||
|
|
||||||
int? _lastSelectTime; //上次点击时间
|
int? _lastSelectTime; //上次点击时间
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
@ -76,28 +76,30 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentPage is DynamicsPage) {
|
if (currentPage is DynamicsPage) {
|
||||||
if (_dynamicController!.flag) {
|
if (_dynamicController.flag) {
|
||||||
// 单击返回顶部 双击并刷新
|
// 单击返回顶部 双击并刷新
|
||||||
if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) {
|
if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) {
|
||||||
_dynamicController!.onRefresh();
|
_dynamicController.onRefresh();
|
||||||
} else {
|
} else {
|
||||||
_dynamicController!.animateToTop();
|
_dynamicController.animateToTop();
|
||||||
}
|
}
|
||||||
_lastSelectTime = DateTime.now().millisecondsSinceEpoch;
|
_lastSelectTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
}
|
}
|
||||||
_dynamicController!.flag = true;
|
_dynamicController.flag = true;
|
||||||
_mainController.clearUnread();
|
_mainController.clearUnread();
|
||||||
} else {
|
} else {
|
||||||
_dynamicController?.flag = false;
|
_dynamicController.flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPage is MediaPage) {
|
if (currentPage is MediaPage) {
|
||||||
_mediaController!.queryFavFolder();
|
_mediaController.queryFavFolder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void controllerInit() {
|
void controllerInit() {
|
||||||
_homeController = Get.put(HomeController());
|
_homeController = Get.put(HomeController());
|
||||||
|
_dynamicController = Get.put(DynamicsController());
|
||||||
|
_mediaController = Get.put(MediaController());
|
||||||
if (_mainController.pagesIds.contains(1)) {
|
if (_mainController.pagesIds.contains(1)) {
|
||||||
_rankController = Get.put(RankController());
|
_rankController = Get.put(RankController());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -411,7 +411,12 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 修改分P或番剧分集
|
// 修改分P或番剧分集
|
||||||
Future changeSeasonOrbangu(bvid, cid, aid, cover) async {
|
Future changeSeasonOrbangu(
|
||||||
|
String bvid,
|
||||||
|
int cid,
|
||||||
|
int? aid,
|
||||||
|
String? cover,
|
||||||
|
) async {
|
||||||
// 重新获取视频资源
|
// 重新获取视频资源
|
||||||
final VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
@ -422,13 +427,14 @@ class VideoIntroController extends GetxController {
|
|||||||
releatedCtr.queryRelatedVideo();
|
releatedCtr.queryRelatedVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
videoDetailCtr.bvid = bvid;
|
videoDetailCtr
|
||||||
videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid);
|
..bvid = bvid
|
||||||
videoDetailCtr.cid.value = cid;
|
..oid.value = aid ?? IdUtils.bv2av(bvid)
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
..cid.value = cid
|
||||||
videoDetailCtr.cover.value = cover;
|
..danmakuCid.value = cid
|
||||||
videoDetailCtr.queryVideoUrl();
|
..cover.value = cover ?? ''
|
||||||
videoDetailCtr.clearSubtitleContent();
|
..queryVideoUrl()
|
||||||
|
..clearSubtitleContent();
|
||||||
await videoDetailCtr.getSubtitle();
|
await videoDetailCtr.getSubtitle();
|
||||||
videoDetailCtr.setSubtitleContent();
|
videoDetailCtr.setSubtitleContent();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
@ -478,7 +484,13 @@ class VideoIntroController extends GetxController {
|
|||||||
final List episodes = [];
|
final List episodes = [];
|
||||||
bool isPages = false;
|
bool isPages = false;
|
||||||
late String cover;
|
late String cover;
|
||||||
if (videoDetail.value.ugcSeason != null) {
|
final VideoDetailController videoDetailCtr =
|
||||||
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
|
||||||
|
/// 优先稍后再看、收藏夹
|
||||||
|
if (videoDetailCtr.isWatchLaterVisible.value) {
|
||||||
|
episodes.addAll(videoDetailCtr.mediaList);
|
||||||
|
} else if (videoDetail.value.ugcSeason != null) {
|
||||||
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||||
final List<SectionItem> sections = ugcSeason.sections!;
|
final List<SectionItem> sections = ugcSeason.sections!;
|
||||||
for (int i = 0; i < sections.length; i++) {
|
for (int i = 0; i < sections.length; i++) {
|
||||||
@ -495,10 +507,15 @@ class VideoIntroController extends GetxController {
|
|||||||
episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
||||||
int nextIndex = currentIndex + 1;
|
int nextIndex = currentIndex + 1;
|
||||||
cover = episodes[nextIndex].cover;
|
cover = episodes[nextIndex].cover;
|
||||||
final VideoDetailController videoDetailCtr =
|
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
|
||||||
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
|
|
||||||
|
int cid = episodes[nextIndex].cid!;
|
||||||
|
while (cid == -1) {
|
||||||
|
nextIndex += 1;
|
||||||
|
SmartDialog.showToast('当前视频暂不支持播放,自动跳过');
|
||||||
|
cid = episodes[nextIndex].cid!;
|
||||||
|
}
|
||||||
|
|
||||||
// 列表循环
|
// 列表循环
|
||||||
if (nextIndex >= episodes.length) {
|
if (nextIndex >= episodes.length) {
|
||||||
if (platRepeat == PlayRepeat.listCycle) {
|
if (platRepeat == PlayRepeat.listCycle) {
|
||||||
@ -508,7 +525,6 @@ class VideoIntroController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final int cid = episodes[nextIndex].cid!;
|
|
||||||
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
||||||
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
||||||
changeSeasonOrbangu(rBvid, cid, rAid, cover);
|
changeSeasonOrbangu(rBvid, cid, rAid, cover);
|
||||||
|
|||||||
@ -109,7 +109,7 @@ class _MediaListPanelState extends State<MediaListPanel> {
|
|||||||
var item = mediaList[index];
|
var item = mediaList[index];
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
String bvid = item.bvId!;
|
String bvid = item.bvid!;
|
||||||
int? aid = item.id;
|
int? aid = item.id;
|
||||||
String cover = item.cover ?? '';
|
String cover = item.cover ?? '';
|
||||||
final int cid =
|
final int cid =
|
||||||
@ -173,7 +173,7 @@ class _MediaListPanelState extends State<MediaListPanel> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: item.bvId == widget.bvid
|
color: item.bvid == widget.bvid
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primary
|
.primary
|
||||||
|
|||||||
@ -1,9 +1,37 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:isolate';
|
||||||
|
|
||||||
class SubTitleUtils {
|
class SubTitleUtils {
|
||||||
// 格式整理
|
// 格式整理
|
||||||
static String convertToWebVTT(List jsonData) {
|
static Future<String> convertToWebVTT(List jsonData) async {
|
||||||
String webVTTContent = 'WEBVTT FILE\n\n';
|
final receivePort = ReceivePort();
|
||||||
|
await Isolate.spawn(_convertToWebVTTIsolate, receivePort.sendPort);
|
||||||
|
|
||||||
for (int i = 0; i < jsonData.length; i++) {
|
final sendPort = await receivePort.first as SendPort;
|
||||||
|
final response = ReceivePort();
|
||||||
|
sendPort.send([jsonData, response.sendPort]);
|
||||||
|
|
||||||
|
return await response.first as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _convertToWebVTTIsolate(SendPort sendPort) async {
|
||||||
|
final port = ReceivePort();
|
||||||
|
sendPort.send(port.sendPort);
|
||||||
|
|
||||||
|
await for (final message in port) {
|
||||||
|
final List jsonData = message[0];
|
||||||
|
final SendPort replyTo = message[1];
|
||||||
|
|
||||||
|
String webVTTContent = 'WEBVTT FILE\n\n';
|
||||||
|
int chunkSize = 100; // 每次处理100条数据
|
||||||
|
int totalChunks = (jsonData.length / chunkSize).ceil();
|
||||||
|
|
||||||
|
for (int chunk = 0; chunk < totalChunks; chunk++) {
|
||||||
|
int start = chunk * chunkSize;
|
||||||
|
int end = start + chunkSize;
|
||||||
|
if (end > jsonData.length) end = jsonData.length;
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
final item = jsonData[i];
|
final item = jsonData[i];
|
||||||
double from = double.parse(item['from'].toString());
|
double from = double.parse(item['from'].toString());
|
||||||
double to = double.parse(item['to'].toString());
|
double to = double.parse(item['to'].toString());
|
||||||
@ -14,8 +42,10 @@ class SubTitleUtils {
|
|||||||
webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n';
|
webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n';
|
||||||
webVTTContent += '$content\n\n';
|
webVTTContent += '$content\n\n';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return webVTTContent;
|
replyTo.send(webVTTContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String formatTime(num seconds) {
|
static String formatTime(num seconds) {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.24+1024
|
version: 1.0.25+1025
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user