Merge branch 'main' into feature-updateVideoDetailStructure
This commit is contained in:
@ -45,7 +45,7 @@
|
|||||||
android:fullBackupContent="false"
|
android:fullBackupContent="false"
|
||||||
tools:replace="android:allowBackup">
|
tools:replace="android:allowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name="com.ryanheise.audioservice.AudioServiceActivity"
|
android:name="com.guozhigq.pilipala.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package com.guozhigq.pilipala
|
package com.guozhigq.pilipala
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
// import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import com.ryanheise.audioservice.AudioServiceActivity;
|
||||||
|
|
||||||
|
class MainActivity: AudioServiceActivity() {
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
change_log/1.0.22.0430.md
Normal file
27
change_log/1.0.22.0430.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
## 1.0.22
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
+ 字幕
|
||||||
|
+ 全屏时选集
|
||||||
|
+ 动态转发
|
||||||
|
+ 评论视频并转发
|
||||||
|
+ 收藏夹删除
|
||||||
|
+ 合集显示封面
|
||||||
|
+ 底部导航栏编辑、排序功能
|
||||||
|
+ 历史记录进度条展示
|
||||||
|
+ 直播画质切换
|
||||||
|
+ 排行榜功能
|
||||||
|
+ 视频详情页推荐视频开关
|
||||||
|
+ 显示联合投稿up
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
+ 收藏夹个数错误
|
||||||
|
+ 封面保存权限问题
|
||||||
|
+ 合集最后1p未展示
|
||||||
|
+ up主页关注按钮触发灰屏
|
||||||
|
|
||||||
|
### 优化
|
||||||
|
+ 视频简介查看逻辑
|
||||||
|
|
||||||
|
更多更新日志可在Github上查看
|
||||||
|
问题反馈、功能建议请查看「关于」页面。
|
||||||
14
change_log/1.0.23.0504.md
Normal file
14
change_log/1.0.23.0504.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
## 1.0.23
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
+ 封面下载
|
||||||
|
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
+ 全屏问题
|
||||||
|
+ 视频播放器灰屏问题
|
||||||
|
+ 评论区点击区域问题
|
||||||
|
|
||||||
|
|
||||||
|
更多更新日志可在Github上查看
|
||||||
|
问题反馈、功能建议请查看「关于」页面。
|
||||||
@ -49,6 +49,8 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
<string>请允许APP保存图片到相册</string>
|
<string>请允许APP保存图片到相册</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>请允许APP保存图片到相册</string>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>App需要您的同意,才能访问相册</string>
|
<string>App需要您的同意,才能访问相册</string>
|
||||||
<key>NSAppleMusicUsageDescription</key>
|
<key>NSAppleMusicUsageDescription</key>
|
||||||
|
|||||||
@ -34,6 +34,9 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final int defaultImgQuality = GlobalData().imgQuality;
|
final int defaultImgQuality = GlobalData().imgQuality;
|
||||||
|
if (src == '' || src == null) {
|
||||||
|
return placeholder(context);
|
||||||
|
}
|
||||||
final String imageUrl =
|
final String imageUrl =
|
||||||
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp';
|
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp';
|
||||||
int? memCacheWidth, memCacheHeight;
|
int? memCacheWidth, memCacheHeight;
|
||||||
|
|||||||
@ -1,87 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../../utils/download.dart';
|
|
||||||
import '../constants.dart';
|
|
||||||
import 'network_img_layer.dart';
|
|
||||||
|
|
||||||
class OverlayPop extends StatelessWidget {
|
|
||||||
const OverlayPop({super.key, this.videoItem, this.closeFn});
|
|
||||||
|
|
||||||
final dynamic videoItem;
|
|
||||||
final Function? closeFn;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final double imgWidth = MediaQuery.sizeOf(context).width - 8 * 2;
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.background,
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
width: imgWidth,
|
|
||||||
height: imgWidth / StyleString.aspectRatio,
|
|
||||||
src: videoItem.pic! as String,
|
|
||||||
quality: 100,
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 8,
|
|
||||||
top: 8,
|
|
||||||
child: Container(
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.black.withOpacity(0.3),
|
|
||||||
borderRadius:
|
|
||||||
const BorderRadius.all(Radius.circular(20))),
|
|
||||||
child: IconButton(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () => closeFn!(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(12, 10, 8, 10),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
videoItem.title! as String,
|
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
IconButton(
|
|
||||||
tooltip: '保存封面图',
|
|
||||||
onPressed: () async {
|
|
||||||
await DownloadUtils.downloadImg(
|
|
||||||
videoItem.pic != null
|
|
||||||
? videoItem.pic as String
|
|
||||||
: videoItem.cover as String,
|
|
||||||
);
|
|
||||||
// closeFn!();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.download, size: 20),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/http/constants.dart';
|
||||||
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
|
import 'package:pilipala/utils/url_utils.dart';
|
||||||
import '../../http/search.dart';
|
import '../../http/search.dart';
|
||||||
import '../../http/user.dart';
|
import '../../http/user.dart';
|
||||||
import '../../http/video.dart';
|
import '../../http/video.dart';
|
||||||
@ -16,8 +21,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
const VideoCardH({
|
const VideoCardH({
|
||||||
super.key,
|
super.key,
|
||||||
required this.videoItem,
|
required this.videoItem,
|
||||||
this.longPress,
|
this.onPressedFn,
|
||||||
this.longPressEnd,
|
|
||||||
this.source = 'normal',
|
this.source = 'normal',
|
||||||
this.showOwner = true,
|
this.showOwner = true,
|
||||||
this.showView = true,
|
this.showView = true,
|
||||||
@ -27,8 +31,8 @@ class VideoCardH extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final videoItem;
|
final videoItem;
|
||||||
final Function()? longPress;
|
final Function()? onPressedFn;
|
||||||
final Function()? longPressEnd;
|
// normal 推荐, later 稍后再看, search 搜索
|
||||||
final String source;
|
final String source;
|
||||||
final bool showOwner;
|
final bool showOwner;
|
||||||
final bool showView;
|
final bool showView;
|
||||||
@ -45,24 +49,27 @@ class VideoCardH extends StatelessWidget {
|
|||||||
type = videoItem.type;
|
type = videoItem.type;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
final String heroTag = Utils.makeHeroTag(aid);
|
final String heroTag = Utils.makeHeroTag(aid);
|
||||||
return GestureDetector(
|
return InkWell(
|
||||||
onLongPress: () {
|
|
||||||
if (longPress != null) {
|
|
||||||
longPress!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// onLongPressEnd: (details) {
|
|
||||||
// if (longPressEnd != null) {
|
|
||||||
// longPressEnd!();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
try {
|
try {
|
||||||
if (type == 'ketang') {
|
if (type == 'ketang') {
|
||||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (showCharge && videoItem?.typeid == 33) {
|
||||||
|
final String redirectUrl = await UrlUtils.parseRedirectUrl(
|
||||||
|
'${HttpString.baseUrl}/video/$bvid/');
|
||||||
|
final String lastPathSegment = redirectUrl.split('/').last;
|
||||||
|
if (lastPathSegment.contains('ss')) {
|
||||||
|
RoutePush.bangumiPush(
|
||||||
|
Utils.matchNum(lastPathSegment).first, null);
|
||||||
|
}
|
||||||
|
if (lastPathSegment.contains('ep')) {
|
||||||
|
RoutePush.bangumiPush(
|
||||||
|
null, Utils.matchNum(lastPathSegment).first);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
final int cid =
|
final int cid =
|
||||||
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||||
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
||||||
@ -71,6 +78,11 @@ class VideoCardH extends StatelessWidget {
|
|||||||
SmartDialog.showToast(err.toString());
|
SmartDialog.showToast(err.toString());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context,
|
||||||
|
videoItem,
|
||||||
|
SmartDialog.dismiss,
|
||||||
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
||||||
@ -142,6 +154,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
showView: showView,
|
showView: showView,
|
||||||
showDanmaku: showDanmaku,
|
showDanmaku: showDanmaku,
|
||||||
showPubdate: showPubdate,
|
showPubdate: showPubdate,
|
||||||
|
onPressedFn: onPressedFn,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -149,7 +162,6 @@ class VideoCardH extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,6 +174,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
final bool showView;
|
final bool showView;
|
||||||
final bool showDanmaku;
|
final bool showDanmaku;
|
||||||
final bool showPubdate;
|
final bool showPubdate;
|
||||||
|
final Function()? onPressedFn;
|
||||||
|
|
||||||
const VideoContent({
|
const VideoContent({
|
||||||
super.key,
|
super.key,
|
||||||
@ -171,6 +184,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
this.showView = true,
|
this.showView = true,
|
||||||
this.showDanmaku = true,
|
this.showDanmaku = true,
|
||||||
this.showPubdate = false,
|
this.showPubdate = false,
|
||||||
|
this.onPressedFn,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -181,7 +195,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (videoItem.title is String) ...[
|
if (source == 'normal' || source == 'later') ...[
|
||||||
Text(
|
Text(
|
||||||
videoItem.title as String,
|
videoItem.title as String,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
@ -196,7 +210,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: [
|
children: [
|
||||||
for (final i in videoItem.title) ...[
|
for (final i in videoItem.titleList) ...[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: i['text'] as String,
|
text: i['text'] as String,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -263,82 +277,86 @@ class VideoContent extends StatelessWidget {
|
|||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
danmu: videoItem.stat.danmaku as int,
|
danmu: videoItem.stat.danmaku as int,
|
||||||
),
|
),
|
||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
// SizedBox(
|
|
||||||
// width: 20,
|
|
||||||
// height: 20,
|
|
||||||
// child: IconButton(
|
|
||||||
// tooltip: '稍后再看',
|
|
||||||
// style: ButtonStyle(
|
|
||||||
// padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
// ),
|
|
||||||
// onPressed: () async {
|
|
||||||
// var res =
|
|
||||||
// await UserHttp.toViewLater(bvid: videoItem.bvid);
|
|
||||||
// SmartDialog.showToast(res['msg']);
|
|
||||||
// },
|
|
||||||
// icon: Icon(
|
|
||||||
// Icons.more_vert_outlined,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// size: 14,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
if (source == 'normal')
|
if (source == 'normal')
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
child: PopupMenuButton<String>(
|
child: IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
onPressed: () {
|
||||||
|
feedBack();
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) {
|
||||||
|
return MorePanel(videoItem: videoItem);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.more_vert_outlined,
|
Icons.more_vert_outlined,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
size: 14,
|
size: 14,
|
||||||
),
|
),
|
||||||
position: PopupMenuPosition.under,
|
),
|
||||||
// constraints: const BoxConstraints(maxHeight: 35),
|
),
|
||||||
onSelected: (String type) {},
|
if (source == 'later') ...[
|
||||||
itemBuilder: (BuildContext context) =>
|
IconButton(
|
||||||
<PopupMenuEntry<String>>[
|
style: ButtonStyle(
|
||||||
PopupMenuItem<String>(
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
onTap: () async {
|
),
|
||||||
var res = await UserHttp.toViewLater(
|
onPressed: () => onPressedFn?.call(),
|
||||||
bvid: videoItem.bvid as String);
|
icon: Icon(
|
||||||
SmartDialog.showToast(res['msg']);
|
Icons.clear_outlined,
|
||||||
},
|
color: Theme.of(context).colorScheme.outline,
|
||||||
value: 'pause',
|
size: 18,
|
||||||
height: 40,
|
),
|
||||||
child: const Row(
|
)
|
||||||
children: [
|
],
|
||||||
Icon(Icons.watch_later_outlined, size: 16),
|
],
|
||||||
SizedBox(width: 6),
|
),
|
||||||
Text('稍后再看', style: TextStyle(fontSize: 13))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const PopupMenuDivider(),
|
);
|
||||||
PopupMenuItem<String>(
|
}
|
||||||
onTap: () async {
|
}
|
||||||
|
|
||||||
|
class MorePanel extends StatelessWidget {
|
||||||
|
final dynamic videoItem;
|
||||||
|
const MorePanel({super.key, required this.videoItem});
|
||||||
|
|
||||||
|
Future<dynamic> menuActionHandler(String type) async {
|
||||||
|
switch (type) {
|
||||||
|
case 'block':
|
||||||
|
blockUser();
|
||||||
|
break;
|
||||||
|
case 'watchLater':
|
||||||
|
var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String);
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
Get.back();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockUser() async {
|
||||||
SmartDialog.show(
|
SmartDialog.show(
|
||||||
useSystem: true,
|
useSystem: true,
|
||||||
animationType:
|
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||||
SmartAnimationType.centerFade_otherSlide,
|
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(
|
content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?'
|
||||||
'确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?'
|
|
||||||
'\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'),
|
'\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => SmartDialog.dismiss(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'点错了',
|
'点错了',
|
||||||
style: TextStyle(
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
@ -349,9 +367,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
reSrc: 11,
|
reSrc: 11,
|
||||||
);
|
);
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
SmartDialog.showToast(res['code'] == 0
|
SmartDialog.showToast(res['msg'] ?? '成功');
|
||||||
? '成功'
|
|
||||||
: res['msg']);
|
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
)
|
)
|
||||||
@ -359,26 +375,58 @@ class VideoContent extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
value: 'pause',
|
|
||||||
height: 40,
|
@override
|
||||||
child: Row(
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.block, size: 16),
|
InkWell(
|
||||||
const SizedBox(width: 6),
|
onTap: () => Get.back(),
|
||||||
Text('拉黑:${videoItem.owner.name}',
|
child: Container(
|
||||||
style: const TextStyle(fontSize: 13))
|
height: 35,
|
||||||
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 32,
|
||||||
|
height: 3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () async => await menuActionHandler('block'),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
leading: const Icon(Icons.block, size: 19),
|
||||||
|
title: Text(
|
||||||
|
'拉黑up主 「${videoItem.owner.name}」',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () async => await menuActionHandler('watchLater'),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
leading: const Icon(Icons.watch_later_outlined, size: 19),
|
||||||
|
title:
|
||||||
|
Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () =>
|
||||||
|
imageSaveDialog(context, videoItem, SmartDialog.dismiss),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
leading: const Icon(Icons.photo_outlined, size: 19),
|
||||||
|
title:
|
||||||
|
Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,15 +2,14 @@ import 'package:flutter/material.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/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import '../../models/model_rec_video_item.dart';
|
import '../../models/model_rec_video_item.dart';
|
||||||
import 'overlay_pop.dart';
|
|
||||||
import 'stat/danmu.dart';
|
import 'stat/danmu.dart';
|
||||||
import 'stat/view.dart';
|
import 'stat/view.dart';
|
||||||
import '../../http/dynamics.dart';
|
import '../../http/dynamics.dart';
|
||||||
import '../../http/search.dart';
|
|
||||||
import '../../http/user.dart';
|
import '../../http/user.dart';
|
||||||
import '../../http/video.dart';
|
import '../../http/video.dart';
|
||||||
import '../../models/common/search_type.dart';
|
|
||||||
import '../../utils/id_utils.dart';
|
import '../../utils/id_utils.dart';
|
||||||
import '../../utils/utils.dart';
|
import '../../utils/utils.dart';
|
||||||
import '../constants.dart';
|
import '../constants.dart';
|
||||||
@ -42,23 +41,11 @@ class VideoCardV extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int epId = videoItem.param;
|
int epId = videoItem.param;
|
||||||
SmartDialog.showLoading(msg: '资源获取中');
|
RoutePush.bangumiPush(
|
||||||
var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId);
|
null,
|
||||||
if (result['status']) {
|
epId,
|
||||||
var bangumiDetail = result['data'];
|
heroTag: heroTag,
|
||||||
int cid = bangumiDetail.episodes!.first.cid;
|
|
||||||
String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid);
|
|
||||||
SmartDialog.dismiss().then(
|
|
||||||
(value) => Get.toNamed(
|
|
||||||
'/video?bvid=$bvid&cid=$cid&epId=$epId',
|
|
||||||
arguments: {
|
|
||||||
'pic': videoItem.pic,
|
|
||||||
'heroTag': heroTag,
|
|
||||||
'videoType': SearchType.media_bangumi,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'av':
|
case 'av':
|
||||||
String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid);
|
String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid);
|
||||||
@ -127,14 +114,11 @@ class VideoCardV extends StatelessWidget {
|
|||||||
String heroTag = Utils.makeHeroTag(videoItem.id);
|
String heroTag = Utils.makeHeroTag(videoItem.id);
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () async => onPushDetail(heroTag),
|
onTap: () async => onPushDetail(heroTag),
|
||||||
onLongPress: () {
|
onLongPress: () => imageSaveDialog(
|
||||||
SmartDialog.show(
|
context,
|
||||||
builder: (context) => OverlayPop(
|
videoItem,
|
||||||
videoItem: videoItem,
|
SmartDialog.dismiss,
|
||||||
closeFn: () => SmartDialog.dismiss(),
|
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -249,6 +233,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
feedBack();
|
feedBack();
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@ -402,6 +387,15 @@ class MorePanel extends StatelessWidget {
|
|||||||
title:
|
title:
|
||||||
Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall),
|
Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () =>
|
||||||
|
imageSaveDialog(context, videoItem, SmartDialog.dismiss),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
leading: const Icon(Icons.photo_outlined, size: 19),
|
||||||
|
title:
|
||||||
|
Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -520,4 +520,7 @@ class Api {
|
|||||||
|
|
||||||
/// 删除收藏夹
|
/// 删除收藏夹
|
||||||
static const String delFavFolder = '/x/v3/fav/folder/del';
|
static const String delFavFolder = '/x/v3/fav/folder/del';
|
||||||
|
|
||||||
|
/// 搜索结果计数
|
||||||
|
static const String searchCount = '/x/web-interface/wbi/search/all/v2';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ class DynamicsHttp {
|
|||||||
'status': false,
|
'status': false,
|
||||||
'data': [],
|
'data': [],
|
||||||
'msg': res.data['message'],
|
'msg': res.data['message'],
|
||||||
|
'code': res.data['code'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ class ApiInterceptor extends Interceptor {
|
|||||||
// 处理网络请求错误
|
// 处理网络请求错误
|
||||||
// handler.next(err);
|
// handler.next(err);
|
||||||
String url = err.requestOptions.uri.toString();
|
String url = err.requestOptions.uri.toString();
|
||||||
if (!url.contains('heartBeat')) {
|
if (!url.contains('heartbeat')) {
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast(
|
||||||
await dioError(err),
|
await dioError(err),
|
||||||
displayType: SmartToastType.onlyRefresh,
|
displayType: SmartToastType.onlyRefresh,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/models/search/all.dart';
|
||||||
|
import 'package:pilipala/utils/wbi_sign.dart';
|
||||||
import '../models/bangumi/info.dart';
|
import '../models/bangumi/info.dart';
|
||||||
import '../models/common/search_type.dart';
|
import '../models/common/search_type.dart';
|
||||||
import '../models/search/hot.dart';
|
import '../models/search/hot.dart';
|
||||||
@ -163,4 +165,42 @@ class SearchHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> ab2cWithPic(
|
||||||
|
{int? aid, String? bvid}) async {
|
||||||
|
Map<String, dynamic> data = {};
|
||||||
|
if (aid != null) {
|
||||||
|
data['aid'] = aid;
|
||||||
|
} else if (bvid != null) {
|
||||||
|
data['bvid'] = bvid;
|
||||||
|
}
|
||||||
|
final dynamic res =
|
||||||
|
await Request().get(Api.ab2c, data: <String, dynamic>{...data});
|
||||||
|
return {
|
||||||
|
'cid': res.data['data'].first['cid'],
|
||||||
|
'pic': res.data['data'].first['first_frame'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> searchCount(
|
||||||
|
{required String keyword}) async {
|
||||||
|
Map<String, dynamic> data = {
|
||||||
|
'keyword': keyword,
|
||||||
|
'web_location': 333.999,
|
||||||
|
};
|
||||||
|
Map params = await WbiSign().makSign(data);
|
||||||
|
final dynamic res = await Request().get(Api.searchCount, data: params);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': SearchAllModel.fromJson(res.data['data']),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': '请求错误 🙅',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,8 @@ class UserHttp {
|
|||||||
return {
|
return {
|
||||||
'status': false,
|
'status': false,
|
||||||
'data': [],
|
'data': [],
|
||||||
'msg': res.data['message'] ?? '账号未登录'
|
'msg': res.data['message'],
|
||||||
|
'code': res.data['code'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +112,12 @@ class UserHttp {
|
|||||||
'data': {'list': list, 'count': res.data['data']['count']}
|
'data': {'list': list, 'count': res.data['data']['count']}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
'code': res.data['code'],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +132,12 @@ class UserHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': HistoryData.fromJson(res.data['data'])};
|
return {'status': true, 'data': HistoryData.fromJson(res.data['data'])};
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
'code': res.data['code'],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +337,12 @@ class UserHttp {
|
|||||||
'data': SubFolderModelData.fromJson(res.data['data'])
|
'data': SubFolderModelData.fromJson(res.data['data'])
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'msg': res.data['message']};
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
'code': res.data['code'],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
@ -67,9 +68,8 @@ void main() async {
|
|||||||
|
|
||||||
// 小白条、导航栏沉浸
|
// 小白条、导航栏沉浸
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
List<String> versionParts = Platform.version.split('.');
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
int androidVersion = int.parse(versionParts[0]);
|
if (androidInfo.version.sdkInt >= 29) {
|
||||||
if (androidVersion >= 29) {
|
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
}
|
}
|
||||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class BangumiListItemModel {
|
|||||||
BangumiListItemModel({
|
BangumiListItemModel({
|
||||||
this.badge,
|
this.badge,
|
||||||
this.badgeType,
|
this.badgeType,
|
||||||
|
this.pic,
|
||||||
this.cover,
|
this.cover,
|
||||||
// this.firstEp,
|
// this.firstEp,
|
||||||
this.indexShow,
|
this.indexShow,
|
||||||
@ -50,6 +51,7 @@ class BangumiListItemModel {
|
|||||||
|
|
||||||
String? badge;
|
String? badge;
|
||||||
int? badgeType;
|
int? badgeType;
|
||||||
|
String? pic;
|
||||||
String? cover;
|
String? cover;
|
||||||
String? indexShow;
|
String? indexShow;
|
||||||
int? isFinish;
|
int? isFinish;
|
||||||
@ -70,6 +72,7 @@ class BangumiListItemModel {
|
|||||||
BangumiListItemModel.fromJson(Map<String, dynamic> json) {
|
BangumiListItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
badge = json['badge'] == '' ? null : json['badge'];
|
badge = json['badge'] == '' ? null : json['badge'];
|
||||||
badgeType = json['badge_type'];
|
badgeType = json['badge_type'];
|
||||||
|
pic = json['cover'];
|
||||||
cover = json['cover'];
|
cover = json['cover'];
|
||||||
indexShow = json['index_show'];
|
indexShow = json['index_show'];
|
||||||
isFinish = json['is_finish'];
|
isFinish = json['is_finish'];
|
||||||
|
|||||||
@ -415,6 +415,7 @@ class DynamicMajorModel {
|
|||||||
this.type,
|
this.type,
|
||||||
this.courses,
|
this.courses,
|
||||||
this.common,
|
this.common,
|
||||||
|
this.music,
|
||||||
});
|
});
|
||||||
|
|
||||||
DynamicArchiveModel? archive;
|
DynamicArchiveModel? archive;
|
||||||
@ -431,6 +432,7 @@ class DynamicMajorModel {
|
|||||||
String? type;
|
String? type;
|
||||||
Map? courses;
|
Map? courses;
|
||||||
Map? common;
|
Map? common;
|
||||||
|
Map? music;
|
||||||
|
|
||||||
DynamicMajorModel.fromJson(Map<String, dynamic> json) {
|
DynamicMajorModel.fromJson(Map<String, dynamic> json) {
|
||||||
archive = json['archive'] != null
|
archive = json['archive'] != null
|
||||||
@ -455,6 +457,7 @@ class DynamicMajorModel {
|
|||||||
type = json['type'];
|
type = json['type'];
|
||||||
courses = json['courses'] ?? {};
|
courses = json['courses'] ?? {};
|
||||||
common = json['common'] ?? {};
|
common = json['common'] ?? {};
|
||||||
|
music = json['music'] ?? {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
lib/models/search/all.dart
Normal file
9
lib/models/search/all.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class SearchAllModel {
|
||||||
|
SearchAllModel({this.topTList});
|
||||||
|
|
||||||
|
Map? topTList;
|
||||||
|
|
||||||
|
SearchAllModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
topTList = json['top_tlist'];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ class SearchVideoItemModel {
|
|||||||
this.aid,
|
this.aid,
|
||||||
this.bvid,
|
this.bvid,
|
||||||
this.title,
|
this.title,
|
||||||
|
this.titleList,
|
||||||
this.description,
|
this.description,
|
||||||
this.pic,
|
this.pic,
|
||||||
// this.play,
|
// this.play,
|
||||||
@ -54,8 +55,8 @@ class SearchVideoItemModel {
|
|||||||
String? arcurl;
|
String? arcurl;
|
||||||
int? aid;
|
int? aid;
|
||||||
String? bvid;
|
String? bvid;
|
||||||
List? title;
|
String? title;
|
||||||
// List? titleList;
|
List? titleList;
|
||||||
String? description;
|
String? description;
|
||||||
String? pic;
|
String? pic;
|
||||||
// String? play;
|
// String? play;
|
||||||
@ -82,8 +83,9 @@ class SearchVideoItemModel {
|
|||||||
aid = json['aid'];
|
aid = json['aid'];
|
||||||
bvid = json['bvid'];
|
bvid = json['bvid'];
|
||||||
mid = json['mid'];
|
mid = json['mid'];
|
||||||
// title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
|
title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
|
||||||
title = Em.regTitle(json['title']);
|
// title = Em.regTitle(json['title']);
|
||||||
|
titleList = Em.regTitle(json['title']);
|
||||||
description = json['description'];
|
description = json['description'];
|
||||||
pic = json['pic'] != null && json['pic'].startsWith('//')
|
pic = json['pic'] != null && json['pic'].startsWith('//')
|
||||||
? 'https:${json['pic']}'
|
? 'https:${json['pic']}'
|
||||||
@ -232,6 +234,7 @@ class SearchLiveItemModel {
|
|||||||
this.userCover,
|
this.userCover,
|
||||||
this.type,
|
this.type,
|
||||||
this.title,
|
this.title,
|
||||||
|
this.titleList,
|
||||||
this.cover,
|
this.cover,
|
||||||
this.pic,
|
this.pic,
|
||||||
this.online,
|
this.online,
|
||||||
@ -251,7 +254,8 @@ class SearchLiveItemModel {
|
|||||||
String? face;
|
String? face;
|
||||||
String? userCover;
|
String? userCover;
|
||||||
String? type;
|
String? type;
|
||||||
List? title;
|
String? title;
|
||||||
|
List? titleList;
|
||||||
String? cover;
|
String? cover;
|
||||||
String? pic;
|
String? pic;
|
||||||
int? online;
|
int? online;
|
||||||
@ -272,7 +276,8 @@ class SearchLiveItemModel {
|
|||||||
face = json['uface'];
|
face = json['uface'];
|
||||||
userCover = json['user_cover'];
|
userCover = json['user_cover'];
|
||||||
type = json['type'];
|
type = json['type'];
|
||||||
title = Em.regTitle(json['title']);
|
title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
|
||||||
|
titleList = Em.regTitle(json['title']);
|
||||||
cover = json['cover'];
|
cover = json['cover'];
|
||||||
pic = json['cover'];
|
pic = json['cover'];
|
||||||
online = json['online'];
|
online = json['online'];
|
||||||
@ -302,6 +307,7 @@ class SearchMBangumiItemModel {
|
|||||||
this.type,
|
this.type,
|
||||||
this.mediaId,
|
this.mediaId,
|
||||||
this.title,
|
this.title,
|
||||||
|
this.titleList,
|
||||||
this.orgTitle,
|
this.orgTitle,
|
||||||
this.mediaType,
|
this.mediaType,
|
||||||
this.cv,
|
this.cv,
|
||||||
@ -328,7 +334,8 @@ class SearchMBangumiItemModel {
|
|||||||
|
|
||||||
String? type;
|
String? type;
|
||||||
int? mediaId;
|
int? mediaId;
|
||||||
List? title;
|
String? title;
|
||||||
|
List? titleList;
|
||||||
String? orgTitle;
|
String? orgTitle;
|
||||||
int? mediaType;
|
int? mediaType;
|
||||||
String? cv;
|
String? cv;
|
||||||
@ -355,7 +362,8 @@ class SearchMBangumiItemModel {
|
|||||||
SearchMBangumiItemModel.fromJson(Map<String, dynamic> json) {
|
SearchMBangumiItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
type = json['type'];
|
type = json['type'];
|
||||||
mediaId = json['media_id'];
|
mediaId = json['media_id'];
|
||||||
title = Em.regTitle(json['title']);
|
title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
|
||||||
|
titleList = Em.regTitle(json['title']);
|
||||||
orgTitle = json['org_title'];
|
orgTitle = json['org_title'];
|
||||||
mediaType = json['media_type'];
|
mediaType = json['media_type'];
|
||||||
cv = json['cv'];
|
cv = json['cv'];
|
||||||
|
|||||||
@ -218,7 +218,7 @@ class AboutController extends GetxController {
|
|||||||
RxString currentVersion = ''.obs;
|
RxString currentVersion = ''.obs;
|
||||||
RxString remoteVersion = ''.obs;
|
RxString remoteVersion = ''.obs;
|
||||||
late LatestDataModel remoteAppInfo;
|
late LatestDataModel remoteAppInfo;
|
||||||
RxBool isUpdate = true.obs;
|
RxBool isUpdate = false.obs;
|
||||||
RxBool isLoading = true.obs;
|
RxBool isLoading = true.obs;
|
||||||
late LatestDataModel data;
|
late LatestDataModel data;
|
||||||
|
|
||||||
|
|||||||
@ -194,7 +194,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
src: widget.bangumiDetail!.cover!,
|
src: widget.bangumiDetail!.cover!,
|
||||||
),
|
),
|
||||||
PBadge(
|
PBadge(
|
||||||
text: '评分 ${widget.bangumiDetail!.rating!['score']!}',
|
text:
|
||||||
|
'评分 ${widget.bangumiDetail?.rating?['score']! ?? '暂无'}',
|
||||||
top: null,
|
top: null,
|
||||||
right: 6,
|
right: 6,
|
||||||
bottom: 6,
|
bottom: 6,
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/models/bangumi/list.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
@ -14,68 +13,28 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
const BangumiCardV({
|
const BangumiCardV({
|
||||||
super.key,
|
super.key,
|
||||||
required this.bangumiItem,
|
required this.bangumiItem,
|
||||||
this.longPress,
|
|
||||||
this.longPressEnd,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final bangumiItem;
|
final BangumiListItemModel bangumiItem;
|
||||||
final Function()? longPress;
|
|
||||||
final Function()? longPressEnd;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(bangumiItem.mediaId);
|
String heroTag = Utils.makeHeroTag(bangumiItem.mediaId);
|
||||||
return Card(
|
return InkWell(
|
||||||
elevation: 0,
|
onTap: () {
|
||||||
clipBehavior: Clip.hardEdge,
|
RoutePush.bangumiPush(
|
||||||
margin: EdgeInsets.zero,
|
bangumiItem.seasonId,
|
||||||
child: GestureDetector(
|
null,
|
||||||
// onLongPress: () {
|
heroTag: heroTag,
|
||||||
// if (longPress != null) {
|
|
||||||
// longPress!();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// onLongPressEnd: (details) {
|
|
||||||
// if (longPressEnd != null) {
|
|
||||||
// longPressEnd!();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () async {
|
|
||||||
final int seasonId = bangumiItem.seasonId;
|
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
|
||||||
final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
|
|
||||||
SmartDialog.dismiss().then((value) {
|
|
||||||
if (res['status']) {
|
|
||||||
if (res['data'].episodes.isEmpty) {
|
|
||||||
SmartDialog.showToast('资源加载失败');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
EpisodeItem episode = res['data'].episodes.first;
|
|
||||||
String bvid = episode.bvid!;
|
|
||||||
int cid = episode.cid!;
|
|
||||||
String pic = episode.cover!;
|
|
||||||
String heroTag = Utils.makeHeroTag(cid);
|
|
||||||
Get.toNamed(
|
|
||||||
'/video?bvid=$bvid&cid=$cid&seasonId=$seasonId',
|
|
||||||
arguments: {
|
|
||||||
'pic': pic,
|
|
||||||
'heroTag': heroTag,
|
|
||||||
'videoType': SearchType.media_bangumi,
|
|
||||||
'bangumiItem': res['data'],
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
onLongPress: () =>
|
||||||
|
imageSaveDialog(context, bangumiItem, SmartDialog.dismiss),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.all(
|
||||||
topLeft: StyleString.imgRadius,
|
StyleString.imgRadius,
|
||||||
topRight: StyleString.imgRadius,
|
|
||||||
bottomLeft: StyleString.imgRadius,
|
|
||||||
bottomRight: StyleString.imgRadius,
|
|
||||||
),
|
),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 0.65,
|
aspectRatio: 0.65,
|
||||||
@ -116,8 +75,6 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
BangumiContent(bangumiItem: bangumiItem)
|
BangumiContent(bangumiItem: bangumiItem)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import 'package:pilipala/models/dynamics/up.dart';
|
|||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ class DynamicsController extends GetxController {
|
|||||||
|
|
||||||
Future queryFollowDynamic({type = 'init'}) async {
|
Future queryFollowDynamic({type = 'init'}) async {
|
||||||
if (!userLogin.value) {
|
if (!userLogin.value) {
|
||||||
return {'status': false, 'msg': '账号未登录'};
|
return {'status': false, 'msg': '账号未登录', 'code': -101};
|
||||||
}
|
}
|
||||||
if (type == 'init') {
|
if (type == 'init') {
|
||||||
dynamicsList.clear();
|
dynamicsList.clear();
|
||||||
@ -220,25 +221,7 @@ class DynamicsController extends GetxController {
|
|||||||
print('DYNAMIC_TYPE_PGC_UNION 番剧');
|
print('DYNAMIC_TYPE_PGC_UNION 番剧');
|
||||||
DynamicArchiveModel pgc = item.modules.moduleDynamic.major.pgc;
|
DynamicArchiveModel pgc = item.modules.moduleDynamic.major.pgc;
|
||||||
if (pgc.epid != null) {
|
if (pgc.epid != null) {
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
RoutePush.bangumiPush(null, pgc.epid);
|
||||||
var res = await SearchHttp.bangumiInfo(epId: pgc.epid);
|
|
||||||
SmartDialog.dismiss();
|
|
||||||
if (res['status']) {
|
|
||||||
EpisodeItem episode = res['data'].episodes.first;
|
|
||||||
String bvid = episode.bvid!;
|
|
||||||
int cid = episode.cid!;
|
|
||||||
String pic = episode.cover!;
|
|
||||||
String heroTag = Utils.makeHeroTag(cid);
|
|
||||||
Get.toNamed(
|
|
||||||
'/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}',
|
|
||||||
arguments: {
|
|
||||||
'pic': pic,
|
|
||||||
'heroTag': heroTag,
|
|
||||||
'videoType': SearchType.media_bangumi,
|
|
||||||
'bangumiItem': res['data'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -246,7 +229,7 @@ class DynamicsController extends GetxController {
|
|||||||
|
|
||||||
Future queryFollowUp({type = 'init'}) async {
|
Future queryFollowUp({type = 'init'}) async {
|
||||||
if (!userLogin.value) {
|
if (!userLogin.value) {
|
||||||
return {'status': false, 'msg': '账号未登录'};
|
return {'status': false, 'msg': '账号未登录', 'code': -101};
|
||||||
}
|
}
|
||||||
if (type == 'init') {
|
if (type == 'init') {
|
||||||
upData.value.upList = <UpItem>[];
|
upData.value.upList = <UpItem>[];
|
||||||
|
|||||||
@ -196,7 +196,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: StreamBuilder(
|
title: StreamBuilder(
|
||||||
stream: titleStreamC.stream.distinct(),
|
stream: titleStreamC.stream,
|
||||||
initialData: false,
|
initialData: false,
|
||||||
builder: (context, AsyncSnapshot snapshot) {
|
builder: (context, AsyncSnapshot snapshot) {
|
||||||
return AnimatedOpacity(
|
return AnimatedOpacity(
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import 'package:pilipala/common/widgets/no_data.dart';
|
|||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/main_stream.dart';
|
import 'package:pilipala/utils/main_stream.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
import '../mine/controller.dart';
|
import '../mine/controller.dart';
|
||||||
@ -224,8 +225,8 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
}
|
}
|
||||||
Map data = snapshot.data;
|
Map? data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data != null && data['status']) {
|
||||||
List<DynamicItemModel> list =
|
List<DynamicItemModel> list =
|
||||||
_dynamicsController.dynamicsList;
|
_dynamicsController.dynamicsList;
|
||||||
return Obx(
|
return Obx(
|
||||||
@ -248,24 +249,21 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (data['msg'] == "账号未登录") {
|
|
||||||
return HttpError(
|
|
||||||
errMsg: data['msg'],
|
|
||||||
btnText: "去登录",
|
|
||||||
fn: () {
|
|
||||||
mineController.onLogin();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
|
btnText: data?['code'] == -101 ? '去登录' : null,
|
||||||
fn: () {
|
fn: () {
|
||||||
|
if (data?['code'] == -101) {
|
||||||
|
RoutePush.loginRedirectPush();
|
||||||
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_futureBuilderFuture =
|
_futureBuilderFuture =
|
||||||
_dynamicsController.queryFollowDynamic();
|
_dynamicsController.queryFollowDynamic();
|
||||||
_futureBuilderFutureUp =
|
_futureBuilderFutureUp =
|
||||||
_dynamicsController.queryFollowUp();
|
_dynamicsController.queryFollowUp();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
import 'package:pilipala/pages/dynamics/index.dart';
|
||||||
import '../../../models/dynamics/result.dart';
|
|
||||||
import 'action_panel.dart';
|
import 'action_panel.dart';
|
||||||
import 'author_panel.dart';
|
import 'author_panel.dart';
|
||||||
import 'content_panel.dart';
|
import 'content_panel.dart';
|
||||||
import 'forward_panel.dart';
|
import 'forward_panel.dart';
|
||||||
|
|
||||||
class DynamicPanel extends StatelessWidget {
|
class DynamicPanel extends StatelessWidget {
|
||||||
final DynamicItemModel item;
|
final dynamic item;
|
||||||
final String? source;
|
final String? source;
|
||||||
DynamicPanel({required this.item, this.source, Key? key}) : super(key: key);
|
DynamicPanel({required this.item, this.source, Key? key}) : super(key: key);
|
||||||
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
||||||
|
|||||||
@ -238,6 +238,61 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
case 'DYNAMIC_TYPE_MUSIC':
|
||||||
|
final Map music = item.modules.moduleDynamic.major.music;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed('/webview', parameters: {
|
||||||
|
'url': "https:${music['jump_url']}",
|
||||||
|
'type': 'url',
|
||||||
|
'pageTitle': music['title']
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10),
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: 45,
|
||||||
|
height: 45,
|
||||||
|
src: music['cover'],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
music['title'],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
music['label'],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// TextButton(onPressed: () {}, child: Text('123'))
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox(
|
return const SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|||||||
@ -80,15 +80,12 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
|||||||
double width = box.maxWidth;
|
double width = box.maxWidth;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
NetworkImgLayer(
|
||||||
tag: content.bvid,
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
type: floor == 1 ? 'emote' : null,
|
type: floor == 1 ? 'emote' : null,
|
||||||
width: width,
|
width: width,
|
||||||
height: width / StyleString.aspectRatio,
|
height: width / StyleString.aspectRatio,
|
||||||
src: content.cover,
|
src: content.cover,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (content.badge != null && type == 'pgc')
|
if (content.badge != null && type == 'pgc')
|
||||||
PBadge(
|
PBadge(
|
||||||
text: content.badge['text'],
|
text: content.badge['text'],
|
||||||
|
|||||||
@ -17,10 +17,15 @@ class FavController extends GetxController {
|
|||||||
int pageSize = 60;
|
int pageSize = 60;
|
||||||
RxBool hasMore = true.obs;
|
RxBool hasMore = true.obs;
|
||||||
|
|
||||||
Future<dynamic> queryFavFolder({type = 'init'}) async {
|
@override
|
||||||
|
void onInit() {
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> queryFavFolder({type = 'init'}) async {
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
return {'status': false, 'msg': '账号未登录'};
|
return {'status': false, 'msg': '账号未登录', 'code': -101};
|
||||||
}
|
}
|
||||||
if (!hasMore.value) {
|
if (!hasMore.value) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/pages/fav/index.dart';
|
import 'package:pilipala/pages/fav/index.dart';
|
||||||
import 'package:pilipala/pages/fav/widgets/item.dart';
|
import 'package:pilipala/pages/fav/widgets/item.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
|
|
||||||
class FavPage extends StatefulWidget {
|
class FavPage extends StatefulWidget {
|
||||||
const FavPage({super.key});
|
const FavPage({super.key});
|
||||||
@ -57,8 +58,8 @@ class _FavPageState extends State<FavPage> {
|
|||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data as Map;
|
Map? data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data != null && data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => ListView.builder(
|
() => ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
@ -74,8 +75,18 @@ class _FavPageState extends State<FavPage> {
|
|||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
HttpError(
|
HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
fn: () => setState(() {}),
|
btnText: data?['code'] == -101 ? '去登录' : null,
|
||||||
|
fn: () {
|
||||||
|
if (data?['code'] == -101) {
|
||||||
|
RoutePush.loginRedirectPush();
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_favController.queryFavFolder();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
@ -7,6 +8,7 @@ import 'package:pilipala/http/search.dart';
|
|||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import '../../../common/widgets/badge.dart';
|
import '../../../common/widgets/badge.dart';
|
||||||
@ -61,6 +63,11 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
epId != null ? SearchType.media_bangumi : SearchType.video,
|
epId != null ? SearchType.media_bangumi : SearchType.video,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context,
|
||||||
|
videoItem,
|
||||||
|
SmartDialog.dismiss,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/models/user/history.dart';
|
import 'package:pilipala/models/user/history.dart';
|
||||||
|
import 'package:pilipala/models/user/info.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class HistoryController extends GetxController {
|
class HistoryController extends GetxController {
|
||||||
@ -15,14 +16,20 @@ class HistoryController extends GetxController {
|
|||||||
RxBool isLoading = false.obs;
|
RxBool isLoading = false.obs;
|
||||||
RxBool enableMultiple = false.obs;
|
RxBool enableMultiple = false.obs;
|
||||||
RxInt checkedCount = 0.obs;
|
RxInt checkedCount = 0.obs;
|
||||||
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
|
UserInfoData? userInfo;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
historyStatus();
|
historyStatus();
|
||||||
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future queryHistoryList({type = 'init'}) async {
|
Future queryHistoryList({type = 'init'}) async {
|
||||||
|
if (userInfo == null) {
|
||||||
|
return {'status': false, 'msg': '账号未登录', 'code': -101};
|
||||||
|
}
|
||||||
int max = 0;
|
int max = 0;
|
||||||
int viewAt = 0;
|
int viewAt = 0;
|
||||||
if (type == 'onload') {
|
if (type == 'onload') {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:pilipala/common/skeleton/video_card_h.dart';
|
|||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
import 'package:pilipala/pages/history/index.dart';
|
import 'package:pilipala/pages/history/index.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
|
|
||||||
import 'widgets/item.dart';
|
import 'widgets/item.dart';
|
||||||
|
|
||||||
@ -183,8 +184,8 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
}
|
}
|
||||||
Map data = snapshot.data;
|
Map? data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data != null && data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => _historyController.historyList.isNotEmpty
|
() => _historyController.historyList.isNotEmpty
|
||||||
? SliverList(
|
? SliverList(
|
||||||
@ -209,8 +210,18 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
fn: () => setState(() {}),
|
btnText: data?['code'] == -101 ? '去登录' : null,
|
||||||
|
fn: () {
|
||||||
|
if (data?['code'] == -101) {
|
||||||
|
RoutePush.loginRedirectPush();
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_historyController.queryHistoryList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
|
||||||
import 'package:pilipala/models/common/business_type.dart';
|
import 'package:pilipala/models/common/business_type.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
import 'package:pilipala/pages/history_search/index.dart';
|
import 'package:pilipala/pages/history_search/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class HistoryItem extends StatelessWidget {
|
class HistoryItem extends StatelessWidget {
|
||||||
@ -101,28 +101,13 @@ class HistoryItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (videoItem.history.epid != '') {
|
if (videoItem.history.epid != '') {
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
RoutePush.bangumiPush(
|
||||||
var res =
|
null,
|
||||||
await SearchHttp.bangumiInfo(epId: videoItem.history.epid);
|
videoItem.history.epid,
|
||||||
SmartDialog.dismiss();
|
heroTag: heroTag,
|
||||||
if (res['status']) {
|
|
||||||
EpisodeItem episode = res['data'].episodes.first;
|
|
||||||
String bvid = episode.bvid!;
|
|
||||||
int cid = episode.cid!;
|
|
||||||
String pic = episode.cover!;
|
|
||||||
String heroTag = Utils.makeHeroTag(cid);
|
|
||||||
Get.toNamed(
|
|
||||||
'/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}',
|
|
||||||
arguments: {
|
|
||||||
'pic': pic,
|
|
||||||
'heroTag': heroTag,
|
|
||||||
'videoType': SearchType.media_bangumi,
|
|
||||||
'bangumiItem': res['data'],
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
int cid = videoItem.history.cid ??
|
int cid = videoItem.history.cid ??
|
||||||
// videoItem.history.oid ??
|
// videoItem.history.oid ??
|
||||||
@ -213,7 +198,8 @@ class HistoryItem extends StatelessWidget {
|
|||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(
|
||||||
|
StyleString.imgRadius.x),
|
||||||
color: Colors.black.withOpacity(
|
color: Colors.black.withOpacity(
|
||||||
ctr!.enableMultiple.value &&
|
ctr!.enableMultiple.value &&
|
||||||
videoItem.checked
|
videoItem.checked
|
||||||
|
|||||||
@ -3,8 +3,6 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/animated_dialog.dart';
|
|
||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
|
||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
@ -78,15 +76,6 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: _hotController.videoList[index],
|
videoItem: _hotController.videoList[index],
|
||||||
showPubdate: true,
|
showPubdate: true,
|
||||||
longPress: () {
|
|
||||||
_hotController.popupDialog = _createPopupDialog(
|
|
||||||
_hotController.videoList[index]);
|
|
||||||
Overlay.of(context)
|
|
||||||
.insert(_hotController.popupDialog!);
|
|
||||||
},
|
|
||||||
longPressEnd: () {
|
|
||||||
_hotController.popupDialog?.remove();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}, childCount: _hotController.videoList.length),
|
}, childCount: _hotController.videoList.length),
|
||||||
),
|
),
|
||||||
@ -122,14 +111,4 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createPopupDialog(videoItem) {
|
|
||||||
return OverlayEntry(
|
|
||||||
builder: (context) => AnimatedDialog(
|
|
||||||
closeFn: _hotController.popupDialog?.remove,
|
|
||||||
child: OverlayPop(
|
|
||||||
videoItem: videoItem, closeFn: _hotController.popupDialog?.remove),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,30 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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:hive/hive.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import 'package:pilipala/models/model_hot_video_item.dart';
|
||||||
|
import 'package:pilipala/models/user/info.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class LaterController extends GetxController {
|
class LaterController extends GetxController {
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
RxList<HotVideoItemModel> laterList = <HotVideoItemModel>[].obs;
|
RxList<HotVideoItemModel> laterList = <HotVideoItemModel>[].obs;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
RxBool isLoading = false.obs;
|
RxBool isLoading = false.obs;
|
||||||
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
|
UserInfoData? userInfo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
|
}
|
||||||
|
|
||||||
Future queryLaterList() async {
|
Future queryLaterList() async {
|
||||||
|
if (userInfo == null) {
|
||||||
|
return {'status': false, 'msg': '账号未登录', 'code': -101};
|
||||||
|
}
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
var res = await UserHttp.seeYouLater();
|
var res = await UserHttp.seeYouLater();
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
|||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
import 'package:pilipala/pages/later/index.dart';
|
import 'package:pilipala/pages/later/index.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
|
|
||||||
class LaterPage extends StatefulWidget {
|
class LaterPage extends StatefulWidget {
|
||||||
const LaterPage({super.key});
|
const LaterPage({super.key});
|
||||||
@ -72,8 +73,8 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data as Map;
|
Map? data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data != null && data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => _laterController.laterList.isNotEmpty &&
|
() => _laterController.laterList.isNotEmpty &&
|
||||||
!_laterController.isLoading.value
|
!_laterController.isLoading.value
|
||||||
@ -84,7 +85,7 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
source: 'later',
|
source: 'later',
|
||||||
longPress: () => _laterController.toViewDel(
|
onPressedFn: () => _laterController.toViewDel(
|
||||||
aid: videoItem.aid));
|
aid: videoItem.aid));
|
||||||
}, childCount: _laterController.laterList.length),
|
}, childCount: _laterController.laterList.length),
|
||||||
)
|
)
|
||||||
@ -96,10 +97,18 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
fn: () => setState(() {
|
btnText: data?['code'] == -101 ? '去登录' : null,
|
||||||
_futureBuilderFuture = _laterController.queryLaterList();
|
fn: () {
|
||||||
}),
|
if (data?['code'] == -101) {
|
||||||
|
RoutePush.loginRedirectPush();
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_laterController.queryLaterList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -5,9 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/skeleton/video_card_v.dart';
|
import 'package:pilipala/common/skeleton/video_card_v.dart';
|
||||||
import 'package:pilipala/common/widgets/animated_dialog.dart';
|
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
|
||||||
import 'package:pilipala/utils/main_stream.dart';
|
import 'package:pilipala/utils/main_stream.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@ -112,16 +110,6 @@ class _LivePageState extends State<LivePage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createPopupDialog(liveItem) {
|
|
||||||
return OverlayEntry(
|
|
||||||
builder: (context) => AnimatedDialog(
|
|
||||||
closeFn: _liveController.popupDialog?.remove,
|
|
||||||
child: OverlayPop(
|
|
||||||
videoItem: liveItem, closeFn: _liveController.popupDialog?.remove),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget contentGrid(ctr, liveList) {
|
Widget contentGrid(ctr, liveList) {
|
||||||
// double maxWidth = Get.size.width;
|
// double maxWidth = Get.size.width;
|
||||||
// int baseWidth = 500;
|
// int baseWidth = 500;
|
||||||
@ -152,14 +140,6 @@ class _LivePageState extends State<LivePage>
|
|||||||
? LiveCardV(
|
? LiveCardV(
|
||||||
liveItem: liveList[index],
|
liveItem: liveList[index],
|
||||||
crossAxisCount: crossAxisCount,
|
crossAxisCount: crossAxisCount,
|
||||||
longPress: () {
|
|
||||||
_liveController.popupDialog =
|
|
||||||
_createPopupDialog(liveList[index]);
|
|
||||||
Overlay.of(context).insert(_liveController.popupDialog!);
|
|
||||||
},
|
|
||||||
longPressEnd: () {
|
|
||||||
_liveController.popupDialog?.remove();
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
: const VideoCardVSkeleton();
|
: const VideoCardVSkeleton();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
@ -9,36 +11,23 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
class LiveCardV extends StatelessWidget {
|
class LiveCardV extends StatelessWidget {
|
||||||
final LiveItemModel liveItem;
|
final LiveItemModel liveItem;
|
||||||
final int crossAxisCount;
|
final int crossAxisCount;
|
||||||
final Function()? longPress;
|
|
||||||
final Function()? longPressEnd;
|
|
||||||
|
|
||||||
const LiveCardV({
|
const LiveCardV({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.liveItem,
|
required this.liveItem,
|
||||||
required this.crossAxisCount,
|
required this.crossAxisCount,
|
||||||
this.longPress,
|
|
||||||
this.longPressEnd,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(liveItem.roomId);
|
String heroTag = Utils.makeHeroTag(liveItem.roomId);
|
||||||
return Card(
|
return InkWell(
|
||||||
elevation: 0,
|
onLongPress: () => imageSaveDialog(
|
||||||
clipBehavior: Clip.hardEdge,
|
context,
|
||||||
margin: EdgeInsets.zero,
|
liveItem,
|
||||||
child: GestureDetector(
|
SmartDialog.dismiss,
|
||||||
onLongPress: () {
|
),
|
||||||
if (longPress != null) {
|
borderRadius: BorderRadius.circular(16),
|
||||||
longPress!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// onLongPressEnd: (details) {
|
|
||||||
// if (longPressEnd != null) {
|
|
||||||
// longPressEnd!();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}',
|
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}',
|
||||||
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
|
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
|
||||||
@ -83,8 +72,6 @@ class LiveCardV extends StatelessWidget {
|
|||||||
LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
|
LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,7 @@ class LiveRoomController extends GetxController {
|
|||||||
double volume = 0.0;
|
double volume = 0.0;
|
||||||
// 静音状态
|
// 静音状态
|
||||||
RxBool volumeOff = false.obs;
|
RxBool volumeOff = false.obs;
|
||||||
PlPlayerController plPlayerController =
|
PlPlayerController plPlayerController = PlPlayerController(videoType: 'live');
|
||||||
PlPlayerController.getInstance(videoType: 'live');
|
|
||||||
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
||||||
late bool enableCDN;
|
late bool enableCDN;
|
||||||
late int currentQn;
|
late int currentQn;
|
||||||
|
|||||||
@ -153,7 +153,8 @@ class _BottomControlState extends State<BottomControl> {
|
|||||||
size: 20,
|
size: 20,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
fuc: () => widget.controller!.triggerFullScreen(),
|
fuc: () => widget.controller!.triggerFullScreen(
|
||||||
|
status: !(widget.controller!.isFullScreen.value)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -40,10 +40,10 @@ class MainController extends GetxController {
|
|||||||
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(
|
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(
|
||||||
SettingBoxKey.dynamicBadgeMode,
|
SettingBoxKey.dynamicBadgeMode,
|
||||||
defaultValue: DynamicBadgeMode.number.code)];
|
defaultValue: DynamicBadgeMode.number.code)];
|
||||||
|
setNavBarConfig();
|
||||||
if (dynamicBadgeType.value != DynamicBadgeMode.hidden) {
|
if (dynamicBadgeType.value != DynamicBadgeMode.hidden) {
|
||||||
getUnreadDynamic();
|
getUnreadDynamic();
|
||||||
}
|
}
|
||||||
setNavBarConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBackPressed(BuildContext context) {
|
void onBackPressed(BuildContext context) {
|
||||||
|
|||||||
@ -138,14 +138,14 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
offset: Offset(0, snapshot.data ? 0 : 1),
|
offset: Offset(0, snapshot.data ? 0 : 1),
|
||||||
child: GlobalData().enableMYBar
|
child: GlobalData().enableMYBar
|
||||||
? NavigationBar(
|
? Obx(
|
||||||
|
() => NavigationBar(
|
||||||
onDestinationSelected: (value) => setIndex(value),
|
onDestinationSelected: (value) => setIndex(value),
|
||||||
selectedIndex: _mainController.selectedIndex,
|
selectedIndex: _mainController.selectedIndex,
|
||||||
destinations: <Widget>[
|
destinations: <Widget>[
|
||||||
..._mainController.navigationBars.map((e) {
|
..._mainController.navigationBars.map((e) {
|
||||||
return NavigationDestination(
|
return NavigationDestination(
|
||||||
icon: Obx(
|
icon: Badge(
|
||||||
() => Badge(
|
|
||||||
label: _mainController
|
label: _mainController
|
||||||
.dynamicBadgeType.value ==
|
.dynamicBadgeType.value ==
|
||||||
DynamicBadgeMode.number
|
DynamicBadgeMode.number
|
||||||
@ -159,14 +159,15 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
e['count'] > 0,
|
e['count'] > 0,
|
||||||
child: e['icon'],
|
child: e['icon'],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
selectedIcon: e['selectIcon'],
|
selectedIcon: e['selectIcon'],
|
||||||
label: e['label'],
|
label: e['label'],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: BottomNavigationBar(
|
: Obx(
|
||||||
|
() => BottomNavigationBar(
|
||||||
currentIndex: _mainController.selectedIndex,
|
currentIndex: _mainController.selectedIndex,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
onTap: (value) => setIndex(value),
|
onTap: (value) => setIndex(value),
|
||||||
@ -176,8 +177,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
items: [
|
items: [
|
||||||
..._mainController.navigationBars.map((e) {
|
..._mainController.navigationBars.map((e) {
|
||||||
return BottomNavigationBarItem(
|
return BottomNavigationBarItem(
|
||||||
icon: Obx(
|
icon: Badge(
|
||||||
() => Badge(
|
|
||||||
label: _mainController
|
label: _mainController
|
||||||
.dynamicBadgeType.value ==
|
.dynamicBadgeType.value ==
|
||||||
DynamicBadgeMode.number
|
DynamicBadgeMode.number
|
||||||
@ -191,13 +191,13 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
e['count'] > 0,
|
e['count'] > 0,
|
||||||
child: e['icon'],
|
child: e['icon'],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
activeIcon: e['selectIcon'],
|
activeIcon: e['selectIcon'],
|
||||||
label: e['label'],
|
label: e['label'],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class MemberSeasonsItem extends StatelessWidget {
|
class MemberSeasonsItem extends StatelessWidget {
|
||||||
@ -29,6 +31,11 @@ class MemberSeasonsItem extends StatelessWidget {
|
|||||||
Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid',
|
Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid',
|
||||||
arguments: {'videoItem': seasonItem, 'heroTag': heroTag});
|
arguments: {'videoItem': seasonItem, 'heroTag': heroTag});
|
||||||
},
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context,
|
||||||
|
seasonItem,
|
||||||
|
SmartDialog.dismiss,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:pilipala/http/user.dart';
|
|||||||
import 'package:pilipala/models/common/theme_type.dart';
|
import 'package:pilipala/models/common/theme_type.dart';
|
||||||
import 'package:pilipala/models/user/info.dart';
|
import 'package:pilipala/models/user/info.dart';
|
||||||
import 'package:pilipala/models/user/stat.dart';
|
import 'package:pilipala/models/user/stat.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class MineController extends GetxController {
|
class MineController extends GetxController {
|
||||||
@ -33,14 +34,7 @@ class MineController extends GetxController {
|
|||||||
|
|
||||||
onLogin() async {
|
onLogin() async {
|
||||||
if (!userLogin.value) {
|
if (!userLogin.value) {
|
||||||
Get.toNamed(
|
RoutePush.loginPush();
|
||||||
'/webview',
|
|
||||||
parameters: {
|
|
||||||
'url': 'https://passport.bilibili.com/h5-app/passport/login',
|
|
||||||
'type': 'login',
|
|
||||||
'pageTitle': '登录bilibili',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// Get.toNamed('/loginPage');
|
// Get.toNamed('/loginPage');
|
||||||
} else {
|
} else {
|
||||||
int mid = userInfo.value.mid!;
|
int mid = userInfo.value.mid!;
|
||||||
|
|||||||
@ -3,8 +3,6 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/animated_dialog.dart';
|
|
||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
|
||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
@ -82,15 +80,6 @@ class _ZonePageState extends State<ZonePage>
|
|||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: _zoneController.videoList[index],
|
videoItem: _zoneController.videoList[index],
|
||||||
showPubdate: true,
|
showPubdate: true,
|
||||||
longPress: () {
|
|
||||||
_zoneController.popupDialog = _createPopupDialog(
|
|
||||||
_zoneController.videoList[index]);
|
|
||||||
Overlay.of(context)
|
|
||||||
.insert(_zoneController.popupDialog!);
|
|
||||||
},
|
|
||||||
longPressEnd: () {
|
|
||||||
_zoneController.popupDialog?.remove();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}, childCount: _zoneController.videoList.length),
|
}, childCount: _zoneController.videoList.length),
|
||||||
),
|
),
|
||||||
@ -126,14 +115,4 @@ class _ZonePageState extends State<ZonePage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createPopupDialog(videoItem) {
|
|
||||||
return OverlayEntry(
|
|
||||||
builder: (context) => AnimatedDialog(
|
|
||||||
closeFn: _zoneController.popupDialog?.remove,
|
|
||||||
child: OverlayPop(
|
|
||||||
videoItem: videoItem, closeFn: _zoneController.popupDialog?.remove),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
Widget searchLivePanel(BuildContext context, ctr, list) {
|
Widget searchLivePanel(BuildContext context, ctr, list) {
|
||||||
@ -42,15 +44,15 @@ class LiveItem extends StatelessWidget {
|
|||||||
Get.toNamed('/liveRoom?roomid=${liveItem.roomid}',
|
Get.toNamed('/liveRoom?roomid=${liveItem.roomid}',
|
||||||
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
|
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
|
||||||
},
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context,
|
||||||
|
liveItem,
|
||||||
|
SmartDialog.dismiss,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.all(StyleString.imgRadius),
|
||||||
topLeft: StyleString.imgRadius,
|
|
||||||
topRight: StyleString.imgRadius,
|
|
||||||
bottomLeft: StyleString.imgRadius,
|
|
||||||
bottomRight: StyleString.imgRadius,
|
|
||||||
),
|
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
@ -108,7 +110,7 @@ class LiveContent extends StatelessWidget {
|
|||||||
RichText(
|
RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: [
|
children: [
|
||||||
for (var i in liveItem.title) ...[
|
for (var i in liveItem.titleList) ...[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: i['text'],
|
text: i['text'],
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import 'package:pilipala/models/bangumi/info.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
||||||
@ -63,7 +64,7 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurface),
|
color: Theme.of(context).colorScheme.onSurface),
|
||||||
children: [
|
children: [
|
||||||
for (var i in i.title) ...[
|
for (var i in i.titleList) ...[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: i['text'],
|
text: i['text'],
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -108,28 +109,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: 32,
|
height: 32,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: () {
|
||||||
SmartDialog.showLoading(msg: '获取中...');
|
RoutePush.bangumiPush(i.seasonId, null);
|
||||||
var res = await SearchHttp.bangumiInfo(
|
|
||||||
seasonId: i.seasonId);
|
|
||||||
SmartDialog.dismiss().then((value) {
|
|
||||||
if (res['status']) {
|
|
||||||
EpisodeItem episode = res['data'].episodes.first;
|
|
||||||
String bvid = episode.bvid!;
|
|
||||||
int cid = episode.cid!;
|
|
||||||
String pic = episode.cover!;
|
|
||||||
String heroTag = Utils.makeHeroTag(cid);
|
|
||||||
Get.toNamed(
|
|
||||||
'/video?bvid=$bvid&cid=$cid&seasonId=${i.seasonId}',
|
|
||||||
arguments: {
|
|
||||||
'pic': pic,
|
|
||||||
'heroTag': heroTag,
|
|
||||||
'videoType': SearchType.media_bangumi,
|
|
||||||
'bangumiItem': res['data'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
child: const Text('观看'),
|
child: const Text('观看'),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -35,7 +35,11 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
padding: index == 0
|
padding: index == 0
|
||||||
? const EdgeInsets.only(top: 2)
|
? const EdgeInsets.only(top: 2)
|
||||||
: EdgeInsets.zero,
|
: EdgeInsets.zero,
|
||||||
child: VideoCardH(videoItem: i, showPubdate: true),
|
child: VideoCardH(
|
||||||
|
videoItem: i,
|
||||||
|
showPubdate: true,
|
||||||
|
source: 'search',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/search.dart';
|
||||||
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
|
|
||||||
class SearchResultController extends GetxController {
|
class SearchResultController extends GetxController {
|
||||||
String? keyword;
|
String? keyword;
|
||||||
int tabIndex = 0;
|
int tabIndex = 0;
|
||||||
|
RxList searchTabs = [].obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -10,5 +13,21 @@ class SearchResultController extends GetxController {
|
|||||||
if (Get.parameters.keys.isNotEmpty) {
|
if (Get.parameters.keys.isNotEmpty) {
|
||||||
keyword = Get.parameters['keyword'];
|
keyword = Get.parameters['keyword'];
|
||||||
}
|
}
|
||||||
|
searchTabs.value = SearchType.values
|
||||||
|
.map((type) => {'label': type.label, 'id': type.type})
|
||||||
|
.toList();
|
||||||
|
querySearchCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future querySearchCount() async {
|
||||||
|
var result = await SearchHttp.searchCount(keyword: keyword!);
|
||||||
|
if (result['status']) {
|
||||||
|
for (var i in searchTabs) {
|
||||||
|
final count = result['data'].topTList[i['id']];
|
||||||
|
i['count'] = count > 99 ? '99+' : count.toString();
|
||||||
|
}
|
||||||
|
searchTabs.refresh();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class SearchResultPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _SearchResultPageState extends State<SearchResultPage>
|
class _SearchResultPageState extends State<SearchResultPage>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late SearchResultController? _searchResultController;
|
late SearchResultController _searchResultController;
|
||||||
late TabController? _tabController;
|
late TabController? _tabController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -25,7 +25,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
_tabController = TabController(
|
_tabController = TabController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
length: SearchType.values.length,
|
length: SearchType.values.length,
|
||||||
initialIndex: _searchResultController!.tabIndex,
|
initialIndex: _searchResultController.tabIndex,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Text(
|
child: Text(
|
||||||
'${_searchResultController!.keyword}',
|
'${_searchResultController.keyword}',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -64,10 +64,12 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
|
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
|
||||||
highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
||||||
),
|
),
|
||||||
child: TabBar(
|
child: Obx(
|
||||||
|
() => (TabBar(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
tabs: [
|
tabs: [
|
||||||
for (var i in SearchType.values) Tab(text: i.label),
|
for (var i in _searchResultController.searchTabs)
|
||||||
|
Tab(text: "${i['label']} ${i['count'] ?? ''}")
|
||||||
],
|
],
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
indicatorWeight: 0,
|
indicatorWeight: 0,
|
||||||
@ -78,21 +80,23 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
labelColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
labelColor:
|
||||||
|
Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
labelStyle: const TextStyle(fontSize: 13),
|
labelStyle: const TextStyle(fontSize: 13),
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
unselectedLabelColor: Theme.of(context).colorScheme.outline,
|
unselectedLabelColor: Theme.of(context).colorScheme.outline,
|
||||||
tabAlignment: TabAlignment.start,
|
tabAlignment: TabAlignment.start,
|
||||||
onTap: (index) {
|
onTap: (index) {
|
||||||
if (index == _searchResultController!.tabIndex) {
|
if (index == _searchResultController.tabIndex) {
|
||||||
Get.find<SearchPanelController>(
|
Get.find<SearchPanelController>(
|
||||||
tag: SearchType.values[index].type +
|
tag: SearchType.values[index].type +
|
||||||
_searchResultController!.keyword!)
|
_searchResultController.keyword!)
|
||||||
.animateToTop();
|
.animateToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
_searchResultController!.tabIndex = index;
|
_searchResultController.tabIndex = index;
|
||||||
},
|
},
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -102,7 +106,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
children: [
|
children: [
|
||||||
for (var i in SearchType.values) ...{
|
for (var i in SearchType.values) ...{
|
||||||
SearchPanel(
|
SearchPanel(
|
||||||
keyword: _searchResultController!.keyword,
|
keyword: _searchResultController.keyword,
|
||||||
searchType: i,
|
searchType: i,
|
||||||
tag: DateTime.now().millisecondsSinceEpoch.toString(),
|
tag: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -17,10 +17,15 @@ class SubController extends GetxController {
|
|||||||
int pageSize = 20;
|
int pageSize = 20;
|
||||||
RxBool hasMore = true.obs;
|
RxBool hasMore = true.obs;
|
||||||
|
|
||||||
Future<dynamic> querySubFolder({type = 'init'}) async {
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> querySubFolder({type = 'init'}) async {
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
return {'status': false, 'msg': '账号未登录'};
|
return {'status': false, 'msg': '账号未登录', 'code': -101};
|
||||||
}
|
}
|
||||||
var res = await UserHttp.userSubFolder(
|
var res = await UserHttp.userSubFolder(
|
||||||
pn: currentPage,
|
pn: currentPage,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:easy_debounce/easy_throttle.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/item.dart';
|
import 'widgets/item.dart';
|
||||||
|
|
||||||
@ -68,8 +69,18 @@ class _SubPageState extends State<SubPage> {
|
|||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
HttpError(
|
HttpError(
|
||||||
errMsg: data?['msg'],
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
fn: () => setState(() {}),
|
btnText: data?['code'] == -101 ? '去登录' : null,
|
||||||
|
fn: () {
|
||||||
|
if (data?['code'] == -101) {
|
||||||
|
RoutePush.loginRedirectPush();
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_subController.querySubFolder();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ class SubItem extends StatelessWidget {
|
|||||||
parameters: {
|
parameters: {
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'seasonId': subFolderItem.id.toString(),
|
'seasonId': subFolderItem.id.toString(),
|
||||||
|
'type': subFolderItem.type.toString(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|||||||
@ -14,13 +14,16 @@ class SubDetailController extends GetxController {
|
|||||||
RxList<SubDetailMediaItem> subList = <SubDetailMediaItem>[].obs;
|
RxList<SubDetailMediaItem> subList = <SubDetailMediaItem>[].obs;
|
||||||
RxString loadingText = '加载中...'.obs;
|
RxString loadingText = '加载中...'.obs;
|
||||||
int mediaCount = 0;
|
int mediaCount = 0;
|
||||||
|
late int channelType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
item = Get.arguments;
|
item = Get.arguments;
|
||||||
if (Get.parameters.keys.isNotEmpty) {
|
final parameters = Get.parameters;
|
||||||
seasonId = int.parse(Get.parameters['seasonId']!);
|
if (parameters.isNotEmpty) {
|
||||||
heroTag = Get.parameters['heroTag']!;
|
seasonId = int.tryParse(parameters['seasonId'] ?? '') ?? 0;
|
||||||
|
heroTag = parameters['heroTag'] ?? '';
|
||||||
|
channelType = int.tryParse(parameters['type'] ?? '') ?? 0;
|
||||||
}
|
}
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
@ -31,7 +34,7 @@ class SubDetailController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isLoadingMore = true;
|
isLoadingMore = true;
|
||||||
var res = type == 21
|
var res = channelType == 21
|
||||||
? await UserHttp.userSeasonList(
|
? await UserHttp.userSeasonList(
|
||||||
seasonId: seasonId,
|
seasonId: seasonId,
|
||||||
ps: 20,
|
ps: 20,
|
||||||
|
|||||||
@ -198,8 +198,8 @@ class _SubDetailPageState extends State<SubDetailPage> {
|
|||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
Map data = snapshot.data;
|
Map? data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data != null && data['status']) {
|
||||||
if (_subDetailController.item.mediaCount == 0) {
|
if (_subDetailController.item.mediaCount == 0) {
|
||||||
return const NoData();
|
return const NoData();
|
||||||
} else {
|
} else {
|
||||||
@ -219,7 +219,7 @@ class _SubDetailPageState extends State<SubDetailPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
fn: () => setState(() {}),
|
fn: () => setState(() {}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
@ -5,6 +6,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart';
|
|||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
|
import 'package:pilipala/utils/image_save.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import '../../../common/widgets/badge.dart';
|
import '../../../common/widgets/badge.dart';
|
||||||
@ -40,6 +42,11 @@ class SubVideoCardH extends StatelessWidget {
|
|||||||
'videoType': SearchType.video,
|
'videoType': SearchType.video,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context,
|
||||||
|
videoItem,
|
||||||
|
SmartDialog.dismiss,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class VideoDetailController extends GetxController
|
|||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
RxString bgCover = ''.obs;
|
RxString bgCover = ''.obs;
|
||||||
RxString cover = ''.obs;
|
RxString cover = ''.obs;
|
||||||
PlPlayerController plPlayerController = PlPlayerController.getInstance();
|
PlPlayerController plPlayerController = PlPlayerController();
|
||||||
|
|
||||||
late VideoItem firstVideo;
|
late VideoItem firstVideo;
|
||||||
late AudioItem firstAudio;
|
late AudioItem firstAudio;
|
||||||
@ -547,7 +547,7 @@ class VideoDetailController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateCover(String? pic) {
|
void updateCover(String? pic) {
|
||||||
if (pic != null && pic != '') {
|
if (pic != null) {
|
||||||
cover.value = videoItem['pic'] = pic;
|
cover.value = videoItem['pic'] = pic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -450,6 +450,7 @@ class VideoIntroController extends GetxController {
|
|||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
videoDetailCtr.cover.value = cover;
|
videoDetailCtr.cover.value = cover;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
|
videoDetailCtr.getSubtitle();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
/// 未渲染回复组件时可能异常
|
/// 未渲染回复组件时可能异常
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:expandable/expandable.dart';
|
import 'package:expandable/expandable.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -265,6 +266,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => showIntroDetail(),
|
onTap: () => showIntroDetail(),
|
||||||
|
onLongPress: () async {
|
||||||
|
feedBack();
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(text: widget.videoDetail!.title!));
|
||||||
|
SmartDialog.showToast('标题已复制');
|
||||||
|
},
|
||||||
child: ExpandablePanel(
|
child: ExpandablePanel(
|
||||||
controller: _expandableCtr,
|
controller: _expandableCtr,
|
||||||
collapsed: Text(
|
collapsed: Text(
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.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/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class IntroDetail extends StatelessWidget {
|
class IntroDetail extends StatelessWidget {
|
||||||
@ -16,9 +17,6 @@ class IntroDetail extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: SelectableRegion(
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -27,6 +25,7 @@ class IntroDetail extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
feedBack();
|
||||||
Clipboard.setData(ClipboardData(text: videoDetail!.bvid!));
|
Clipboard.setData(ClipboardData(text: videoDetail!.bvid!));
|
||||||
SmartDialog.showToast('已复制');
|
SmartDialog.showToast('已复制');
|
||||||
},
|
},
|
||||||
@ -40,7 +39,9 @@ class IntroDetail extends StatelessWidget {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: videoDetail!.bvid!));
|
feedBack();
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: videoDetail!.aid!.toString()));
|
||||||
SmartDialog.showToast('已复制');
|
SmartDialog.showToast('已复制');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -53,7 +54,10 @@ class IntroDetail extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text.rich(
|
SelectableRegion(
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
selectionControls: MaterialTextSelectionControls(),
|
||||||
|
child: Text.rich(
|
||||||
style: const TextStyle(height: 1.4),
|
style: const TextStyle(height: 1.4),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
@ -61,8 +65,8 @@ class IntroDetail extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
||||||
import 'package:pilipala/common/widgets/animated_dialog.dart';
|
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
|
||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
import './controller.dart';
|
import './controller.dart';
|
||||||
|
|
||||||
@ -54,20 +52,6 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
|||||||
child: VideoCardH(
|
child: VideoCardH(
|
||||||
videoItem: relatedVideoList[index],
|
videoItem: relatedVideoList[index],
|
||||||
showPubdate: true,
|
showPubdate: true,
|
||||||
longPress: () {
|
|
||||||
try {
|
|
||||||
_releatedController.popupDialog =
|
|
||||||
_createPopupDialog(_releatedController
|
|
||||||
.relatedVideoList[index]);
|
|
||||||
Overlay.of(context)
|
|
||||||
.insert(_releatedController.popupDialog!);
|
|
||||||
} catch (err) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
longPressEnd: () {
|
|
||||||
_releatedController.popupDialog?.remove();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -89,15 +73,4 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createPopupDialog(videoItem) {
|
|
||||||
return OverlayEntry(
|
|
||||||
builder: (BuildContext context) => AnimatedDialog(
|
|
||||||
closeFn: _releatedController.popupDialog?.remove,
|
|
||||||
child: OverlayPop(
|
|
||||||
videoItem: videoItem,
|
|
||||||
closeFn: _releatedController.popupDialog?.remove),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import 'package:pilipala/pages/preview/index.dart';
|
|||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/url_utils.dart';
|
import 'package:pilipala/utils/url_utils.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
@ -44,7 +45,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
feedBack();
|
feedBack();
|
||||||
if (replyReply != null) {
|
if (replyReply != null) {
|
||||||
replyReply!(replyItem, null);
|
replyReply!(replyItem);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
@ -358,7 +359,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
InkWell(
|
InkWell(
|
||||||
// 一楼点击评论展开评论详情
|
// 一楼点击评论展开评论详情
|
||||||
// onTap: () {
|
// onTap: () {
|
||||||
// replyReply?.call(replyItem, replies![i]);
|
// replyReply?.call(replyItem);
|
||||||
// },
|
// },
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
feedBack();
|
feedBack();
|
||||||
@ -642,23 +643,25 @@ InlineSpan buildContent(
|
|||||||
'',
|
'',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final String redirectUrl =
|
final String pathSegment = Uri.parse(matchStr).path;
|
||||||
await UrlUtils.parseRedirectUrl(matchStr);
|
Map matchRes = IdUtils.matchAvorBv(input: pathSegment);
|
||||||
if (redirectUrl == matchStr) {
|
List matchKeys = matchRes.keys.toList();
|
||||||
Clipboard.setData(ClipboardData(text: matchStr));
|
if (matchKeys.isNotEmpty) {
|
||||||
SmartDialog.showToast('地址可能有误');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String pathSegment = Uri.parse(redirectUrl).path;
|
|
||||||
final String lastPathSegment =
|
|
||||||
pathSegment.split('/').last;
|
|
||||||
if (lastPathSegment.startsWith('BV')) {
|
|
||||||
UrlUtils.matchUrlPush(
|
UrlUtils.matchUrlPush(
|
||||||
lastPathSegment,
|
matchRes.containsKey('AV')
|
||||||
|
? matchRes['AV']! as int
|
||||||
|
: matchRes['BV'],
|
||||||
title,
|
title,
|
||||||
redirectUrl,
|
matchStr,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
final String redirectUrl =
|
||||||
|
await UrlUtils.parseRedirectUrl(matchStr);
|
||||||
|
// if (redirectUrl == matchStr) {
|
||||||
|
// Clipboard.setData(ClipboardData(text: matchStr));
|
||||||
|
// SmartDialog.showToast('地址可能有误');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/webview',
|
'/webview',
|
||||||
parameters: {
|
parameters: {
|
||||||
|
|||||||
@ -323,11 +323,13 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: handlePlay,
|
onTap: handlePlay,
|
||||||
child: Image.network(
|
child: Obx(
|
||||||
vdCtr.videoItem['pic'],
|
() => NetworkImgLayer(
|
||||||
|
src: vdCtr.cover.value,
|
||||||
width: Get.width,
|
width: Get.width,
|
||||||
height: videoHeight,
|
height: videoHeight,
|
||||||
fit: BoxFit.cover, // 适应方式根据需要调整
|
type: 'emote',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
buildCustomAppBar(),
|
buildCustomAppBar(),
|
||||||
@ -535,6 +537,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
controller: _extendNestCtr,
|
controller: _extendNestCtr,
|
||||||
headerSliverBuilder:
|
headerSliverBuilder:
|
||||||
(BuildContext context2, bool innerBoxIsScrolled) {
|
(BuildContext context2, bool innerBoxIsScrolled) {
|
||||||
|
return <Widget>[
|
||||||
|
Obx(
|
||||||
|
() {
|
||||||
final Orientation orientation =
|
final Orientation orientation =
|
||||||
MediaQuery.of(context).orientation;
|
MediaQuery.of(context).orientation;
|
||||||
final bool isFullScreen =
|
final bool isFullScreen =
|
||||||
@ -546,9 +551,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
? 0
|
? 0
|
||||||
: MediaQuery.of(context).padding.top))
|
: MediaQuery.of(context).padding.top))
|
||||||
: videoHeight.value;
|
: videoHeight.value;
|
||||||
return <Widget>[
|
|
||||||
Obx(
|
|
||||||
() {
|
|
||||||
if (orientation == Orientation.landscape ||
|
if (orientation == Orientation.landscape ||
|
||||||
isFullScreen) {
|
isFullScreen) {
|
||||||
enterFullScreen();
|
enterFullScreen();
|
||||||
|
|||||||
@ -52,7 +52,10 @@ class WebviewController extends GetxController {
|
|||||||
loadProgress.value = progress;
|
loadProgress.value = progress;
|
||||||
},
|
},
|
||||||
onPageStarted: (String url) {
|
onPageStarted: (String url) {
|
||||||
final String str = Uri.parse(url).pathSegments[0];
|
final List pathSegments = Uri.parse(url).pathSegments;
|
||||||
|
if (pathSegments.isNotEmpty &&
|
||||||
|
url != 'https://passport.bilibili.com/h5-app/passport/login') {
|
||||||
|
final String str = pathSegments[0];
|
||||||
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||||
final List matchKeys = matchRes.keys.toList();
|
final List matchKeys = matchRes.keys.toList();
|
||||||
if (matchKeys.isNotEmpty) {
|
if (matchKeys.isNotEmpty) {
|
||||||
@ -63,6 +66,7 @@ class WebviewController extends GetxController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// 加载完成
|
// 加载完成
|
||||||
onUrlChange: (UrlChange urlChange) async {
|
onUrlChange: (UrlChange urlChange) async {
|
||||||
@ -106,6 +110,9 @@ class WebviewController extends GetxController {
|
|||||||
SmartDialog.showToast('登录成功');
|
SmartDialog.showToast('登录成功');
|
||||||
try {
|
try {
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
|
if (!userInfoCache.isOpen) {
|
||||||
|
userInfoCache = await Hive.openBox('userInfo');
|
||||||
|
}
|
||||||
await userInfoCache.put('userInfoCache', result['data']);
|
await userInfoCache.put('userInfoCache', result['data']);
|
||||||
|
|
||||||
final HomeController homeCtr = Get.find<HomeController>();
|
final HomeController homeCtr = Get.find<HomeController>();
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:flutter/material.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/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
@ -154,16 +155,33 @@ class ChatItem extends StatelessWidget {
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
SmartDialog.showLoading();
|
SmartDialog.showLoading();
|
||||||
var bvid = content["bvid"];
|
final String bvid = content["bvid"];
|
||||||
|
// 16番剧 5投稿
|
||||||
|
final int source = content["source"];
|
||||||
|
final String? url = content["url"];
|
||||||
|
|
||||||
final int cid = await SearchHttp.ab2c(bvid: bvid);
|
final int cid = await SearchHttp.ab2c(bvid: bvid);
|
||||||
final String heroTag = Utils.makeHeroTag(bvid);
|
final String heroTag = Utils.makeHeroTag(bvid);
|
||||||
SmartDialog.dismiss<dynamic>().then(
|
await SmartDialog.dismiss();
|
||||||
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
if (source == 5) {
|
||||||
|
Get.toNamed<dynamic>(
|
||||||
|
'/video?bvid=$bvid&cid=$cid',
|
||||||
arguments: <String, String?>{
|
arguments: <String, String?>{
|
||||||
'pic': content['thumb'],
|
'pic': content['thumb'],
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (source == 16) {
|
||||||
|
if (url != null) {
|
||||||
|
final String area = url.split('/').last;
|
||||||
|
if (area.startsWith('ep')) {
|
||||||
|
RoutePush.bangumiPush(null, Utils.matchNum(area).first);
|
||||||
|
} else if (area.startsWith('ss')) {
|
||||||
|
RoutePush.bangumiPush(Utils.matchNum(area).first, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
width: 220,
|
width: 220,
|
||||||
@ -183,7 +201,7 @@ class ChatItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 1),
|
const SizedBox(height: 1),
|
||||||
Text(
|
Text(
|
||||||
content['author'],
|
content['author'] ?? '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
letterSpacing: 0.6,
|
letterSpacing: 0.6,
|
||||||
height: 1.5,
|
height: 1.5,
|
||||||
@ -206,7 +224,7 @@ class ChatItem extends StatelessWidget {
|
|||||||
SmartDialog.dismiss<dynamic>().then(
|
SmartDialog.dismiss<dynamic>().then(
|
||||||
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
||||||
arguments: <String, String?>{
|
arguments: <String, String?>{
|
||||||
'pic': content['thumb'],
|
'pic': content['thumb'] ?? '',
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -123,6 +123,7 @@ class PlPlayerController {
|
|||||||
PreferredSizeWidget? bottomControl;
|
PreferredSizeWidget? bottomControl;
|
||||||
Widget? danmuWidget;
|
Widget? danmuWidget;
|
||||||
late RxList subtitles;
|
late RxList subtitles;
|
||||||
|
String videoType = 'archive';
|
||||||
|
|
||||||
/// 数据加载监听
|
/// 数据加载监听
|
||||||
Stream<DataStatus> get onDataStatusChanged => dataStatus.status.stream;
|
Stream<DataStatus> get onDataStatusChanged => dataStatus.status.stream;
|
||||||
@ -220,7 +221,7 @@ class PlPlayerController {
|
|||||||
Rx<int> get playerCount => _playerCount;
|
Rx<int> get playerCount => _playerCount;
|
||||||
|
|
||||||
///
|
///
|
||||||
Rx<String> get videoType => _videoType;
|
// Rx<String> get videoType => _videoType;
|
||||||
|
|
||||||
/// 弹幕开关
|
/// 弹幕开关
|
||||||
Rx<bool> isOpenDanmu = false.obs;
|
Rx<bool> isOpenDanmu = false.obs;
|
||||||
@ -274,8 +275,7 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加一个私有构造函数
|
// 添加一个私有构造函数
|
||||||
PlPlayerController._() {
|
PlPlayerController._internal(this.videoType) {
|
||||||
_videoType = videoType;
|
|
||||||
isOpenDanmu.value =
|
isOpenDanmu.value =
|
||||||
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
||||||
blockTypes =
|
blockTypes =
|
||||||
@ -330,11 +330,11 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取实例 传参
|
// 获取实例 传参
|
||||||
static PlPlayerController getInstance({
|
factory PlPlayerController({
|
||||||
String videoType = 'archive',
|
String videoType = 'archive',
|
||||||
}) {
|
}) {
|
||||||
// 如果实例尚未创建,则创建一个新实例
|
// 如果实例尚未创建,则创建一个新实例
|
||||||
_instance ??= PlPlayerController._();
|
_instance ??= PlPlayerController._internal(videoType);
|
||||||
if (videoType != 'none') {
|
if (videoType != 'none') {
|
||||||
_instance!._playerCount.value += 1;
|
_instance!._playerCount.value += 1;
|
||||||
_videoType.value = videoType;
|
_videoType.value = videoType;
|
||||||
@ -395,7 +395,7 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
// 配置Player 音轨、字幕等等
|
// 配置Player 音轨、字幕等等
|
||||||
_videoPlayerController = await _createVideoController(
|
_videoPlayerController = await _createVideoController(
|
||||||
dataSource, _looping, enableHA, width, height);
|
dataSource, _looping, enableHA, width, height, seekTo);
|
||||||
// 获取视频时长 00:00
|
// 获取视频时长 00:00
|
||||||
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
||||||
updateDurationSecond();
|
updateDurationSecond();
|
||||||
@ -406,7 +406,7 @@ class PlPlayerController {
|
|||||||
if (!_listenersInitialized) {
|
if (!_listenersInitialized) {
|
||||||
startListeners();
|
startListeners();
|
||||||
}
|
}
|
||||||
await _initializePlayer(seekTo: seekTo, duration: _duration.value);
|
await _initializePlayer(duration: _duration.value);
|
||||||
bool autoEnterFullcreen =
|
bool autoEnterFullcreen =
|
||||||
setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false);
|
setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false);
|
||||||
if (autoEnterFullcreen && _isFirstTime) {
|
if (autoEnterFullcreen && _isFirstTime) {
|
||||||
@ -426,6 +426,7 @@ class PlPlayerController {
|
|||||||
bool enableHA,
|
bool enableHA,
|
||||||
double? width,
|
double? width,
|
||||||
double? height,
|
double? height,
|
||||||
|
Duration seekTo,
|
||||||
) async {
|
) async {
|
||||||
// 每次配置时先移除监听
|
// 每次配置时先移除监听
|
||||||
removeListeners();
|
removeListeners();
|
||||||
@ -442,7 +443,7 @@ class PlPlayerController {
|
|||||||
configuration: PlayerConfiguration(
|
configuration: PlayerConfiguration(
|
||||||
// 默认缓存 5M 大小
|
// 默认缓存 5M 大小
|
||||||
bufferSize:
|
bufferSize:
|
||||||
videoType.value == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024,
|
videoType == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -507,8 +508,9 @@ class PlPlayerController {
|
|||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
player.open(
|
await player.open(
|
||||||
Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders),
|
Media(dataSource.videoSource!,
|
||||||
|
httpHeaders: dataSource.httpHeaders, start: seekTo),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
// 音轨
|
// 音轨
|
||||||
@ -521,7 +523,6 @@ class PlPlayerController {
|
|||||||
|
|
||||||
// 开始播放
|
// 开始播放
|
||||||
Future _initializePlayer({
|
Future _initializePlayer({
|
||||||
Duration seekTo = Duration.zero,
|
|
||||||
Duration? duration,
|
Duration? duration,
|
||||||
}) async {
|
}) async {
|
||||||
getVideoFit();
|
getVideoFit();
|
||||||
@ -530,9 +531,9 @@ class PlPlayerController {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
/// 跳转播放
|
/// 跳转播放
|
||||||
if (seekTo != Duration.zero) {
|
// if (seekTo != Duration.zero) {
|
||||||
await this.seekTo(seekTo);
|
// await this.seekTo(seekTo);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// 自动播放
|
/// 自动播放
|
||||||
if (_autoPlay) {
|
if (_autoPlay) {
|
||||||
@ -540,7 +541,7 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 设置倍速
|
/// 设置倍速
|
||||||
if (videoType.value == 'live') {
|
if (videoType == 'live') {
|
||||||
await setPlaybackSpeed(1.0);
|
await setPlaybackSpeed(1.0);
|
||||||
} else {
|
} else {
|
||||||
if (_playbackSpeed.value != 1.0) {
|
if (_playbackSpeed.value != 1.0) {
|
||||||
@ -932,7 +933,7 @@ class PlPlayerController {
|
|||||||
|
|
||||||
/// 设置长按倍速状态 live模式下禁用
|
/// 设置长按倍速状态 live模式下禁用
|
||||||
void setDoubleSpeedStatus(bool val) {
|
void setDoubleSpeedStatus(bool val) {
|
||||||
if (videoType.value == 'live') {
|
if (videoType == 'live') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (controlsLock.value) {
|
if (controlsLock.value) {
|
||||||
@ -976,41 +977,8 @@ class PlPlayerController {
|
|||||||
} else {
|
} else {
|
||||||
await landScape();
|
await landScape();
|
||||||
}
|
}
|
||||||
|
} else if (isFullScreen.value && !status) {
|
||||||
// bool isValid =
|
|
||||||
// direction.value == 'vertical' || mode == FullScreenMode.vertical
|
|
||||||
// ? true
|
|
||||||
// : false;
|
|
||||||
// var result = await showDialog(
|
|
||||||
// context: Get.context!,
|
|
||||||
// useSafeArea: false,
|
|
||||||
// builder: (context) => Dialog.fullscreen(
|
|
||||||
// backgroundColor: Colors.black,
|
|
||||||
// child: SafeArea(
|
|
||||||
// // 忽略手机安全区域
|
|
||||||
// top: isValid,
|
|
||||||
// left: false,
|
|
||||||
// right: false,
|
|
||||||
// bottom: isValid,
|
|
||||||
// child: PLVideoPlayer(
|
|
||||||
// controller: this,
|
|
||||||
// headerControl: headerControl,
|
|
||||||
// bottomControl: bottomControl,
|
|
||||||
// danmuWidget: danmuWidget,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// if (result == null) {
|
|
||||||
// // 退出全屏
|
|
||||||
// StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
|
||||||
// exitFullScreen();
|
|
||||||
// await verticalScreen();
|
|
||||||
// toggleFullScreen(false);
|
|
||||||
// }
|
|
||||||
} else if (isFullScreen.value) {
|
|
||||||
StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
||||||
// Get.back();
|
|
||||||
exitFullScreen();
|
exitFullScreen();
|
||||||
await verticalScreen();
|
await verticalScreen();
|
||||||
toggleFullScreen(false);
|
toggleFullScreen(false);
|
||||||
@ -1047,7 +1015,7 @@ class PlPlayerController {
|
|||||||
if (!_enableHeart) {
|
if (!_enableHeart) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (videoType.value == 'live') {
|
if (videoType == 'live') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 播放状态变化时,更新
|
// 播放状态变化时,更新
|
||||||
@ -1146,7 +1114,6 @@ class PlPlayerController {
|
|||||||
// _buffered.close();
|
// _buffered.close();
|
||||||
// _showControls.close();
|
// _showControls.close();
|
||||||
// _controlsLock.close();
|
// _controlsLock.close();
|
||||||
|
|
||||||
// playerStatus.status.close();
|
// playerStatus.status.close();
|
||||||
// dataStatus.status.close();
|
// dataStatus.status.close();
|
||||||
|
|
||||||
|
|||||||
@ -334,7 +334,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
fuc: () => _.triggerFullScreen(),
|
fuc: () => _.triggerFullScreen(status: !_.isFullScreen.value),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
final List<Widget> list = [];
|
final List<Widget> list = [];
|
||||||
@ -652,7 +652,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
onDoubleTapDown: (TapDownDetails details) {
|
onDoubleTapDown: (TapDownDetails details) {
|
||||||
// live模式下禁用 锁定时🔒禁用
|
// live模式下禁用 锁定时🔒禁用
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType == 'live' || _.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final double totalWidth = MediaQuery.sizeOf(context).width;
|
final double totalWidth = MediaQuery.sizeOf(context).width;
|
||||||
@ -679,7 +679,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
/// 水平位置 快进 live模式下禁用
|
/// 水平位置 快进 live模式下禁用
|
||||||
onHorizontalDragUpdate: (DragUpdateDetails details) {
|
onHorizontalDragUpdate: (DragUpdateDetails details) {
|
||||||
// live模式下禁用 锁定时🔒禁用
|
// live模式下禁用 锁定时🔒禁用
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType == 'live' || _.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// final double tapPosition = details.localPosition.dx;
|
// final double tapPosition = details.localPosition.dx;
|
||||||
@ -695,7 +695,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
_.onChangedSliderStart();
|
_.onChangedSliderStart();
|
||||||
},
|
},
|
||||||
onHorizontalDragEnd: (DragEndDetails details) {
|
onHorizontalDragEnd: (DragEndDetails details) {
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType == 'live' || _.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_.onChangedSliderEnd();
|
_.onChangedSliderEnd();
|
||||||
@ -826,7 +826,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.videoType.value == 'live') {
|
if (_.videoType == 'live') {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
if (value > max || max <= 0) {
|
if (value > max || max <= 0) {
|
||||||
@ -879,7 +879,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
// 锁
|
// 锁
|
||||||
Obx(
|
Obx(
|
||||||
() => Visibility(
|
() => Visibility(
|
||||||
visible: _.videoType.value != 'live' && _.isFullScreen.value,
|
visible: _.videoType != 'live' && _.isFullScreen.value,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: FractionalTranslation(
|
child: FractionalTranslation(
|
||||||
|
|||||||
@ -26,7 +26,7 @@ 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(videoType: 'none');
|
PlPlayerController player = PlPlayerController();
|
||||||
|
|
||||||
VideoPlayerServiceHandler() {
|
VideoPlayerServiceHandler() {
|
||||||
revalidateSetting();
|
revalidateSetting();
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class AudioSessionHandler {
|
|||||||
session.configure(const AudioSessionConfiguration.music());
|
session.configure(const AudioSessionConfiguration.music());
|
||||||
|
|
||||||
session.interruptionEventStream.listen((event) {
|
session.interruptionEventStream.listen((event) {
|
||||||
final player = PlPlayerController.getInstance(videoType: 'none');
|
final player = PlPlayerController(videoType: 'none');
|
||||||
if (event.begin) {
|
if (event.begin) {
|
||||||
if (!player.playerStatus.playing) return;
|
if (!player.playerStatus.playing) return;
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
@ -51,7 +51,7 @@ class AudioSessionHandler {
|
|||||||
|
|
||||||
// 耳机拔出暂停
|
// 耳机拔出暂停
|
||||||
session.becomingNoisyEventStream.listen((_) {
|
session.becomingNoisyEventStream.listen((_) {
|
||||||
final player = PlPlayerController.getInstance(videoType: 'none');
|
final player = PlPlayerController(videoType: 'none');
|
||||||
if (player.playerStatus.playing) {
|
if (player.playerStatus.playing) {
|
||||||
player.pause();
|
player.pause();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,7 +89,7 @@ class ShutdownTimerService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PlPlayerController plPlayerController =
|
PlPlayerController plPlayerController =
|
||||||
PlPlayerController.getInstance(videoType: 'none');
|
PlPlayerController(videoType: 'none');
|
||||||
if (!exitApp && !waitForPlayingCompleted) {
|
if (!exitApp && !waitForPlayingCompleted) {
|
||||||
if (!plPlayerController.playerStatus.playing) {
|
if (!plPlayerController.playerStatus.playing) {
|
||||||
//仅提示用户
|
//仅提示用户
|
||||||
@ -124,7 +124,7 @@ class ShutdownTimerService {
|
|||||||
} else {
|
} else {
|
||||||
//暂停播放
|
//暂停播放
|
||||||
PlPlayerController plPlayerController =
|
PlPlayerController plPlayerController =
|
||||||
PlPlayerController.getInstance(videoType: 'none');
|
PlPlayerController(videoType: 'none');
|
||||||
if (plPlayerController.playerStatus.playing) {
|
if (plPlayerController.playerStatus.playing) {
|
||||||
plPlayerController.pause();
|
plPlayerController.pause();
|
||||||
waitForPlayingCompleted = true;
|
waitForPlayingCompleted = true;
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import 'package:appscheme/appscheme.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/utils/route_push.dart';
|
||||||
import '../http/search.dart';
|
import '../http/search.dart';
|
||||||
import '../models/common/search_type.dart';
|
|
||||||
import 'id_utils.dart';
|
import 'id_utils.dart';
|
||||||
import 'url_utils.dart';
|
import 'url_utils.dart';
|
||||||
import 'utils.dart';
|
import 'utils.dart';
|
||||||
@ -68,7 +68,7 @@ class PiliSchame {
|
|||||||
} else if (host == 'bangumi') {
|
} else if (host == 'bangumi') {
|
||||||
if (path.startsWith('/season')) {
|
if (path.startsWith('/season')) {
|
||||||
final String seasonId = path.split('/').last;
|
final String seasonId = path.split('/').last;
|
||||||
_bangumiPush(int.parse(seasonId), null);
|
RoutePush.bangumiPush(int.parse(seasonId), null);
|
||||||
}
|
}
|
||||||
} else if (host == 'opus') {
|
} else if (host == 'opus') {
|
||||||
if (path.startsWith('/detail')) {
|
if (path.startsWith('/detail')) {
|
||||||
@ -126,35 +126,6 @@ class PiliSchame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 番剧跳转
|
|
||||||
static Future<void> _bangumiPush(int? seasonId, int? epId) async {
|
|
||||||
SmartDialog.showLoading<dynamic>(msg: '获取中...');
|
|
||||||
try {
|
|
||||||
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId);
|
|
||||||
if (result['status']) {
|
|
||||||
var bangumiDetail = result['data'];
|
|
||||||
final int cid = bangumiDetail.episodes!.first.cid;
|
|
||||||
final String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid);
|
|
||||||
final String heroTag = Utils.makeHeroTag(cid);
|
|
||||||
var epId = bangumiDetail.episodes!.first.id;
|
|
||||||
SmartDialog.dismiss().then(
|
|
||||||
(e) => Get.toNamed(
|
|
||||||
'/video?bvid=$bvid&cid=$cid&epId=$epId',
|
|
||||||
arguments: <String, dynamic>{
|
|
||||||
'pic': bangumiDetail.cover,
|
|
||||||
'heroTag': heroTag,
|
|
||||||
'videoType': SearchType.media_bangumi,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast(result['msg']);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
SmartDialog.showToast('番剧获取失败:$e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<void> _fullPathPush(SchemeEntity value) async {
|
static Future<void> _fullPathPush(SchemeEntity value) async {
|
||||||
// https://m.bilibili.com/bangumi/play/ss39708
|
// https://m.bilibili.com/bangumi/play/ss39708
|
||||||
// https | m.bilibili.com | /bangumi/play/ss39708
|
// https | m.bilibili.com | /bangumi/play/ss39708
|
||||||
@ -167,8 +138,23 @@ class PiliSchame {
|
|||||||
print('bilibili.com host: $host');
|
print('bilibili.com host: $host');
|
||||||
print('bilibili.com path: $path');
|
print('bilibili.com path: $path');
|
||||||
final String lastPathSegment = path!.split('/').last;
|
final String lastPathSegment = path!.split('/').last;
|
||||||
if (lastPathSegment.contains('BV')) {
|
if (path.startsWith('/video')) {
|
||||||
_videoPush(null, lastPathSegment);
|
Map matchRes = IdUtils.matchAvorBv(input: path);
|
||||||
|
if (matchRes.containsKey('AV')) {
|
||||||
|
_videoPush(matchRes['AV']! as int, null);
|
||||||
|
} else if (matchRes.containsKey('BV')) {
|
||||||
|
_videoPush(null, matchRes['BV'] as String);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('投稿匹配失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path.startsWith('/bangumi')) {
|
||||||
|
if (lastPathSegment.contains('ss')) {
|
||||||
|
RoutePush.bangumiPush(Utils.matchNum(lastPathSegment).first, null);
|
||||||
|
}
|
||||||
|
if (lastPathSegment.contains('ep')) {
|
||||||
|
RoutePush.bangumiPush(null, Utils.matchNum(lastPathSegment).first);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (host.contains('live')) {
|
} else if (host.contains('live')) {
|
||||||
int roomId = int.parse(path!.split('/').last);
|
int roomId = int.parse(path!.split('/').last);
|
||||||
@ -220,9 +206,9 @@ class PiliSchame {
|
|||||||
case 'bangumi':
|
case 'bangumi':
|
||||||
print('番剧');
|
print('番剧');
|
||||||
if (area.startsWith('ep')) {
|
if (area.startsWith('ep')) {
|
||||||
_bangumiPush(null, matchNum(area).first);
|
RoutePush.bangumiPush(null, Utils.matchNum(area).first);
|
||||||
} else if (area.startsWith('ss')) {
|
} else if (area.startsWith('ss')) {
|
||||||
_bangumiPush(matchNum(area).first, null);
|
RoutePush.bangumiPush(Utils.matchNum(area).first, null);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'video':
|
case 'video':
|
||||||
@ -238,7 +224,7 @@ class PiliSchame {
|
|||||||
break;
|
break;
|
||||||
case 'read':
|
case 'read':
|
||||||
print('专栏');
|
print('专栏');
|
||||||
String id = 'cv${matchNum(query!['id']!).first}';
|
String id = 'cv${Utils.matchNum(query!['id']!).first}';
|
||||||
Get.toNamed('/htmlRender', parameters: {
|
Get.toNamed('/htmlRender', parameters: {
|
||||||
'url': value.dataString!,
|
'url': value.dataString!,
|
||||||
'title': '',
|
'title': '',
|
||||||
@ -254,21 +240,14 @@ class PiliSchame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<int> matchNum(String str) {
|
|
||||||
final RegExp regExp = RegExp(r'\d+');
|
|
||||||
final Iterable<Match> matches = regExp.allMatches(str);
|
|
||||||
|
|
||||||
return matches.map((Match match) => int.parse(match.group(0)!)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handleEpisodePath(String lastPathSegment, String redirectUrl) {
|
static void _handleEpisodePath(String lastPathSegment, String redirectUrl) {
|
||||||
final String seasonId = _extractIdFromPath(lastPathSegment);
|
final String seasonId = _extractIdFromPath(lastPathSegment);
|
||||||
_bangumiPush(null, matchNum(seasonId).first);
|
RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _handleSeasonPath(String lastPathSegment, String redirectUrl) {
|
static void _handleSeasonPath(String lastPathSegment, String redirectUrl) {
|
||||||
final String seasonId = _extractIdFromPath(lastPathSegment);
|
final String seasonId = _extractIdFromPath(lastPathSegment);
|
||||||
_bangumiPush(matchNum(seasonId).first, null);
|
RoutePush.bangumiPush(Utils.matchNum(seasonId).first, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _extractIdFromPath(String lastPathSegment) {
|
static String _extractIdFromPath(String lastPathSegment) {
|
||||||
|
|||||||
@ -15,24 +15,7 @@ class DownloadUtils {
|
|||||||
PermissionStatus status = await Permission.storage.status;
|
PermissionStatus status = await Permission.storage.status;
|
||||||
if (status == PermissionStatus.denied ||
|
if (status == PermissionStatus.denied ||
|
||||||
status == PermissionStatus.permanentlyDenied) {
|
status == PermissionStatus.permanentlyDenied) {
|
||||||
SmartDialog.show(
|
await permissionDialog('提示', '存储权限未授权');
|
||||||
useSystem: true,
|
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('提示'),
|
|
||||||
content: const Text('存储权限未授权'),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
openAppSettings();
|
|
||||||
},
|
|
||||||
child: const Text('去授权'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
@ -45,24 +28,7 @@ class DownloadUtils {
|
|||||||
PermissionStatus status = await Permission.photos.status;
|
PermissionStatus status = await Permission.photos.status;
|
||||||
if (status == PermissionStatus.denied ||
|
if (status == PermissionStatus.denied ||
|
||||||
status == PermissionStatus.permanentlyDenied) {
|
status == PermissionStatus.permanentlyDenied) {
|
||||||
SmartDialog.show(
|
await permissionDialog('提示', '相册权限未授权');
|
||||||
useSystem: true,
|
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('提示'),
|
|
||||||
content: const Text('相册权限未授权'),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
openAppSettings();
|
|
||||||
},
|
|
||||||
child: const Text('去授权'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
@ -72,9 +38,7 @@ class DownloadUtils {
|
|||||||
static Future<bool> downloadImg(String imgUrl,
|
static Future<bool> downloadImg(String imgUrl,
|
||||||
{String imgType = 'cover'}) async {
|
{String imgType = 'cover'}) async {
|
||||||
try {
|
try {
|
||||||
if (!Platform.isAndroid || !await requestPhotoPer()) {
|
if (Platform.isAndroid) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
if (androidInfo.version.sdkInt <= 32) {
|
if (androidInfo.version.sdkInt <= 32) {
|
||||||
if (!await requestStoragePer()) {
|
if (!await requestStoragePer()) {
|
||||||
@ -85,6 +49,7 @@ class DownloadUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SmartDialog.showLoading(msg: '保存中');
|
SmartDialog.showLoading(msg: '保存中');
|
||||||
var response = await Dio()
|
var response = await Dio()
|
||||||
@ -101,13 +66,38 @@ class DownloadUtils {
|
|||||||
);
|
);
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
await SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 ');
|
SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 ');
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
await permissionDialog('保存失败', '相册权限未授权');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
SmartDialog.showToast(err.toString());
|
SmartDialog.showToast(err.toString());
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future permissionDialog(String title, String content,
|
||||||
|
{Function? onGranted}) async {
|
||||||
|
await SmartDialog.show(
|
||||||
|
useSystem: true,
|
||||||
|
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(content),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
openAppSettings();
|
||||||
|
},
|
||||||
|
child: const Text('去授权'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
lib/utils/image_save.dart
Normal file
88
lib/utils/image_save.dart
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:pilipala/common/constants.dart';
|
||||||
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/utils/download.dart';
|
||||||
|
|
||||||
|
Future imageSaveDialog(context, videoItem, closeFn) {
|
||||||
|
final double imgWidth =
|
||||||
|
MediaQuery.sizeOf(context).width - StyleString.safeSpace * 2;
|
||||||
|
return SmartDialog.show(
|
||||||
|
animationType: SmartAnimationType.centerScale_otherSlide,
|
||||||
|
builder: (context) => Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: imgWidth,
|
||||||
|
height: imgWidth / StyleString.aspectRatio,
|
||||||
|
src: videoItem.pic! as String,
|
||||||
|
quality: 100,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
child: Container(
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(20))),
|
||||||
|
child: IconButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () => closeFn!(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 10, 8, 10),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
videoItem.title! as String,
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
IconButton(
|
||||||
|
tooltip: '保存封面图',
|
||||||
|
onPressed: () async {
|
||||||
|
bool saveStatus = await DownloadUtils.downloadImg(
|
||||||
|
videoItem.pic != null
|
||||||
|
? videoItem.pic as String
|
||||||
|
: videoItem.cover as String,
|
||||||
|
);
|
||||||
|
// 保存成功,自动关闭弹窗
|
||||||
|
if (saveStatus) {
|
||||||
|
closeFn?.call();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.download, size: 20),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
68
lib/utils/route_push.dart
Normal file
68
lib/utils/route_push.dart
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/search.dart';
|
||||||
|
import 'package:pilipala/models/bangumi/info.dart';
|
||||||
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
|
class RoutePush {
|
||||||
|
// 番剧跳转
|
||||||
|
static Future<void> bangumiPush(int? seasonId, int? epId,
|
||||||
|
{String? heroTag}) async {
|
||||||
|
SmartDialog.showLoading<dynamic>(msg: '获取中...');
|
||||||
|
try {
|
||||||
|
var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId);
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
if (result['status']) {
|
||||||
|
if (result['data'].episodes.isEmpty) {
|
||||||
|
SmartDialog.showToast('资源获取失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final BangumiInfoModel bangumiDetail = result['data'];
|
||||||
|
final EpisodeItem episode = bangumiDetail.episodes!.first;
|
||||||
|
final int epId = episode.id!;
|
||||||
|
final int cid = episode.cid!;
|
||||||
|
final String bvid = episode.bvid!;
|
||||||
|
final String cover = episode.cover!;
|
||||||
|
final Map arguments = <String, dynamic>{
|
||||||
|
'pic': cover,
|
||||||
|
'videoType': SearchType.media_bangumi,
|
||||||
|
// 'bangumiItem': bangumiDetail,
|
||||||
|
};
|
||||||
|
arguments['heroTag'] = heroTag ?? Utils.makeHeroTag(cid);
|
||||||
|
Get.toNamed(
|
||||||
|
'/video?bvid=$bvid&cid=$cid&epId=$epId',
|
||||||
|
arguments: arguments,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('番剧获取失败:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录跳转
|
||||||
|
static Future<void> loginPush() async {
|
||||||
|
await Get.toNamed(
|
||||||
|
'/webview',
|
||||||
|
parameters: {
|
||||||
|
'url': 'https://passport.bilibili.com/h5-app/passport/login',
|
||||||
|
'type': 'login',
|
||||||
|
'pageTitle': '登录bilibili',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录跳转
|
||||||
|
static Future<void> loginRedirectPush() async {
|
||||||
|
await Get.offAndToNamed(
|
||||||
|
'/webview',
|
||||||
|
parameters: {
|
||||||
|
'url': 'https://passport.bilibili.com/h5-app/passport/login',
|
||||||
|
'type': 'login',
|
||||||
|
'pageTitle': '登录bilibili',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ class UrlUtils {
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
final response = await dio.get(url);
|
final response = await dio.get(url);
|
||||||
if (response.statusCode == 302) {
|
if (response.statusCode == 302 || response.statusCode == 301) {
|
||||||
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);
|
||||||
@ -42,12 +42,14 @@ class UrlUtils {
|
|||||||
final Map matchRes = IdUtils.matchAvorBv(input: pathSegment);
|
final Map matchRes = IdUtils.matchAvorBv(input: pathSegment);
|
||||||
if (matchRes.containsKey('BV')) {
|
if (matchRes.containsKey('BV')) {
|
||||||
final String bv = matchRes['BV'];
|
final String bv = matchRes['BV'];
|
||||||
final int cid = await SearchHttp.ab2c(bvid: bv);
|
final Map res = await SearchHttp.ab2cWithPic(bvid: bv);
|
||||||
|
final int cid = res['cid'];
|
||||||
|
final String pic = res['pic'];
|
||||||
final String heroTag = Utils.makeHeroTag(bv);
|
final String heroTag = Utils.makeHeroTag(bv);
|
||||||
await Get.toNamed(
|
await Get.toNamed(
|
||||||
'/video?bvid=$bv&cid=$cid',
|
'/video?bvid=$bv&cid=$cid',
|
||||||
arguments: <String, String?>{
|
arguments: <String, String?>{
|
||||||
'pic': '',
|
'pic': pic,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -383,4 +383,11 @@ class Utils {
|
|||||||
List<int> randomBytes = generateRandomBytes(minLength, maxLength);
|
List<int> randomBytes = generateRandomBytes(minLength, maxLength);
|
||||||
return base64.encode(randomBytes);
|
return base64.encode(randomBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<int> matchNum(String str) {
|
||||||
|
final RegExp regExp = RegExp(r'\d+');
|
||||||
|
final Iterable<Match> matches = regExp.allMatches(str);
|
||||||
|
|
||||||
|
return matches.map((Match match) => int.parse(match.group(0)!)).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
pubspec.lock
35
pubspec.lock
@ -69,10 +69,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: audio_service
|
name: audio_service
|
||||||
sha256: a4d989f1225ea9621898d60f23236dcbfc04876fa316086c23c5c4af075dbac4
|
sha256: "4547c312a94f9cb2c48b60823fb190767cbd63454a83c73049384d5d3cba4650"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.12"
|
version: "0.18.13"
|
||||||
audio_service_platform_interface:
|
audio_service_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -85,10 +85,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: audio_service_web
|
name: audio_service_web
|
||||||
sha256: "523e64ddc914c714d53eec2da85bba1074f08cf26c786d4efb322de510815ea7"
|
sha256: "9d7d5ae5f98a5727f2580fad73062f2484f400eef6cef42919413268e62a363e"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
version: "0.1.2"
|
||||||
audio_session:
|
audio_session:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -873,10 +873,11 @@ packages:
|
|||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit
|
path: media_kit
|
||||||
sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a"
|
ref: HEAD
|
||||||
url: "https://pub.flutter-io.cn"
|
resolved-ref: "285f7919bbf4a7d89a62615b14a3766a171ad575"
|
||||||
source: hosted
|
url: "https://github.com/media-kit/media-kit"
|
||||||
|
source: git
|
||||||
version: "1.1.10+1"
|
version: "1.1.10+1"
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
@ -913,10 +914,11 @@ packages:
|
|||||||
media_kit_libs_video:
|
media_kit_libs_video:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_video
|
path: "libs/universal/media_kit_libs_video"
|
||||||
sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067"
|
ref: HEAD
|
||||||
url: "https://pub.flutter-io.cn"
|
resolved-ref: "285f7919bbf4a7d89a62615b14a3766a171ad575"
|
||||||
source: hosted
|
url: "https://github.com/media-kit/media-kit"
|
||||||
|
source: git
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
media_kit_libs_windows_video:
|
media_kit_libs_windows_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
@ -937,10 +939,11 @@ packages:
|
|||||||
media_kit_video:
|
media_kit_video:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_video
|
path: media_kit_video
|
||||||
sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882
|
ref: HEAD
|
||||||
url: "https://pub.flutter-io.cn"
|
resolved-ref: "285f7919bbf4a7d89a62615b14a3766a171ad575"
|
||||||
source: hosted
|
url: "https://github.com/media-kit/media-kit"
|
||||||
|
source: git
|
||||||
version: "1.2.4"
|
version: "1.2.4"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|||||||
20
pubspec.yaml
20
pubspec.yaml
@ -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.23+1023
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
@ -90,8 +90,8 @@ dependencies:
|
|||||||
media_kit_libs_video: ^1.0.4
|
media_kit_libs_video: ^1.0.4
|
||||||
|
|
||||||
# 媒体通知
|
# 媒体通知
|
||||||
audio_service: ^0.18.12
|
audio_service: ^0.18.13
|
||||||
audio_session: ^0.1.16
|
audio_session: ^0.1.18
|
||||||
|
|
||||||
# 音量、亮度、屏幕控制
|
# 音量、亮度、屏幕控制
|
||||||
flutter_volume_controller: ^1.3.1
|
flutter_volume_controller: ^1.3.1
|
||||||
@ -165,6 +165,20 @@ dev_dependencies:
|
|||||||
hive_generator: ^2.0.0
|
hive_generator: ^2.0.0
|
||||||
build_runner: ^2.4.8
|
build_runner: ^2.4.8
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
media_kit:
|
||||||
|
git:
|
||||||
|
url: https://github.com/media-kit/media-kit
|
||||||
|
path: media_kit
|
||||||
|
media_kit_video:
|
||||||
|
git:
|
||||||
|
url: https://github.com/media-kit/media-kit
|
||||||
|
path: media_kit_video
|
||||||
|
media_kit_libs_video:
|
||||||
|
git:
|
||||||
|
url: https://github.com/media-kit/media-kit
|
||||||
|
path: libs/universal/media_kit_libs_video
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: true
|
android: true
|
||||||
ios: true
|
ios: true
|
||||||
|
|||||||
Reference in New Issue
Block a user