Compare commits

..

1 Commits

Author SHA1 Message Date
bc26e79bf9 feat: 取消订阅 issues #606 2024-03-09 00:22:58 +08:00
20 changed files with 152 additions and 98 deletions

View File

@ -1,9 +0,0 @@
## 1.0.21
### 修复
+ 推荐视频全屏问题
+ 番剧全屏播放时灰屏问题
+ 评论回调导致页面卡死问题
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

View File

@ -490,6 +490,9 @@ class Api {
/// 我的订阅详情 /// 我的订阅详情
static const userSubFolderDetail = '/x/space/fav/season/list'; static const userSubFolderDetail = '/x/space/fav/season/list';
/// 取消订阅
static const userSubCancel = '/x/v3/fav/season/unfav';
/// 表情 /// 表情
static const emojiList = '/x/emote/user/panel/web'; static const emojiList = '/x/emote/user/panel/web';

View File

@ -349,4 +349,21 @@ class UserHttp {
return {'status': false, 'msg': res.data['message']}; return {'status': false, 'msg': res.data['message']};
} }
} }
// 取消订阅
static Future userSubCancel({required int seasonId}) async {
var res = await Request().post(
Api.userSubCancel,
queryParameters: {
'season_id': seasonId,
'platform': 'web',
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': '取消订阅成功'};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
} }

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
List defaultNavigationBars = [ const defaultNavigationBars = [
{ {
'id': 0, 'id': 0,
'icon': const Icon( 'icon': Icon(
Icons.home_outlined, Icons.home_outlined,
size: 21, size: 21,
), ),
'selectIcon': const Icon( 'selectIcon': Icon(
Icons.home, Icons.home,
size: 21, size: 21,
), ),
@ -16,11 +16,11 @@ List defaultNavigationBars = [
}, },
{ {
'id': 1, 'id': 1,
'icon': const Icon( 'icon': Icon(
Icons.motion_photos_on_outlined, Icons.motion_photos_on_outlined,
size: 21, size: 21,
), ),
'selectIcon': const Icon( 'selectIcon': Icon(
Icons.motion_photos_on, Icons.motion_photos_on,
size: 21, size: 21,
), ),
@ -29,11 +29,11 @@ List defaultNavigationBars = [
}, },
{ {
'id': 2, 'id': 2,
'icon': const Icon( 'icon': Icon(
Icons.video_collection_outlined, Icons.video_collection_outlined,
size: 20, size: 20,
), ),
'selectIcon': const Icon( 'selectIcon': Icon(
Icons.video_collection, Icons.video_collection,
size: 21, size: 21,
), ),

View File

@ -34,6 +34,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
userInfo = userInfoCache.get('userInfoCache'); userInfo = userInfoCache.get('userInfoCache');
userLogin.value = userInfo != null; userLogin.value = userInfo != null;
userFace.value = userInfo != null ? userInfo.face : ''; userFace.value = userInfo != null ? userInfo.face : '';
// 进行tabs配置
setTabConfig();
hideSearchBar = hideSearchBar =
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) {
@ -41,8 +43,6 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
} }
enableGradientBg = enableGradientBg =
setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
// 进行tabs配置
setTabConfig();
} }
void onRefresh() { void onRefresh() {
@ -91,21 +91,19 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
vsync: this, vsync: this,
); );
// 监听 tabController 切换 // 监听 tabController 切换
if (enableGradientBg) { tabController.animation!.addListener(() {
tabController.animation!.addListener(() { if (tabController.indexIsChanging) {
if (tabController.indexIsChanging) { if (initialIndex.value != tabController.index) {
if (initialIndex.value != tabController.index) { 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 { void searchDefault() async {

View File

@ -46,4 +46,41 @@ class SubController extends GetxController {
Future onLoad() async { Future onLoad() async {
querySubFolder(type: 'onload'); querySubFolder(type: 'onload');
} }
// 取消订阅
Future<dynamic> cancelSub({required int id}) async {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('确认要取消订阅吗?'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'取消',
style:
TextStyle(color: Theme.of(context).colorScheme.outline),
)),
TextButton(
onPressed: () async {
Get.back();
var res = await UserHttp.userSubCancel(seasonId: id);
if (res['status']) {
SmartDialog.showToast('取消订阅成功');
subFolderData.value.list!
.removeWhere((element) => element.id == id);
subFolderData.update((val) {});
} else {
SmartDialog.showToast(res['msg']);
}
},
child: const Text('确认'),
)
],
);
},
);
}
} }

View File

@ -57,8 +57,15 @@ class _SubPageState extends State<SubPage> {
itemCount: _subController.subFolderData.value.list!.length, itemCount: _subController.subFolderData.value.list!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return SubItem( return SubItem(
subFolderItem: subFolderItem:
_subController.subFolderData.value.list![index]); _subController.subFolderData.value.list![index],
fuc: () {
_subController.cancelSub(
id: _subController
.subFolderData.value.list![index].id!,
);
},
);
}, },
), ),
); );

View File

@ -8,7 +8,8 @@ import '../../../models/user/sub_folder.dart';
class SubItem extends StatelessWidget { class SubItem extends StatelessWidget {
final SubFolderItemData subFolderItem; final SubFolderItemData subFolderItem;
const SubItem({super.key, required this.subFolderItem}); final Function fuc;
const SubItem({super.key, required this.subFolderItem, required this.fuc});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -51,7 +52,7 @@ class SubItem extends StatelessWidget {
}, },
), ),
), ),
VideoContent(subFolderItem: subFolderItem) VideoContent(subFolderItem: subFolderItem, fuc: fuc)
], ],
), ),
); );
@ -64,7 +65,9 @@ class SubItem extends StatelessWidget {
class VideoContent extends StatelessWidget { class VideoContent extends StatelessWidget {
final SubFolderItemData subFolderItem; final SubFolderItemData subFolderItem;
const VideoContent({super.key, required this.subFolderItem}); final Function fuc;
const VideoContent(
{super.key, required this.subFolderItem, required this.fuc});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -100,6 +103,20 @@ class VideoContent extends StatelessWidget {
color: Theme.of(context).colorScheme.outline, color: Theme.of(context).colorScheme.outline,
), ),
), ),
const Spacer(),
SizedBox(
height: 34,
child: TextButton(
onPressed: () => fuc(),
style: TextButton.styleFrom(
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
foregroundColor: Theme.of(context).colorScheme.outline,
backgroundColor:
Theme.of(context).colorScheme.onInverseSurface, // 设置按钮背景色
),
child: const Text('取消订阅'),
),
),
], ],
), ),
), ),

View File

@ -129,7 +129,6 @@ class VideoDetailController extends GetxController
videoDetailCtr: this, videoDetailCtr: this,
floating: floating, floating: floating,
bvid: bvid, bvid: bvid,
videoType: videoType,
); );
// CDN优化 // CDN优化
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);

View File

@ -23,10 +23,7 @@ class IntroDetail extends StatelessWidget {
sheetHeight = localCache.get('sheetHeight'); sheetHeight = localCache.get('sheetHeight');
return Container( return Container(
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
padding: EdgeInsets.only( padding: const EdgeInsets.only(left: 14, right: 14),
left: 14,
right: 14,
bottom: MediaQuery.of(context).padding.bottom + 20),
height: sheetHeight, height: sheetHeight,
child: Column( child: Column(
children: [ children: [

View File

@ -280,7 +280,7 @@ class ReplyItem extends StatelessWidget {
// 完成评论,数据添加 // 完成评论,数据添加
if (value != null && value['data'] != null) if (value != null && value['data'] != null)
{ {
addReply?.call(value['data']) addReply!(value['data'])
// replyControl.replies.add(value['data']), // replyControl.replies.add(value['data']),
} }
}); });
@ -531,8 +531,8 @@ InlineSpan buildContent(
spanChilds.add(TextSpan( spanChilds.add(TextSpan(
text: str, text: str,
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () => ..onTap =
replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem))); () => replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
} }
// 分割文本并处理每个部分 // 分割文本并处理每个部分
@ -642,11 +642,6 @@ InlineSpan buildContent(
} else { } else {
final String redirectUrl = final String redirectUrl =
await UrlUtils.parseRedirectUrl(matchStr); await UrlUtils.parseRedirectUrl(matchStr);
if (redirectUrl == matchStr) {
Clipboard.setData(ClipboardData(text: matchStr));
SmartDialog.showToast('地址可能有误');
return;
}
final String pathSegment = Uri.parse(redirectUrl).path; final String pathSegment = Uri.parse(redirectUrl).path;
final String lastPathSegment = final String lastPathSegment =
pathSegment.split('/').last; pathSegment.split('/').last;

View File

@ -30,9 +30,6 @@ class VideoReplyReplyController extends GetxController {
if (type == 'init') { if (type == 'init') {
currentPage = 0; currentPage = 0;
} }
if (isLoadingMore) {
return;
}
isLoadingMore = true; isLoadingMore = true;
final res = await ReplyHttp.replyReplyList( final res = await ReplyHttp.replyReplyList(
oid: aid!, oid: aid!,
@ -44,7 +41,7 @@ class VideoReplyReplyController extends GetxController {
final List<ReplyItemModel> replies = res['data'].replies; final List<ReplyItemModel> replies = res['data'].replies;
if (replies.isNotEmpty) { if (replies.isNotEmpty) {
noMore.value = '加载中...'; noMore.value = '加载中...';
if (replies.length == res['data'].page.count) { if (replyList.length == res['data'].page.count) {
noMore.value = '没有更多了'; noMore.value = '没有更多了';
} }
currentPage++; currentPage++;
@ -53,6 +50,21 @@ class VideoReplyReplyController extends GetxController {
noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了'; noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了';
} }
if (type == 'init') { 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; replyList.value = replies;
} else { } else {
// 每次回复之后,翻页请求有且只有相同的一条回复数据 // 每次回复之后,翻页请求有且只有相同的一条回复数据

View File

@ -54,8 +54,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) { scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('replylist', const Duration(milliseconds: 200), EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
() {
_videoReplyReplyController.queryReplyList(type: 'onLoad'); _videoReplyReplyController.queryReplyList(type: 'onLoad');
}); });
} }
@ -93,7 +92,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
icon: const Icon(Icons.close, size: 20), icon: const Icon(Icons.close, size: 20),
onPressed: () { onPressed: () {
_videoReplyReplyController.currentPage = 0; _videoReplyReplyController.currentPage = 0;
widget.closePanel?.call; widget.closePanel!();
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
@ -185,8 +184,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
.add(replyItem); .add(replyItem);
}, },
replyType: widget.replyType, replyType: widget.replyType,
replyReply: (replyItem) =>
replyReply(replyItem),
); );
} }
}, },

View File

@ -572,7 +572,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
controller: plPlayerController, controller: plPlayerController,
videoDetailCtr: videoDetailController, videoDetailCtr: videoDetailController,
bvid: videoDetailController.bvid, bvid: videoDetailController.bvid,
videoType: videoDetailController.videoType,
), ),
danmuWidget: Obx( danmuWidget: Obx(
() => PlDanmaku( () => PlDanmaku(

View File

@ -19,7 +19,6 @@ import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/http/danmaku.dart'; import 'package:pilipala/http/danmaku.dart';
import 'package:pilipala/services/shutdown_timer_service.dart'; import 'package:pilipala/services/shutdown_timer_service.dart';
import '../../../../models/common/search_type.dart';
import '../../../../models/video_detail_res.dart'; import '../../../../models/video_detail_res.dart';
import '../introduction/index.dart'; import '../introduction/index.dart';
@ -29,14 +28,12 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
this.videoDetailCtr, this.videoDetailCtr,
this.floating, this.floating,
this.bvid, this.bvid,
this.videoType,
super.key, super.key,
}); });
final PlPlayerController? controller; final PlPlayerController? controller;
final VideoDetailController? videoDetailCtr; final VideoDetailController? videoDetailCtr;
final Floating? floating; final Floating? floating;
final String? bvid; final String? bvid;
final SearchType? videoType;
@override @override
State<HeaderControl> createState() => _HeaderControlState(); State<HeaderControl> createState() => _HeaderControlState();
@ -79,11 +76,7 @@ class _HeaderControlState extends State<HeaderControl> {
} else { } else {
showTitle = false; showTitle = false;
} }
setState(() {});
/// TODO setState() called after dispose()
if (mounted) {
setState(() {});
}
}); });
} }
@ -1114,16 +1107,14 @@ class _HeaderControlState extends State<HeaderControl> {
}, },
), ),
SizedBox(width: buttonSpace), SizedBox(width: buttonSpace),
if (showTitle && if (showTitle && isLandscape) ...[
isLandscape &&
widget.videoType == SearchType.video) ...[
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200), constraints: BoxConstraints(maxWidth: 200),
child: Text( child: Text(
videoIntroController.videoDetail.value.title ?? '', videoIntroController.videoDetail.value.title!,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16, fontSize: 16,

View File

@ -277,7 +277,8 @@ class PlPlayerController {
danmakuDurationVal = danmakuDurationVal =
localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0); 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( playRepeat = PlayRepeat.values.toList().firstWhere(
(e) => (e) =>
e.value == e.value ==
@ -534,10 +535,8 @@ class PlPlayerController {
if (event) { if (event) {
playerStatus.status.value = PlayerStatus.playing; playerStatus.status.value = PlayerStatus.playing;
} else { } else {
playerStatus.status.value = PlayerStatus.paused; // playerStatus.status.value = PlayerStatus.paused;
} }
videoPlayerServiceHandler.onStatusChange(
playerStatus.status.value, isBuffering.value);
/// 触发回调事件 /// 触发回调事件
for (var element in _statusListeners) { for (var element in _statusListeners) {

View File

@ -26,7 +26,6 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
static final List<MediaItem> _item = []; static final List<MediaItem> _item = [];
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
bool enableBackgroundPlay = false; bool enableBackgroundPlay = false;
PlPlayerController player = PlPlayerController.getInstance();
VideoPlayerServiceHandler() { VideoPlayerServiceHandler() {
revalidateSetting(); revalidateSetting();
@ -39,12 +38,12 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
@override @override
Future<void> play() async { Future<void> play() async {
player.play(); PlPlayerController.getInstance().play();
} }
@override @override
Future<void> pause() async { Future<void> pause() async {
player.pause(); PlPlayerController.getInstance().pause();
} }
@override @override
@ -52,7 +51,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
playbackState.add(playbackState.value.copyWith( playbackState.add(playbackState.value.copyWith(
updatePosition: position, updatePosition: position,
)); ));
await player.seekTo(position); await PlPlayerController.getInstance().seekTo(position);
} }
Future<void> setMediaItem(MediaItem newMediaItem) async { Future<void> setMediaItem(MediaItem newMediaItem) async {

View File

@ -20,7 +20,7 @@ class AudioSessionHandler {
session.interruptionEventStream.listen((event) { session.interruptionEventStream.listen((event) {
final player = PlPlayerController.getInstance(); final player = PlPlayerController.getInstance();
if (event.begin) { if (event.begin) {
if (!player.playerStatus.playing) return; if (player.playerStatus != PlayerStatus.playing) return;
switch (event.type) { switch (event.type) {
case AudioInterruptionType.duck: case AudioInterruptionType.duck:
player.setVolume(player.volume.value * 0.5); player.setVolume(player.volume.value * 0.5);
@ -52,7 +52,7 @@ class AudioSessionHandler {
// 耳机拔出暂停 // 耳机拔出暂停
session.becomingNoisyEventStream.listen((_) { session.becomingNoisyEventStream.listen((_) {
final player = PlPlayerController.getInstance(); final player = PlPlayerController.getInstance();
if (player.playerStatus.playing) { if (player.playerStatus == PlayerStatus.playing) {
player.pause(); player.pause();
} }
}); });

View File

@ -14,23 +14,19 @@ class UrlUtils {
dio.options.validateStatus = (status) { dio.options.validateStatus = (status) {
return status == 200 || status == 301 || status == 302; return status == 200 || status == 301 || status == 302;
}; };
try { final response = await dio.get(url);
final response = await dio.get(url); if (response.statusCode == 302) {
if (response.statusCode == 302) { redirectUrl = response.headers['location']?.first as String;
redirectUrl = response.headers['location']?.first as String; if (redirectUrl.endsWith('/')) {
if (redirectUrl.endsWith('/')) { redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1);
redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1); }
} } else {
} else { if (url.endsWith('/')) {
if (url.endsWith('/')) { url = url.substring(0, url.length - 1);
url = url.substring(0, url.length - 1);
}
return url;
} }
return redirectUrl;
} catch (err) {
return url; return url;
} }
return redirectUrl;
} }
// 匹配url路由跳转 // 匹配url路由跳转

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 # 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.21+1021 version: 1.0.20+1020
environment: environment:
sdk: ">=2.19.6 <3.0.0" sdk: ">=2.19.6 <3.0.0"