Merge branch 'main' into fix

This commit is contained in:
guozhigq
2024-05-31 23:32:38 +08:00
9 changed files with 183 additions and 222 deletions

View File

@ -94,7 +94,7 @@ QQ频道: https://pd.qq.com/s/365esodk3
- [x] 音质选择(视视频而定)
- [x] 解码格式选择(视视频而定)
- [x] 弹幕
- [ ] 字幕
- [x] 字幕
- [x] 记忆播放
- [x] 视频比例:高度/宽度适应、填充、包含等

View File

@ -1,3 +1,4 @@
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
@ -30,6 +31,31 @@ class _UpPanelState extends State<UpPanel> {
liveList = widget.upData.liveList!;
}
void onClickUp(data, i) {
currentMid = data.mid;
Get.find<DynamicsController>().mid.value = data.mid;
Get.find<DynamicsController>().upInfo.value = data;
Get.find<DynamicsController>().onSelectUp(data.mid);
int liveLen = liveList.length;
int upLen = upList.length;
double itemWidth = contentWidth + itemPadding.horizontal;
double screenWidth = MediaQuery.sizeOf(context).width;
double moveDistance = 0.0;
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
} else {
moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
}
data.hasUpdate = false;
scrollController.animateTo(
moveDistance,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
setState(() {});
}
@override
Widget build(BuildContext context) {
listFormat();
@ -120,30 +146,10 @@ class _UpPanelState extends State<UpPanel> {
onTap: () {
feedBack();
if (data.type == 'up') {
currentMid = data.mid;
Get.find<DynamicsController>().mid.value = data.mid;
Get.find<DynamicsController>().upInfo.value = data;
Get.find<DynamicsController>().onSelectUp(data.mid);
int liveLen = liveList.length;
int upLen = upList.length;
double itemWidth = contentWidth + itemPadding.horizontal;
double screenWidth = MediaQuery.sizeOf(context).width;
double moveDistance = 0.0;
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
moveDistance =
(i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
} else {
moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
}
data.hasUpdate = false;
scrollController.animateTo(
moveDistance,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
setState(() {});
EasyThrottle.throttle('follow', const Duration(milliseconds: 300),
() {
onClickUp(data, i);
});
} else if (data.type == 'live') {
LiveItemModel liveItem = LiveItemModel.fromJson({
'title': data.title,

View File

@ -1,6 +1,7 @@
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/pages/fav/index.dart';
import 'package:pilipala/pages/fav/widgets/item.dart';
@ -93,7 +94,12 @@ class _FavPageState extends State<FavPage> {
}
} else {
// 骨架屏
return const Text('请求中');
return ListView.builder(
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
itemCount: 10,
);
}
},
),

View File

@ -244,7 +244,7 @@ class HistoryItem extends StatelessWidget {
),
),
),
videoItem.progress != 0
videoItem.progress != 0 && videoItem.duration != 0
? Positioned(
left: 3,
right: 3,

View File

@ -10,9 +10,8 @@ class HistorySearchController extends GetxController {
final FocusNode searchFocusNode = FocusNode();
RxString searchKeyWord = ''.obs;
String hintText = '搜索';
RxString loadingStatus = 'init'.obs;
RxBool loadingStatus = false.obs;
RxString loadingText = '加载中...'.obs;
bool hasRequest = false;
late int mid;
RxString uname = ''.obs;
int pn = 1;
@ -36,8 +35,7 @@ class HistorySearchController extends GetxController {
// 提交搜索内容
void submit() {
loadingStatus.value = 'loading';
if (hasRequest) {
if (!loadingStatus.value) {
pn = 1;
searchHistories();
}
@ -48,6 +46,7 @@ class HistorySearchController extends GetxController {
if (type == 'onLoad' && loadingText.value == '没有更多了') {
return;
}
loadingStatus.value = true;
var res = await UserHttp.searchHistory(
pn: pn,
keyword: controller.value.text,
@ -63,9 +62,8 @@ class HistorySearchController extends GetxController {
loadingText.value = '没有更多了';
}
pn += 1;
hasRequest = true;
}
loadingStatus.value = 'finish';
loadingStatus.value = false;
return res;
}
@ -86,6 +84,6 @@ class HistorySearchController extends GetxController {
historyList.removeWhere((e) => e.kid == kid);
SmartDialog.showToast(res['msg']);
}
loadingStatus.value = 'finish';
// loadingStatus.value = fasle;
}
}

View File

@ -2,7 +2,6 @@ import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/pages/history/widgets/item.dart';
@ -16,20 +15,19 @@ class HistorySearchPage extends StatefulWidget {
}
class _HistorySearchPageState extends State<HistorySearchPage> {
final HistorySearchController _historySearchCtr =
Get.put(HistorySearchController());
final HistorySearchController _hisCtr = Get.put(HistorySearchController());
late ScrollController scrollController;
@override
void initState() {
super.initState();
scrollController = _historySearchCtr.scrollController;
scrollController = _hisCtr.scrollController;
scrollController.addListener(
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('history', const Duration(seconds: 1), () {
_historySearchCtr.onLoad();
_hisCtr.onLoad();
});
}
},
@ -50,19 +48,19 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
titleSpacing: 0,
actions: [
IconButton(
onPressed: () => _historySearchCtr.submit(),
onPressed: () => _hisCtr.submit(),
icon: const Icon(Icons.search_outlined, size: 22)),
const SizedBox(width: 10)
],
title: Obx(
() => TextField(
autofocus: true,
focusNode: _historySearchCtr.searchFocusNode,
controller: _historySearchCtr.controller.value,
focusNode: _hisCtr.searchFocusNode,
controller: _hisCtr.controller.value,
textInputAction: TextInputAction.search,
onChanged: (value) => _historySearchCtr.onChange(value),
onChanged: (value) => _hisCtr.onChange(value),
decoration: InputDecoration(
hintText: _historySearchCtr.hintText,
hintText: _hisCtr.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
icon: Icon(
@ -70,103 +68,61 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
size: 22,
color: Theme.of(context).colorScheme.outline,
),
onPressed: () => _historySearchCtr.onClear(),
onPressed: () => _hisCtr.onClear(),
),
),
onSubmitted: (String value) => _historySearchCtr.submit(),
onSubmitted: (String value) => _hisCtr.submit(),
),
),
),
body: Obx(
() => Column(
children: _historySearchCtr.loadingStatus.value == 'init'
? [const SizedBox()]
: [
Expanded(
child: FutureBuilder(
future: _historySearchCtr.searchHistories(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => _historySearchCtr.historyList.isNotEmpty
? ListView.builder(
controller: scrollController,
itemCount:
_historySearchCtr.historyList.length +
1,
itemBuilder: (context, index) {
if (index ==
_historySearchCtr
.historyList.length) {
return Container(
height: MediaQuery.of(context)
.padding
.bottom +
60,
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.padding
.bottom),
child: Center(
child: Obx(
() => Text(
_historySearchCtr
.loadingText.value,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.outline,
fontSize: 13),
),
),
),
);
} else {
return HistoryItem(
videoItem: _historySearchCtr
.historyList[index],
ctr: _historySearchCtr,
onChoose: null,
onUpdateMultiple: () => null,
);
}
},
)
: _historySearchCtr.loadingStatus.value ==
'loading'
? const SizedBox(child: Text('加载中...'))
: const CustomScrollView(
slivers: <Widget>[
NoData(),
],
),
);
} else {
return CustomScrollView(
slivers: <Widget>[
HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
)
],
);
}
() {
return _hisCtr.loadingStatus.value && _hisCtr.historyList.isEmpty
? ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
)
: _hisCtr.historyList.isNotEmpty
? ListView.builder(
controller: scrollController,
itemCount: _hisCtr.historyList.length + 1,
itemBuilder: (context, index) {
if (index == _hisCtr.historyList.length) {
return Container(
height: MediaQuery.of(context).padding.bottom + 60,
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
child: Center(
child: Obx(
() => Text(
_hisCtr.loadingText.value,
style: TextStyle(
color:
Theme.of(context).colorScheme.outline,
fontSize: 13,
),
),
),
),
);
} else {
// 骨架屏
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
return HistoryItem(
videoItem: _hisCtr.historyList[index],
ctr: _hisCtr,
onChoose: null,
onUpdateMultiple: () => null,
);
}
},
),
),
],
),
)
: const CustomScrollView(
slivers: <Widget>[
NoData(),
],
);
},
),
);
}

View File

@ -105,7 +105,7 @@ class _MediaPageState extends State<MediaPage>
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
ListTile(
onTap: () {},
onTap: () => Get.toNamed('/fav'),
leading: null,
dense: true,
title: Padding(

View File

@ -1,6 +1,7 @@
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/utils/route_push.dart';
import 'controller.dart';
@ -87,7 +88,12 @@ class _SubPageState extends State<SubPage> {
}
} else {
// 骨架屏
return const Text('请求中');
return ListView.builder(
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
itemCount: 10,
);
}
},
),

View File

@ -525,11 +525,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
Scaffold(
resizeToAvoidBottomInset: false,
key: vdCtr.scaffoldKey,
backgroundColor: Colors.black,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(0),
child: AppBar(
backgroundColor: Colors.transparent,
backgroundColor: Colors.black,
elevation: 0,
),
),
@ -559,8 +558,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
}
return SliverAppBar(
automaticallyImplyLeading: false,
// 假装使用一个非空变量避免Obx检测不到而罢工
pinned: vdCtr.autoPlay.value,
pinned: true,
elevation: 0,
scrolledUnderElevation: 0,
forceElevated: innerBoxIsScrolled,
@ -568,47 +566,42 @@ class _VideoDetailPageState extends State<VideoDetailPage>
backgroundColor: Colors.black,
flexibleSpace: FlexibleSpaceBar(
background: PopScope(
canPop: plPlayerController?.isFullScreen.value !=
true,
onPopInvoked: (bool didPop) {
if (plPlayerController?.isFullScreen.value ==
true) {
plPlayerController!
.triggerFullScreen(status: false);
}
if (MediaQuery.of(context).orientation ==
Orientation.landscape) {
verticalScreen();
}
},
child: LayoutBuilder(
builder: (BuildContext context,
BoxConstraints boxConstraints) {
return Stack(
children: <Widget>[
if (isShowing)
Padding(
padding: EdgeInsets.only(top: 0),
child: videoPlayerPanel,
),
canPop:
plPlayerController?.isFullScreen.value != true,
onPopInvoked: (bool didPop) {
if (plPlayerController?.isFullScreen.value ==
true) {
plPlayerController!
.triggerFullScreen(status: false);
}
if (MediaQuery.of(context).orientation ==
Orientation.landscape) {
verticalScreen();
}
},
child: Hero(
tag: heroTag,
child: Stack(
children: <Widget>[
if (isShowing) videoPlayerPanel,
/// 关闭自动播放时 手动播放
Obx(
() => Visibility(
visible: !vdCtr.autoPlay.value &&
vdCtr.isShowCover.value,
child: Positioned(
top: 0,
left: 0,
right: 0,
child: handlePlayPanel(),
),
),
/// 关闭自动播放时 手动播放
Obx(
() => Visibility(
visible: !vdCtr.autoPlay.value &&
vdCtr.isShowCover.value,
child: Positioned(
top: 0,
left: 0,
right: 0,
child: handlePlayPanel(),
),
],
);
},
)),
),
),
],
),
),
),
),
);
},
@ -627,55 +620,51 @@ class _VideoDetailPageState extends State<VideoDetailPage>
: pinnedHeaderHeight;
},
onlyOneScrollInBody: true,
body: ColoredBox(
key: Key(heroTag),
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
tabbarBuild(),
Expanded(
child: TabBarView(
controller: vdCtr.tabCtr,
children: <Widget>[
Builder(
builder: (BuildContext context) {
return CustomScrollView(
key: const PageStorageKey<String>('简介'),
slivers: <Widget>[
if (vdCtr.videoType == SearchType.video) ...[
VideoIntroPanel(bvid: vdCtr.bvid),
] else if (vdCtr.videoType ==
SearchType.media_bangumi) ...[
Obx(() => BangumiIntroPanel(
cid: vdCtr.cid.value)),
],
SliverToBoxAdapter(
child: Divider(
indent: 12,
endIndent: 12,
color: Theme.of(context)
.dividerColor
.withOpacity(0.06),
),
),
if (vdCtr.videoType == SearchType.video &&
vdCtr.enableRelatedVideo)
const RelatedVideoPanel(),
body: Column(
children: [
tabbarBuild(),
Expanded(
child: TabBarView(
controller: vdCtr.tabCtr,
children: <Widget>[
Builder(
builder: (BuildContext context) {
return CustomScrollView(
key: const PageStorageKey<String>('简介'),
slivers: <Widget>[
if (vdCtr.videoType == SearchType.video) ...[
VideoIntroPanel(bvid: vdCtr.bvid),
] else if (vdCtr.videoType ==
SearchType.media_bangumi) ...[
Obx(() =>
BangumiIntroPanel(cid: vdCtr.cid.value)),
],
);
},
SliverToBoxAdapter(
child: Divider(
indent: 12,
endIndent: 12,
color: Theme.of(context)
.dividerColor
.withOpacity(0.06),
),
),
if (vdCtr.videoType == SearchType.video &&
vdCtr.enableRelatedVideo)
const RelatedVideoPanel(),
],
);
},
),
Obx(
() => VideoReplyPanel(
bvid: vdCtr.bvid,
oid: vdCtr.oid.value,
),
Obx(
() => VideoReplyPanel(
bvid: vdCtr.bvid,
oid: vdCtr.oid.value,
),
)
],
),
)
],
),
],
),
),
],
),
),
),