Compare commits
12 Commits
feature-se
...
feature-ba
Author | SHA1 | Date | |
---|---|---|---|
105a29f311 | |||
3bf6136bc6 | |||
ab24da5f55 | |||
ed0b43eff1 | |||
ab9ae3a481 | |||
d728b1fb6d | |||
12e947ef84 | |||
3fad86e7e3 | |||
fea70011cb | |||
32cdb27f7c | |||
eb4435045b | |||
f1b829cec1 |
9
change_log/1.0.21.0306.md
Normal file
9
change_log/1.0.21.0306.md
Normal file
@ -0,0 +1,9 @@
|
||||
## 1.0.21
|
||||
|
||||
### 修复
|
||||
+ 推荐视频全屏问题
|
||||
+ 番剧全屏播放时灰屏问题
|
||||
+ 评论回调导致页面卡死问题
|
||||
|
||||
更多更新日志可在Github上查看
|
||||
问题反馈、功能建议请查看「关于」页面。
|
@ -16,6 +16,7 @@ import 'package:pilipala/pages/search/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/router/app_pages.dart';
|
||||
import 'package:pilipala/pages/main/view.dart';
|
||||
import 'package:pilipala/services/disable_battery_opt.dart';
|
||||
import 'package:pilipala/services/service_locator.dart';
|
||||
import 'package:pilipala/utils/app_scheme.dart';
|
||||
import 'package:pilipala/utils/data.dart';
|
||||
@ -71,6 +72,7 @@ void main() async {
|
||||
));
|
||||
Data.init();
|
||||
PiliSchame.init();
|
||||
DisableBatteryOpt();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const defaultNavigationBars = [
|
||||
List defaultNavigationBars = [
|
||||
{
|
||||
'id': 0,
|
||||
'icon': Icon(
|
||||
'icon': const Icon(
|
||||
Icons.home_outlined,
|
||||
size: 21,
|
||||
),
|
||||
'selectIcon': Icon(
|
||||
'selectIcon': const Icon(
|
||||
Icons.home,
|
||||
size: 21,
|
||||
),
|
||||
@ -16,11 +16,11 @@ const defaultNavigationBars = [
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'icon': Icon(
|
||||
'icon': const Icon(
|
||||
Icons.motion_photos_on_outlined,
|
||||
size: 21,
|
||||
),
|
||||
'selectIcon': Icon(
|
||||
'selectIcon': const Icon(
|
||||
Icons.motion_photos_on,
|
||||
size: 21,
|
||||
),
|
||||
@ -29,11 +29,11 @@ const defaultNavigationBars = [
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'icon': Icon(
|
||||
'icon': const Icon(
|
||||
Icons.video_collection_outlined,
|
||||
size: 20,
|
||||
),
|
||||
'selectIcon': Icon(
|
||||
'selectIcon': const Icon(
|
||||
Icons.video_collection,
|
||||
size: 21,
|
||||
),
|
||||
|
@ -34,8 +34,6 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
userLogin.value = userInfo != null;
|
||||
userFace.value = userInfo != null ? userInfo.face : '';
|
||||
// 进行tabs配置
|
||||
setTabConfig();
|
||||
hideSearchBar =
|
||||
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
|
||||
if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) {
|
||||
@ -43,6 +41,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
}
|
||||
enableGradientBg =
|
||||
setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
|
||||
// 进行tabs配置
|
||||
setTabConfig();
|
||||
}
|
||||
|
||||
void onRefresh() {
|
||||
@ -91,19 +91,21 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
vsync: this,
|
||||
);
|
||||
// 监听 tabController 切换
|
||||
tabController.animation!.addListener(() {
|
||||
if (tabController.indexIsChanging) {
|
||||
if (initialIndex.value != tabController.index) {
|
||||
initialIndex.value = tabController.index;
|
||||
if (enableGradientBg) {
|
||||
tabController.animation!.addListener(() {
|
||||
if (tabController.indexIsChanging) {
|
||||
if (initialIndex.value != tabController.index) {
|
||||
initialIndex.value = tabController.index;
|
||||
}
|
||||
} else {
|
||||
final int temp = tabController.animation!.value.round();
|
||||
if (initialIndex.value != temp) {
|
||||
initialIndex.value = temp;
|
||||
tabController.index = initialIndex.value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final int temp = tabController.animation!.value.round();
|
||||
if (initialIndex.value != temp) {
|
||||
initialIndex.value = temp;
|
||||
tabController.index = initialIndex.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void searchDefault() async {
|
||||
|
@ -129,6 +129,7 @@ class VideoDetailController extends GetxController
|
||||
videoDetailCtr: this,
|
||||
floating: floating,
|
||||
bvid: bvid,
|
||||
videoType: videoType,
|
||||
);
|
||||
// CDN优化
|
||||
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
|
||||
|
@ -23,7 +23,10 @@ class IntroDetail extends StatelessWidget {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
padding: EdgeInsets.only(
|
||||
left: 14,
|
||||
right: 14,
|
||||
bottom: MediaQuery.of(context).padding.bottom + 20),
|
||||
height: sheetHeight,
|
||||
child: Column(
|
||||
children: [
|
||||
|
@ -280,7 +280,7 @@ class ReplyItem extends StatelessWidget {
|
||||
// 完成评论,数据添加
|
||||
if (value != null && value['data'] != null)
|
||||
{
|
||||
addReply!(value['data'])
|
||||
addReply?.call(value['data'])
|
||||
// replyControl.replies.add(value['data']),
|
||||
}
|
||||
});
|
||||
@ -531,8 +531,8 @@ InlineSpan buildContent(
|
||||
spanChilds.add(TextSpan(
|
||||
text: str,
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap =
|
||||
() => replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||
..onTap = () =>
|
||||
replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||
}
|
||||
|
||||
// 分割文本并处理每个部分
|
||||
@ -642,6 +642,11 @@ InlineSpan buildContent(
|
||||
} else {
|
||||
final String redirectUrl =
|
||||
await UrlUtils.parseRedirectUrl(matchStr);
|
||||
if (redirectUrl == matchStr) {
|
||||
Clipboard.setData(ClipboardData(text: matchStr));
|
||||
SmartDialog.showToast('地址可能有误');
|
||||
return;
|
||||
}
|
||||
final String pathSegment = Uri.parse(redirectUrl).path;
|
||||
final String lastPathSegment =
|
||||
pathSegment.split('/').last;
|
||||
|
@ -30,6 +30,9 @@ class VideoReplyReplyController extends GetxController {
|
||||
if (type == 'init') {
|
||||
currentPage = 0;
|
||||
}
|
||||
if (isLoadingMore) {
|
||||
return;
|
||||
}
|
||||
isLoadingMore = true;
|
||||
final res = await ReplyHttp.replyReplyList(
|
||||
oid: aid!,
|
||||
@ -41,7 +44,7 @@ class VideoReplyReplyController extends GetxController {
|
||||
final List<ReplyItemModel> replies = res['data'].replies;
|
||||
if (replies.isNotEmpty) {
|
||||
noMore.value = '加载中...';
|
||||
if (replyList.length == res['data'].page.count) {
|
||||
if (replies.length == res['data'].page.count) {
|
||||
noMore.value = '没有更多了';
|
||||
}
|
||||
currentPage++;
|
||||
@ -50,21 +53,6 @@ class VideoReplyReplyController extends GetxController {
|
||||
noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了';
|
||||
}
|
||||
if (type == 'init') {
|
||||
// List<ReplyItemModel> replies = res['data'].replies;
|
||||
// 添加置顶回复
|
||||
// if (res['data'].upper.top != null) {
|
||||
// bool flag = false;
|
||||
// for (var i = 0; i < res['data'].topReplies.length; i++) {
|
||||
// if (res['data'].topReplies[i].rpid == res['data'].upper.top.rpid) {
|
||||
// flag = true;
|
||||
// }
|
||||
// }
|
||||
// if (!flag) {
|
||||
// replies.insert(0, res['data'].upper.top);
|
||||
// }
|
||||
// }
|
||||
// replies.insertAll(0, res['data'].topReplies);
|
||||
// res['data'].replies = replies;
|
||||
replyList.value = replies;
|
||||
} else {
|
||||
// 每次回复之后,翻页请求有且只有相同的一条回复数据
|
||||
|
@ -54,7 +54,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
() {
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 300) {
|
||||
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
|
||||
EasyThrottle.throttle('replylist', const Duration(milliseconds: 200),
|
||||
() {
|
||||
_videoReplyReplyController.queryReplyList(type: 'onLoad');
|
||||
});
|
||||
}
|
||||
@ -92,7 +93,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
icon: const Icon(Icons.close, size: 20),
|
||||
onPressed: () {
|
||||
_videoReplyReplyController.currentPage = 0;
|
||||
widget.closePanel!();
|
||||
widget.closePanel?.call;
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
@ -184,6 +185,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
.add(replyItem);
|
||||
},
|
||||
replyType: widget.replyType,
|
||||
replyReply: (replyItem) =>
|
||||
replyReply(replyItem),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -572,6 +572,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
controller: plPlayerController,
|
||||
videoDetailCtr: videoDetailController,
|
||||
bvid: videoDetailController.bvid,
|
||||
videoType: videoDetailController.videoType,
|
||||
),
|
||||
danmuWidget: Obx(
|
||||
() => PlDanmaku(
|
||||
|
@ -19,6 +19,7 @@ import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/http/danmaku.dart';
|
||||
import 'package:pilipala/services/shutdown_timer_service.dart';
|
||||
import '../../../../models/common/search_type.dart';
|
||||
import '../../../../models/video_detail_res.dart';
|
||||
import '../introduction/index.dart';
|
||||
|
||||
@ -28,12 +29,14 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||
this.videoDetailCtr,
|
||||
this.floating,
|
||||
this.bvid,
|
||||
this.videoType,
|
||||
super.key,
|
||||
});
|
||||
final PlPlayerController? controller;
|
||||
final VideoDetailController? videoDetailCtr;
|
||||
final Floating? floating;
|
||||
final String? bvid;
|
||||
final SearchType? videoType;
|
||||
|
||||
@override
|
||||
State<HeaderControl> createState() => _HeaderControlState();
|
||||
@ -76,7 +79,11 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
} else {
|
||||
showTitle = false;
|
||||
}
|
||||
setState(() {});
|
||||
|
||||
/// TODO setState() called after dispose()
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1107,14 +1114,16 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
},
|
||||
),
|
||||
SizedBox(width: buttonSpace),
|
||||
if (showTitle && isLandscape) ...[
|
||||
if (showTitle &&
|
||||
isLandscape &&
|
||||
widget.videoType == SearchType.video) ...[
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: 200),
|
||||
constraints: const BoxConstraints(maxWidth: 200),
|
||||
child: Text(
|
||||
videoIntroController.videoDetail.value.title!,
|
||||
videoIntroController.videoDetail.value.title ?? '',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
|
@ -277,8 +277,7 @@ class PlPlayerController {
|
||||
danmakuDurationVal =
|
||||
localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0);
|
||||
// 描边粗细
|
||||
strokeWidth =
|
||||
localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5);
|
||||
strokeWidth = localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5);
|
||||
playRepeat = PlayRepeat.values.toList().firstWhere(
|
||||
(e) =>
|
||||
e.value ==
|
||||
@ -535,8 +534,10 @@ class PlPlayerController {
|
||||
if (event) {
|
||||
playerStatus.status.value = PlayerStatus.playing;
|
||||
} else {
|
||||
// playerStatus.status.value = PlayerStatus.paused;
|
||||
playerStatus.status.value = PlayerStatus.paused;
|
||||
}
|
||||
videoPlayerServiceHandler.onStatusChange(
|
||||
playerStatus.status.value, isBuffering.value);
|
||||
|
||||
/// 触发回调事件
|
||||
for (var element in _statusListeners) {
|
||||
|
@ -26,6 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
|
||||
static final List<MediaItem> _item = [];
|
||||
Box setting = GStrorage.setting;
|
||||
bool enableBackgroundPlay = false;
|
||||
PlPlayerController player = PlPlayerController.getInstance();
|
||||
|
||||
VideoPlayerServiceHandler() {
|
||||
revalidateSetting();
|
||||
@ -38,12 +39,12 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
|
||||
|
||||
@override
|
||||
Future<void> play() async {
|
||||
PlPlayerController.getInstance().play();
|
||||
player.play();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pause() async {
|
||||
PlPlayerController.getInstance().pause();
|
||||
player.pause();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -51,7 +52,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
|
||||
playbackState.add(playbackState.value.copyWith(
|
||||
updatePosition: position,
|
||||
));
|
||||
await PlPlayerController.getInstance().seekTo(position);
|
||||
await player.seekTo(position);
|
||||
}
|
||||
|
||||
Future<void> setMediaItem(MediaItem newMediaItem) async {
|
||||
|
@ -20,7 +20,7 @@ class AudioSessionHandler {
|
||||
session.interruptionEventStream.listen((event) {
|
||||
final player = PlPlayerController.getInstance();
|
||||
if (event.begin) {
|
||||
if (player.playerStatus != PlayerStatus.playing) return;
|
||||
if (!player.playerStatus.playing) return;
|
||||
switch (event.type) {
|
||||
case AudioInterruptionType.duck:
|
||||
player.setVolume(player.volume.value * 0.5);
|
||||
@ -52,7 +52,7 @@ class AudioSessionHandler {
|
||||
// 耳机拔出暂停
|
||||
session.becomingNoisyEventStream.listen((_) {
|
||||
final player = PlPlayerController.getInstance();
|
||||
if (player.playerStatus == PlayerStatus.playing) {
|
||||
if (player.playerStatus.playing) {
|
||||
player.pause();
|
||||
}
|
||||
});
|
||||
|
40
lib/services/disable_battery_opt.dart
Normal file
40
lib/services/disable_battery_opt.dart
Normal file
@ -0,0 +1,40 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:disable_battery_optimization/disable_battery_optimization.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
void DisableBatteryOpt() async {
|
||||
if (!Platform.isAndroid) {
|
||||
return;
|
||||
}
|
||||
// 本地缓存中读取 是否禁用了电池优化 默认未禁用
|
||||
bool isDisableBatteryOptLocal =
|
||||
GStrorage.localCache.get('isDisableBatteryOptLocal', defaultValue: false);
|
||||
if (!isDisableBatteryOptLocal) {
|
||||
final isBatteryOptimizationDisabled =
|
||||
await DisableBatteryOptimization.isBatteryOptimizationDisabled;
|
||||
if (isBatteryOptimizationDisabled == false) {
|
||||
final hasDisabled = await DisableBatteryOptimization
|
||||
.showDisableBatteryOptimizationSettings();
|
||||
// 设置为已禁用
|
||||
GStrorage.localCache.put('isDisableBatteryOptLocal', hasDisabled == true);
|
||||
}
|
||||
}
|
||||
|
||||
bool isManufacturerBatteryOptimizationDisabled = GStrorage.localCache
|
||||
.get('isManufacturerBatteryOptimizationDisabled', defaultValue: false);
|
||||
if (!isManufacturerBatteryOptimizationDisabled) {
|
||||
final isManBatteryOptimizationDisabled = await DisableBatteryOptimization
|
||||
.isManufacturerBatteryOptimizationDisabled;
|
||||
if (isManBatteryOptimizationDisabled == false) {
|
||||
final hasDisabled = await DisableBatteryOptimization
|
||||
.showDisableManufacturerBatteryOptimizationSettings(
|
||||
"当前设备可能有额外的电池优化",
|
||||
"按照步骤操作以禁用电池优化,以保证应用在后台正常运行",
|
||||
);
|
||||
// 设置为已禁用
|
||||
GStrorage.localCache.put(
|
||||
'isManufacturerBatteryOptimizationDisabled', hasDisabled == true);
|
||||
}
|
||||
}
|
||||
}
|
@ -170,6 +170,10 @@ class LocalCacheKey {
|
||||
// 代理host port
|
||||
systemProxyHost = 'systemProxyHost',
|
||||
systemProxyPort = 'systemProxyPort';
|
||||
|
||||
static const String isDisableBatteryOptLocal = 'isDisableBatteryOptLocal',
|
||||
isManufacturerBatteryOptimizationDisabled =
|
||||
'isManufacturerBatteryOptimizationDisabled';
|
||||
}
|
||||
|
||||
class VideoBoxKey {
|
||||
|
@ -14,19 +14,23 @@ class UrlUtils {
|
||||
dio.options.validateStatus = (status) {
|
||||
return status == 200 || status == 301 || status == 302;
|
||||
};
|
||||
final response = await dio.get(url);
|
||||
if (response.statusCode == 302) {
|
||||
redirectUrl = response.headers['location']?.first as String;
|
||||
if (redirectUrl.endsWith('/')) {
|
||||
redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1);
|
||||
}
|
||||
} else {
|
||||
if (url.endsWith('/')) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
try {
|
||||
final response = await dio.get(url);
|
||||
if (response.statusCode == 302) {
|
||||
redirectUrl = response.headers['location']?.first as String;
|
||||
if (redirectUrl.endsWith('/')) {
|
||||
redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1);
|
||||
}
|
||||
} else {
|
||||
if (url.endsWith('/')) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
return redirectUrl;
|
||||
} catch (err) {
|
||||
return url;
|
||||
}
|
||||
return redirectUrl;
|
||||
}
|
||||
|
||||
// 匹配url路由跳转
|
||||
|
@ -393,6 +393,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
disable_battery_optimization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: disable_battery_optimization
|
||||
sha256: "6b2ba802f984af141faf1b6b5fb956d5ef01f9cd555597c35b9cc335a03185ba"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
dismissible_page:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -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.20+1020
|
||||
version: 1.0.21+1021
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.6 <3.0.0"
|
||||
@ -140,6 +140,8 @@ dependencies:
|
||||
catcher_2: ^1.1.0
|
||||
logger: ^2.0.2+1
|
||||
path: 1.8.3
|
||||
# 电池优化
|
||||
disable_battery_optimization: ^1.1.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user