Merge branch 'main' into opt-videoPlayerControl

This commit is contained in:
guozhigq
2024-12-02 00:12:45 +08:00
13 changed files with 94 additions and 49 deletions

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
@ -183,7 +182,7 @@ class _PagesBottomSheetState extends State<PagesBottomSheet>
isFixedHeight: true, isFixedHeight: true,
); );
} else { } else {
_listObserverControllerList![widget.currentEpisodeIndex!] _listObserverControllerList![widget.currentEpisodeIndex ?? 0]
.initialIndexModel = ObserverIndexPositionModel( .initialIndexModel = ObserverIndexPositionModel(
index: currentIndex, index: currentIndex,
isFixedHeight: true, isFixedHeight: true,

View File

@ -5,6 +5,8 @@ import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart'; import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
import 'package:pilipala/utils/highlight.dart'; import 'package:pilipala/utils/highlight.dart';
import '../../utils/global_data_cache.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class HtmlRender extends StatelessWidget { class HtmlRender extends StatelessWidget {
const HtmlRender({ const HtmlRender({
@ -41,6 +43,8 @@ class HtmlRender extends StatelessWidget {
TagExtension( TagExtension(
tagsToExtend: <String>{'img'}, tagsToExtend: <String>{'img'},
builder: (ExtensionContext extensionContext) { builder: (ExtensionContext extensionContext) {
int defaultImgQuality = 10;
defaultImgQuality = GlobalDataCache.imgQuality;
try { try {
final Map<String, dynamic> attributes = final Map<String, dynamic> attributes =
extensionContext.attributes; extensionContext.attributes;
@ -99,13 +103,13 @@ class HtmlRender extends StatelessWidget {
), ),
); );
}, },
child: CachedNetworkImage(imageUrl: imgUrl), child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: CachedNetworkImage(
imageUrl: '$imgUrl@${defaultImgQuality}q.webp',
),
),
); );
// return NetworkImgLayer(
// width: isEmote ? 22 : Get.size.width - 24,
// height: isEmote ? 22 : 200,
// src: imgUrl,
// );
} catch (err) { } catch (err) {
return const SizedBox(); return const SizedBox();
} }
@ -138,6 +142,14 @@ class HtmlRender extends StatelessWidget {
textAlign: TextAlign.justify, textAlign: TextAlign.justify,
), ),
'img': Style(margin: Margins.only(top: 4, bottom: 4)), 'img': Style(margin: Margins.only(top: 4, bottom: 4)),
'figcaption': Style(
textAlign: TextAlign.center,
margin: Margins.only(top: 8, bottom: 20),
color: Theme.of(context).colorScheme.secondary,
),
'figure': Style(
margin: Margins.zero,
),
}, },
); );
} }

View File

@ -20,7 +20,6 @@ import '../../../models/video/reply/item.dart';
import '../widgets/dynamic_panel.dart'; import '../widgets/dynamic_panel.dart';
class DynamicDetailPage extends StatefulWidget { class DynamicDetailPage extends StatefulWidget {
// const DynamicDetailPage({super.key});
const DynamicDetailPage({Key? key}) : super(key: key); const DynamicDetailPage({Key? key}) : super(key: key);
@override @override
@ -90,19 +89,22 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
_dynamicDetailController = Get.put( _dynamicDetailController = Get.put(
DynamicDetailController(oid, replyType), DynamicDetailController(oid, replyType),
tag: opusId.toString()); tag: opusId.toString());
_futureBuilderFuture = _dynamicDetailController.queryReplyList();
await _dynamicDetailController.reqHtmlByOpusId(opusId!); await _dynamicDetailController.reqHtmlByOpusId(opusId!);
setState(() {}); setState(() {});
} }
} else { } else {
oid = moduleDynamic.major!.draw!.id!; oid = moduleDynamic.major!.draw!.id!;
} }
} catch (_) {} } catch (err) {
print('err:${err.toString()}');
}
} }
if (!isOpusId) { if (!isOpusId) {
_dynamicDetailController = _dynamicDetailController =
Get.put(DynamicDetailController(oid, replyType), tag: oid.toString()); Get.put(DynamicDetailController(oid, replyType), tag: oid.toString());
_futureBuilderFuture = _dynamicDetailController.queryReplyList();
} }
_futureBuilderFuture = _dynamicDetailController.queryReplyList();
} }
// 查看二级评论 // 查看二级评论
@ -132,7 +134,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
// 分页加载 // 分页加载
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) { scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () { EasyThrottle.throttle('replyList', const Duration(seconds: 2), () {
_dynamicDetailController.onLoad(); _dynamicDetailController.onLoad();
}); });
} }

View File

@ -122,6 +122,7 @@ class LaterController extends GetxController {
'heroTag': heroTag, 'heroTag': heroTag,
'sourceType': 'watchLater', 'sourceType': 'watchLater',
'count': laterList.length, 'count': laterList.length,
'mediaId': userInfo!.mid,
}, },
); );
} }

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/read/opus.dart'; import 'package:pilipala/models/read/opus.dart';
import 'controller.dart'; import 'controller.dart';
import 'text_helper.dart'; import 'text_helper.dart';
@ -26,16 +27,18 @@ class _OpusPageState extends State<OpusPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: _buildAppBar(), appBar: _buildAppBar(),
body: SingleChildScrollView( body: CustomScrollView(
controller: controller.scrollController, controller: controller.scrollController,
child: Column( slivers: [
mainAxisAlignment: MainAxisAlignment.start, SliverList(
crossAxisAlignment: CrossAxisAlignment.start, delegate: SliverChildListDelegate(
children: [ [
_buildTitle(), _buildTitle(),
_buildFutureContent(), _buildFutureContent(),
], ],
), ),
),
],
), ),
); );
} }

View File

@ -6,6 +6,7 @@ import 'package:pilipala/models/read/opus.dart';
import 'package:pilipala/models/read/read.dart'; import 'package:pilipala/models/read/read.dart';
import 'package:pilipala/pages/opus/text_helper.dart'; import 'package:pilipala/pages/opus/text_helper.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'controller.dart'; import 'controller.dart';
class ReadPage extends StatefulWidget { class ReadPage extends StatefulWidget {
@ -38,16 +39,18 @@ class _ReadPageState extends State<ReadPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: _buildAppBar(), appBar: _buildAppBar(),
body: SingleChildScrollView( body: CustomScrollView(
controller: controller.scrollController, controller: controller.scrollController,
child: Column( slivers: [
mainAxisAlignment: MainAxisAlignment.start, SliverList(
crossAxisAlignment: CrossAxisAlignment.start, delegate: SliverChildListDelegate(
children: [ [
_buildTitle(), _buildTitle(),
_buildFutureContent(), _buildFutureContent(),
], ],
), ),
),
],
), ),
); );
} }

View File

@ -487,7 +487,7 @@ class VideoDetailController extends GetxController
if (result['status']) { if (result['status']) {
if (result['data'].subtitles.isNotEmpty) { if (result['data'].subtitles.isNotEmpty) {
subtitles = result['data'].subtitles; subtitles = result['data'].subtitles;
getDanmaku(subtitles); getSubtitleContent(subtitles);
} }
} }
headerControl = HeaderControl( headerControl = HeaderControl(
@ -501,8 +501,8 @@ class VideoDetailController extends GetxController
plPlayerController.setHeaderControl(headerControl); plPlayerController.setHeaderControl(headerControl);
} }
// 获取 // 获取
Future getDanmaku(List subtitles) async { Future getSubtitleContent(List subtitles) async {
if (subtitles.isNotEmpty) { if (subtitles.isNotEmpty) {
for (var i in subtitles) { for (var i in subtitles) {
final Map<String, dynamic> res = await VideoHttp.getSubtitleContent( final Map<String, dynamic> res = await VideoHttp.getSubtitleContent(

View File

@ -523,7 +523,7 @@ class VideoIntroController extends GetxController {
Get.find<VideoDetailController>(tag: heroTag); Get.find<VideoDetailController>(tag: heroTag);
/// 优先稍后再看、收藏夹 /// 优先稍后再看、收藏夹
if (videoDetailCtr.isWatchLaterVisible.value) { if (videoDetailCtr.sourceType.value != 'normal') {
episodes.addAll(videoDetailCtr.mediaList); episodes.addAll(videoDetailCtr.mediaList);
} else if (videoDetail.value.ugcSeason != null) { } else if (videoDetail.value.ugcSeason != null) {
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
@ -617,6 +617,7 @@ class VideoIntroController extends GetxController {
// 播放器底栏 选集 回调 // 播放器底栏 选集 回调
void showEposideHandler() { void showEposideHandler() {
late List episodes; late List episodes;
int currentEpisodeIndex = 0;
VideoEpidoesType dataType = VideoEpidoesType.videoEpisode; VideoEpidoesType dataType = VideoEpidoesType.videoEpisode;
if (videoDetail.value.ugcSeason != null) { if (videoDetail.value.ugcSeason != null) {
dataType = VideoEpidoesType.videoEpisode; dataType = VideoEpidoesType.videoEpisode;
@ -625,6 +626,7 @@ class VideoIntroController extends GetxController {
final List<EpisodeItem> episodesList = sections[i].episodes!; final List<EpisodeItem> episodesList = sections[i].episodes!;
for (int j = 0; j < episodesList.length; j++) { for (int j = 0; j < episodesList.length; j++) {
if (episodesList[j].cid == lastPlayCid.value) { if (episodesList[j].cid == lastPlayCid.value) {
currentEpisodeIndex = i;
episodes = episodesList; episodes = episodesList;
continue; continue;
} }
@ -644,6 +646,7 @@ class VideoIntroController extends GetxController {
sheetHeight: Get.size.height, sheetHeight: Get.size.height,
isFullScreen: true, isFullScreen: true,
ugcSeason: ugcSeason, ugcSeason: ugcSeason,
currentEpisodeIndex: currentEpisodeIndex,
changeFucCall: (item, index) { changeFucCall: (item, index) {
if (dataType == VideoEpidoesType.videoEpisode) { if (dataType == VideoEpidoesType.videoEpisode) {
changeSeasonOrbangu( changeSeasonOrbangu(

View File

@ -42,23 +42,14 @@ class _SeasonPanelState extends State<SeasonPanel> {
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag); _videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
/// 根据 cid 找到对应集,找到对应 episodes /// 根据 cid 找到对应集,找到对应 episodes
final List<SectionItem> sections = widget.ugcSeason.sections!; getCurrentEpisodeIndex();
for (int i = 0; i < sections.length; i++) {
final List<EpisodeItem> episodesList = sections[i].episodes!;
for (int j = 0; j < episodesList.length; j++) {
if (episodesList[j].cid == cid) {
currentEpisodeIndex = i;
episodes = episodesList;
continue;
}
}
}
/// 取对应 season_id 的 episodes /// 取对应 season_id 的 episodes
getCurrentIndex(); getCurrentIndex();
_videoDetailController.cid.listen((int p0) { _videoDetailController.cid.listen((int p0) {
cid = p0; cid = p0;
getCurrentIndex(); getCurrentIndex();
getCurrentEpisodeIndex();
}); });
} }
@ -94,6 +85,21 @@ class _SeasonPanelState extends State<SeasonPanel> {
} }
} }
// 获取currentEpisodeIndex
void getCurrentEpisodeIndex() {
final List<SectionItem> sections = widget.ugcSeason.sections!;
for (int i = 0; i < sections.length; i++) {
final List<EpisodeItem> episodesList = sections[i].episodes!;
for (int j = 0; j < episodesList.length; j++) {
if (episodesList[j].cid == cid) {
currentEpisodeIndex = i;
episodes = episodesList;
continue;
}
}
}
}
Widget buildEpisodeListItem( Widget buildEpisodeListItem(
EpisodeItem episode, EpisodeItem episode,
int index, int index,

View File

@ -70,6 +70,13 @@ class VideoReplyController extends GetxController {
isEnd = res['data'].cursor.isEnd ?? false; isEnd = res['data'].cursor.isEnd ?? false;
nextOffset = res['data'].cursor.paginationReply.nextOffset ?? ""; nextOffset = res['data'].cursor.paginationReply.nextOffset ?? "";
if (replies.isNotEmpty) { if (replies.isNotEmpty) {
/// 临时修复
final bool flag = replyList
.any((ReplyItemModel reply) => reply.rpid == replies.first.rpid);
if (replies.length == 1 && flag) {
replies.clear();
isEnd = true;
}
noMore.value = isEnd ? '没有更多了' : '加载中...'; noMore.value = isEnd ? '没有更多了' : '加载中...';
} else { } else {
noMore.value = noMore.value =

View File

@ -685,6 +685,11 @@ class _VideoDetailPageState extends State<VideoDetailPage>
canPop: canPop:
plPlayerController?.isFullScreen.value != true, plPlayerController?.isFullScreen.value != true,
onPopInvoked: (bool didPop) { onPopInvoked: (bool didPop) {
if (plPlayerController?.controlsLock.value ==
true) {
plPlayerController?.onLockControl(false);
return;
}
if (plPlayerController?.isFullScreen.value == if (plPlayerController?.isFullScreen.value ==
true) { true) {
plPlayerController! plPlayerController!

View File

@ -85,9 +85,12 @@ class _MediaListPanelState extends State<MediaListPanel> {
AppBar( AppBar(
toolbarHeight: 45, toolbarHeight: 45,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
title: Text( title: Padding(
widget.panelTitle ?? '稍后再看', padding: const EdgeInsets.only(left: 12),
style: Theme.of(context).textTheme.titleSmall, child: Text(
widget.panelTitle ?? '稍后再看',
style: Theme.of(context).textTheme.titleSmall,
),
), ),
actions: [ actions: [
IconButton( IconButton(

View File

@ -917,7 +917,7 @@ class PlPlayerController {
if (videoType == 'live') { if (videoType == 'live') {
return; return;
} }
if (controlsLock.value) { if (_controlsLock.value) {
return; return;
} }
_doubleSpeedStatus.value = val; _doubleSpeedStatus.value = val;
@ -1093,6 +1093,7 @@ class PlPlayerController {
videoFitChangedTimer?.cancel(); videoFitChangedTimer?.cancel();
// _position.close(); // _position.close();
_playerEventSubs?.cancel(); _playerEventSubs?.cancel();
_controlsLock.value = false;
// _sliderPosition.close(); // _sliderPosition.close();
// _sliderTempPosition.close(); // _sliderTempPosition.close();
// _isSliderMoving.close(); // _isSliderMoving.close();