Compare commits

..

10 Commits

10 changed files with 273 additions and 168 deletions

View File

@ -36,7 +36,7 @@ jobs:
if: steps.cache-flutter.outputs.cache-hit != 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: 3.10.6
flutter-version: 3.16.4
channel: any
- name: 下载项目依赖

View File

View File

@ -50,7 +50,6 @@ class _RcmdPageState extends State<RcmdPage>
_rcmdController.onLoad();
});
}
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
@ -208,12 +207,13 @@ class LoadingMore extends StatelessWidget {
child: GestureDetector(
onTap: () {
if (ctr != null) {
ctr!.isLoadingMore = true;
ctr!.onLoad();
}
},
child: Center(
child: Text(
'加载更多 👇',
'点击加载更多 👇',
style: TextStyle(
color: Theme.of(context).colorScheme.outline, fontSize: 13),
),

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/pages/setting/widgets/switch_item.dart';
import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_speed.dart';
import 'package:pilipala/utils/storage.dart';
@ -13,9 +15,11 @@ class PlaySpeedPage extends StatefulWidget {
class _PlaySpeedPageState extends State<PlaySpeedPage> {
Box videoStorage = GStrorage.video;
Box settingStorage = GStrorage.setting;
late double playSpeedDefault;
late double longPressSpeedDefault;
late List customSpeedsList;
late bool enableAutoLongPressSpeed;
List<Map<dynamic, dynamic>> sheetMenu = [
{
'id': 1,
@ -24,6 +28,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
Icons.speed,
size: 21,
),
'show': true,
},
{
'id': 2,
@ -32,6 +37,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
Icons.speed_sharp,
size: 21,
),
'show': true,
},
{
'id': -1,
@ -40,6 +46,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
Icons.delete_outline,
size: 21,
),
'show': true,
},
];
@ -55,6 +62,15 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
// 自定义倍速
customSpeedsList =
videoStorage.get(VideoBoxKey.customSpeedsList, defaultValue: []);
enableAutoLongPressSpeed = settingStorage
.get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false);
if (enableAutoLongPressSpeed) {
Map newItem = sheetMenu[1];
newItem['show'] = false;
setState(() {
sheetMenu[1] = newItem;
});
}
}
// 添加自定义倍速
@ -120,19 +136,21 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
//重要
itemCount: sheetMenu.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
onTap: () {
Navigator.pop(context);
menuAction(type, i, sheetMenu[index]['id']);
},
minLeadingWidth: 0,
iconColor: Theme.of(context).colorScheme.onSurface,
leading: sheetMenu[index]['leading'],
title: Text(
sheetMenu[index]['title'],
style: Theme.of(context).textTheme.titleSmall,
),
);
return sheetMenu[index]['show']
? ListTile(
onTap: () {
Navigator.pop(context);
menuAction(type, i, sheetMenu[index]['id']);
},
minLeadingWidth: 0,
iconColor: Theme.of(context).colorScheme.onSurface,
leading: sheetMenu[index]['leading'],
title: Text(
sheetMenu[index]['title'],
style: Theme.of(context).textTheme.titleSmall,
),
)
: const SizedBox();
},
),
);
@ -210,11 +228,27 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
title: const Text('默认倍速'),
subtitle: Text(playSpeedDefault.toString()),
),
ListTile(
dense: false,
title: const Text('默认长按倍速'),
subtitle: Text(longPressSpeedDefault.toString()),
SetSwitchItem(
title: '动态长按倍速',
subTitle: '根据默认倍速长按时自动双倍',
setKey: SettingBoxKey.enableAutoLongPressSpeed,
defaultVal: enableAutoLongPressSpeed,
callFn: (val) {
Map newItem = sheetMenu[1];
val ? newItem['show'] = false : newItem['show'] = true;
setState(() {
sheetMenu[1] = newItem;
enableAutoLongPressSpeed = val;
});
},
),
!enableAutoLongPressSpeed
? ListTile(
dense: false,
title: const Text('默认长按倍速'),
subtitle: Text(longPressSpeedDefault.toString()),
)
: const SizedBox(),
Padding(
padding: const EdgeInsets.only(
left: 14,

View File

@ -27,6 +27,7 @@ class _PagesPanelState extends State<PagesPanel> {
late int currentIndex;
String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
@ -50,6 +51,12 @@ class _PagesPanelState extends State<PagesPanel> {
setState(() {});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
@ -80,73 +87,92 @@ class _PagesPanelState extends State<PagesPanel> {
onPressed: () {
showBottomSheet(
context: context,
builder: (_) => Container(
height: widget.sheetHeight,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Container(
height: 45,
padding:
const EdgeInsets.only(left: 14, right: 14),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'合集(${episodes.length}',
style:
Theme.of(context).textTheme.titleMedium,
builder: (BuildContext context) {
return StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
WidgetsBinding.instance
.addPostFrameCallback((_) async {
await Future.delayed(
const Duration(milliseconds: 200));
_scrollController.jumpTo(currentIndex * 56);
});
return Container(
height: widget.sheetHeight,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Container(
height: 45,
padding: const EdgeInsets.only(
left: 14, right: 14),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'合集(${episodes.length}',
style: Theme.of(context)
.textTheme
.titleMedium,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
),
Divider(
height: 1,
color: Theme.of(context)
.dividerColor
.withOpacity(0.1),
),
Expanded(
child: Material(
child: ListView.builder(
itemCount: episodes.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
changeFucCall(episodes[index], index);
Get.back();
},
child: Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
left: 15,
right: 15),
child: Text(
episodes[index].pagePart!,
style: TextStyle(
),
Divider(
height: 1,
color: Theme.of(context)
.dividerColor
.withOpacity(0.1),
),
Expanded(
child: Material(
child: ListView.builder(
controller: _scrollController,
itemCount: episodes.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
changeFucCall(
episodes[index], index);
Get.back();
},
dense: false,
leading: index == currentIndex
? Image.asset(
'assets/images/live.gif',
color: Theme.of(context)
.colorScheme
.primary,
height: 12,
)
: null,
title: Text(
episodes[index].pagePart!,
style: TextStyle(
fontSize: 14,
color: index == currentIndex
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface),
),
),
);
},
.onSurface,
),
),
);
},
),
),
),
),
],
),
],
),
),
);
});
},
);
},
child: Text(

View File

@ -28,6 +28,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
late int currentIndex;
String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
@ -73,6 +74,12 @@ class _SeasonPanelState extends State<SeasonPanel> {
setState(() {});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Builder(builder: (context) {
@ -90,63 +97,82 @@ class _SeasonPanelState extends State<SeasonPanel> {
child: InkWell(
onTap: () => showBottomSheet(
context: context,
builder: (_) => Container(
height: widget.sheetHeight,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Container(
height: 45,
padding: const EdgeInsets.only(left: 14, right: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'合集(${episodes.length}',
style: Theme.of(context).textTheme.titleMedium,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 200));
_scrollController.jumpTo(currentIndex * 56);
});
return Container(
height: widget.sheetHeight,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Container(
height: 45,
padding: const EdgeInsets.only(left: 14, right: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'合集(${episodes.length}',
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
),
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
Expanded(
child: Material(
child: ListView.builder(
itemCount: episodes.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () =>
changeFucCall(episodes[index], index),
child: Padding(
padding: const EdgeInsets.only(
top: 10, bottom: 10, left: 15, right: 15),
child: Text(
episodes[index].title!,
style: TextStyle(
),
Divider(
height: 1,
color:
Theme.of(context).dividerColor.withOpacity(0.1),
),
Expanded(
child: Material(
child: ListView.builder(
controller: _scrollController,
itemCount: episodes.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () =>
changeFucCall(episodes[index], index),
dense: false,
leading: index == currentIndex
? Image.asset(
'assets/images/live.gif',
color: Theme.of(context)
.colorScheme
.primary,
height: 12,
)
: null,
title: Text(
episodes[index].title!,
style: TextStyle(
fontSize: 14,
color: index == currentIndex
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface),
),
),
);
},
.onSurface,
),
),
);
},
),
),
),
),
],
),
],
),
),
);
});
},
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),

View File

@ -8,6 +8,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/video/play/quality.dart';
import 'package:pilipala/models/video/play/url.dart';
import 'package:pilipala/pages/video/detail/index.dart';
@ -91,41 +92,41 @@ class _HeaderControlState extends State<HeaderControl> {
child: Material(
child: ListView(
children: [
ListTile(
onTap: () {},
dense: true,
enabled: false,
leading:
const Icon(Icons.network_cell_outlined, size: 20),
title: Text('省流模式', style: titleStyle),
subtitle: Text('低画质 减少视频缓存', style: subTitleStyle),
trailing: Transform.scale(
scale: 0.75,
child: Switch(
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
(Set<MaterialState> states) {
if (states.isNotEmpty &&
states.first == MaterialState.selected) {
return const Icon(Icons.done);
}
return null; // All other states will use the default thumbIcon.
}),
value: false,
onChanged: (value) => {},
),
),
),
// Obx(
// () => ListTile(
// onTap: () => {Get.back(), showSetSpeedSheet()},
// dense: true,
// leading: const Icon(Icons.speed_outlined, size: 20),
// title: Text('播放速度', style: titleStyle),
// subtitle: Text(
// '当前倍速 x${widget.controller!.playbackSpeed}',
// style: subTitleStyle),
// ListTile(
// onTap: () {},
// dense: true,
// enabled: false,
// leading:
// const Icon(Icons.network_cell_outlined, size: 20),
// title: Text('省流模式', style: titleStyle),
// subtitle: Text('低画质 减少视频缓存', style: subTitleStyle),
// trailing: Transform.scale(
// scale: 0.75,
// child: Switch(
// thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
// (Set<MaterialState> states) {
// if (states.isNotEmpty &&
// states.first == MaterialState.selected) {
// return const Icon(Icons.done);
// }
// return null; // All other states will use the default thumbIcon.
// }),
// value: false,
// onChanged: (value) => {},
// ),
// ),
// ),
ListTile(
onTap: () async {
var res = await UserHttp.toViewLater(
bvid: widget.videoDetailCtr!.bvid);
SmartDialog.showToast(res['msg']);
Get.back();
},
dense: true,
leading: const Icon(Icons.watch_later_outlined, size: 20),
title: Text('添加至「稍后再看」', style: titleStyle),
),
ListTile(
onTap: () => {Get.back(), showSetVideoQa()},
dense: true,
@ -240,7 +241,8 @@ class _HeaderControlState extends State<HeaderControl> {
DanmakuItem(
msg,
color: Colors.white,
time: widget.controller!.position.value.inMilliseconds,
time: widget
.controller!.position.value.inMilliseconds,
type: DanmakuItemType.scroll,
)
]);
@ -992,8 +994,8 @@ class _HeaderControlState extends State<HeaderControl> {
SizedBox(width: buttonSpace),
ComBtn(
icon: const Icon(
FontAwesomeIcons.sliders,
size: 15,
Icons.more_vert_outlined,
size: 18,
color: Colors.white,
),
fuc: () => showSettingSheet(),

View File

@ -220,6 +220,7 @@ class PlPlayerController {
late List speedsList;
// 缓存
double? defaultDuration;
late bool enableAutoLongPressSpeed = false;
// 播放顺序相关
PlayRepeat playRepeat = PlayRepeat.pause;
@ -249,8 +250,12 @@ class PlPlayerController {
);
_playbackSpeed.value =
videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0);
_longPressSpeed.value =
videoStorage.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0);
enableAutoLongPressSpeed = setting
.get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false);
if (!enableAutoLongPressSpeed) {
_longPressSpeed.value = videoStorage
.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0);
}
List speedsListTemp =
videoStorage.get(VideoBoxKey.customSpeedsList, defaultValue: []);
speedsList = List.from(speedsListTemp);
@ -370,9 +375,10 @@ class PlPlayerController {
}
Player player = _videoPlayerController ??
Player(
configuration: const PlayerConfiguration(
configuration: PlayerConfiguration(
// 默认缓存 5M 大小
bufferSize: 5 * 1024 * 1024,
bufferSize:
videoType.value == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024,
),
);
@ -385,6 +391,10 @@ class PlPlayerController {
await pp.setProperty("ao", "audiotrack,opensles");
}
await player.setAudioTrack(
AudioTrack.auto(),
);
// 音轨
if (dataSource.audioSource != '' && dataSource.audioSource != null) {
await pp.setProperty(
@ -393,6 +403,11 @@ class PlPlayerController {
? dataSource.audioSource!.replaceAll(';', '\\;')
: dataSource.audioSource!.replaceAll(':', '\\:'),
);
} else {
await pp.setProperty(
'audio-files',
'',
);
}
// 字幕
@ -858,7 +873,8 @@ class PlPlayerController {
}
_doubleSpeedStatus.value = val;
if (val) {
setPlaybackSpeed(longPressSpeed);
setPlaybackSpeed(
enableAutoLongPressSpeed ? playbackSpeed * 2 : longPressSpeed);
} else {
print(playbackSpeed);
setPlaybackSpeed(playbackSpeed);

View File

@ -107,6 +107,7 @@ class SettingBoxKey {
static const String p1080 = 'p1080';
static const String enableCDN = 'enableCDN';
static const String autoPiP = 'autoPiP';
static const String enableAutoLongPressSpeed = 'enableAutoLongPressSpeed';
// youtube 双击快进快退
static const String enableQuickDouble = 'enableQuickDouble';

View File

@ -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
# 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.
version: 1.0.12
version: 1.0.13
environment:
sdk: ">=2.19.6 <3.0.0"