Merge branch 'main' into feature-danmaku

This commit is contained in:
guozhigq
2023-08-27 18:40:39 +08:00
32 changed files with 876 additions and 446 deletions

View File

@@ -20,7 +20,22 @@
"android.support.customtabs.action.CustomTabsService" /> "android.support.customtabs.action.CustomTabsService" />
</intent> </intent>
</queries> </queries>
<application
<queries>
<!-- If your app checks for http support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
<application
android:label="PiliPala" android:label="PiliPala"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
@@ -48,6 +63,164 @@
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bilibili" android:host="forward" />
<data android:scheme="bilibili" android:host="comment"
android:pathPattern="/detail/.*/.*/.*" />
<data android:scheme="bilibili" android:host="uper" />
<data android:scheme="bilibili" android:host="article"
android:pathPattern="/readlist" />
<data android:scheme="bilibili" android:host="advertise" android:path="/home" />
<data android:scheme="bilibili" android:host="clip" />
<data android:scheme="bilibili" android:host="search" />
<data android:scheme="bilibili" android:host="stardust-search" />
<data android:scheme="bilibili" android:host="music" />
<data android:scheme="bilibili" android:host="bangumi"
android:pathPattern="/season.*" />
<data android:scheme="bilibili" android:host="bangumi" android:pathPattern="/.*" />
<data android:scheme="bilibili" android:host="pictureshow"
android:pathPrefix="/creative_center" />
<data android:scheme="bilibili" android:host="cliparea" />
<data android:scheme="bilibili" android:host="im" />
<data android:scheme="bilibili" android:host="im" android:path="/notifications" />
<data android:scheme="bilibili" android:host="following" />
<data android:scheme="bilibili" android:host="following"
android:pathPattern="/detail/.*" />
<data android:scheme="bilibili" android:host="following"
android:path="/publishInfo/" />
<data android:scheme="bilibili" android:host="laser" android:pathPattern="/.*" />
<data android:scheme="bilibili" android:host="livearea" />
<data android:scheme="bilibili" android:host="live" />
<data android:scheme="bilibili" android:host="catalog" />
<data android:scheme="bilibili" android:host="browser" />
<data android:scheme="bilibili" android:host="user_center" />
<data android:scheme="bilibili" android:host="login" />
<data android:scheme="bilibili" android:host="space" />
<data android:scheme="bilibili" android:host="author" />
<data android:scheme="bilibili" android:host="tag" />
<data android:scheme="bilibili" android:host="rank" />
<data android:scheme="bilibili" android:host="external" />
<data android:scheme="bilibili" android:host="blank" />
<data android:scheme="bilibili" android:host="home" />
<data android:scheme="bilibili" android:host="root" />
<data android:scheme="bilibili" android:host="video" />
<data android:scheme="bilibili" android:host="story" />
<data android:scheme="bilibili" android:host="podcast" />
<data android:scheme="bilibili" android:host="search" />
<data android:scheme="bilibili" android:host="main" android:path="/favorite" />
<data android:scheme="bilibili" android:host="pgc" android:path="/theater/match" />
<data android:scheme="bilibili" android:host="pgc" android:path="/theater/square" />
<data android:scheme="bilibili" android:host="m.bilibili.com"
android:path="/topic-detail" />
<data android:scheme="bilibili" android:host="article" />
<data android:scheme="bilibili" android:host="pegasus"
android:pathPattern="/channel/v2/.*" />
<data android:scheme="bilibili" android:host="feed" android:pathPattern="/channel" />
<data android:scheme="bilibili" android:host="vip" />
<data android:scheme="bilibili" android:host="user_center" android:path="/vip" />
<data android:scheme="bilibili" android:host="history" />
<data android:scheme="bilibili" android:host="charge" android:path="/rank" />
<data android:scheme="bilibili" android:host="assistant" />
<data android:scheme="bilibili" android:host="assistant" />
<data android:scheme="bilibili" android:host="feedback" />
<data android:scheme="bilibili" android:host="auth" android:path="/launch" />
<data android:scheme="http" android:host="live.bilibili.com"
android:pathPattern="/live/.*" />
<data android:scheme="https" android:host="live.bilibili.com"
android:pathPattern="/live/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.tv"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="www.bilibili.tv"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.cn"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="www.bilibili.cn"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/mobile/video/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/mobile/video/.*" />
<data android:scheme="http" android:host="m.bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="m.bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/story/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/story/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/bangumi/i/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/bangumi/i/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/mobile/bangumi/i/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/mobile/bangumi/i/.*" />
<data android:scheme="http" android:host="bangumi.bilibili.com"
android:pathPattern="/.*" />
<data android:scheme="https" android:host="bangumi.bilibili.com"
android:pathPattern="/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/bangumi/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/bangumi/.*" />
<data android:scheme="http" android:host="m.bilibili.com"
android:pathPattern="/bangumi/.*" />
<data android:scheme="https" android:host="m.bilibili.com"
android:pathPattern="/bangumi/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/cheese/play/ss.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/cheese/play/ss.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/cheese/play/ep.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/cheese/play/ep.*" />
<data android:scheme="http" android:host="m.bilibili.com"
android:pathPattern="/bangumi/play/ss.*" />
<data android:scheme="https" android:host="m.bilibili.com"
android:pathPattern="/cheese/play/ss.*" />
<data android:scheme="http" android:host="m.bilibili.com"
android:pathPattern="/cheese/play/ep.*" />
<data android:scheme="https" android:host="m.bilibili.com"
android:pathPattern="/cheese/play/ep.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/read/cv.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/read/cv.*" />
<data android:scheme="http" android:host="www.bilibili.com" android:path="/review/" />
<data android:scheme="https" android:host="www.bilibili.com" android:path="/review/" />
<data android:scheme="http" android:host="bilibili.cn"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="bilibili.cn"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.cn"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="www.bilibili.cn"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/video/.*" />
<data android:scheme="http" android:host="www.bilibili.com"
android:pathPattern="/mobile/video/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/mobile/video/.*" />
</intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
@@ -56,7 +229,6 @@
android:value="2" /> android:value="2" />
</application> </application>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@@ -1,4 +1,6 @@
PODS: PODS:
- appscheme (1.0.4):
- Flutter
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- ReachabilitySwift - ReachabilitySwift
@@ -45,6 +47,7 @@ PODS:
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- appscheme (from `.symlinks/plugins/appscheme/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
@@ -71,6 +74,8 @@ SPEC REPOS:
- ReachabilitySwift - ReachabilitySwift
EXTERNAL SOURCES: EXTERNAL SOURCES:
appscheme:
:path: ".symlinks/plugins/appscheme/ios"
connectivity_plus: connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios" :path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus: device_info_plus:
@@ -111,6 +116,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios" :path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854

View File

@@ -1,62 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>PiliPala</string> <string>PiliPala</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>pilipala</string> <string>PiliPala</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>NSPhotoLibraryAddUsageDescription</key> <key>NSPhotoLibraryAddUsageDescription</key>
<string>请允许APP保存图片到相册</string> <string>请允许APP保存图片到相册</string>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相册</string> <string>App需要您的同意,才能访问相册</string>
<key>NSAppleMusicUsageDescription</key> <key>NSAppleMusicUsageDescription</key>
<string>App需要您的同意,才能访问媒体资料库</string> <string>App需要您的同意,才能访问媒体资料库</string>
<key>LSApplicationQueriesSchemes</key> <key>LSApplicationQueriesSchemes</key>
<array> <array>
<string>https</string> <string>https</string>
<string>http</string> <string>http</string>
</array> </array>
</dict> <!-- Add Scheme related information -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>http</string>
<string>https</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>m.bilibili.com</string>
<string>bilibili.com</string>
<string>www.bilibili.com</string>
<string>bangumi.bilibili.com</string>
<string>bilibili.cn</string>
<string>www.bilibili.cn</string>
<string>bangumi.bilibili.cn</string>
<string>bilibili.tv</string>
<string>www.bilibili.tv</string>
<string>bangumi.bilibili.tv</string>
<string>miniapp.bilibili.com</string>
<string>live.bilibili.com</string>
</array>
</dict>
</array>
</dict>
<!-- 当其他应用程序或系统通过 bilibili -->
<dict>
<key>CFBundleURLName</key>
<string>bilibili</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bilibili</string>
</array>
</dict>
</array>
</dict>
</plist> </plist>

View File

@@ -45,11 +45,6 @@ class VideoCardVSkeleton extends StatelessWidget {
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
color: Theme.of(context).colorScheme.onInverseSurface, color: Theme.of(context).colorScheme.onInverseSurface,
), ),
Container(
width: 80,
height: 12,
color: Theme.of(context).colorScheme.onInverseSurface,
),
], ],
), ),
), ),

View File

@@ -15,12 +15,14 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
// 视频卡片 - 垂直布局 // 视频卡片 - 垂直布局
class VideoCardV extends StatelessWidget { class VideoCardV extends StatelessWidget {
final dynamic videoItem; final dynamic videoItem;
final int crossAxisCount;
final Function()? longPress; final Function()? longPress;
final Function()? longPressEnd; final Function()? longPressEnd;
const VideoCardV({ const VideoCardV({
Key? key, Key? key,
required this.videoItem, required this.videoItem,
required this.crossAxisCount,
this.longPress, this.longPress,
this.longPressEnd, this.longPressEnd,
}) : super(key: key); }) : super(key: key);
@@ -77,7 +79,7 @@ class VideoCardV extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(videoItem.id); String heroTag = Utils.makeHeroTag(videoItem.id);
return Card( return Card(
elevation: 1, elevation: crossAxisCount == 1 ? 0 : 1,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
child: GestureDetector( child: GestureDetector(
@@ -100,17 +102,27 @@ class VideoCardV extends StatelessWidget {
child: LayoutBuilder(builder: (context, boxConstraints) { child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth; double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight; double maxHeight = boxConstraints.maxHeight;
return Hero( return Stack(
tag: heroTag, children: [
child: NetworkImgLayer( Hero(
src: videoItem.pic, tag: heroTag,
width: maxWidth, child: NetworkImgLayer(
height: maxHeight, src: videoItem.pic,
), width: maxWidth,
height: maxHeight,
),
),
if (crossAxisCount == 1)
PBadge(
bottom: 10,
right: 10,
text: videoItem.duration,
)
],
); );
}), }),
), ),
VideoContent(videoItem: videoItem) VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount)
], ],
), ),
), ),
@@ -121,22 +133,47 @@ class VideoCardV extends StatelessWidget {
class VideoContent extends StatelessWidget { class VideoContent extends StatelessWidget {
final dynamic videoItem; final dynamic videoItem;
const VideoContent({Key? key, required this.videoItem}) : super(key: key); final int crossAxisCount;
const VideoContent(
{Key? key, required this.videoItem, required this.crossAxisCount})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Expanded( return Expanded(
flex: crossAxisCount == 1 ? 0 : 1,
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(9, 8, 9, 4), padding: crossAxisCount == 1
? const EdgeInsets.fromLTRB(9, 9, 9, 4)
: const EdgeInsets.fromLTRB(9, 8, 9, 4),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Row(
videoItem.title, children: [
maxLines: 2, Expanded(
overflow: TextOverflow.ellipsis, child: Text(
videoItem.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
if (videoItem.goto == 'av' && crossAxisCount == 1) ...[
const SizedBox(width: 10),
WatchLater(
size: 32,
iconSize: 18,
callFn: () async {
int aid = videoItem.param;
var res =
await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
SmartDialog.showToast(res['msg']);
},
),
],
],
), ),
if (crossAxisCount == 1) const SizedBox(height: 6),
Row( Row(
children: [ children: [
if (videoItem.goto == 'bangumi') ...[ if (videoItem.goto == 'bangumi') ...[
@@ -167,6 +204,7 @@ class VideoContent extends StatelessWidget {
) )
], ],
Expanded( Expanded(
flex: crossAxisCount == 1 ? 0 : 1,
child: Text( child: Text(
videoItem.owner.name, videoItem.owner.name,
maxLines: 1, maxLines: 1,
@@ -177,95 +215,33 @@ class VideoContent extends StatelessWidget {
), ),
), ),
), ),
if (videoItem.goto == 'av') if (crossAxisCount == 1) ...[
SizedBox( Text(
width: 24, '',
height: 24, style: TextStyle(
child: PopupMenuButton<String>( fontSize:
padding: EdgeInsets.zero, Theme.of(context).textTheme.labelMedium!.fontSize,
tooltip: '稍后再看', color: Theme.of(context).colorScheme.outline,
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: 14,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
onTap: () async {
int aid = videoItem.param;
var res = await UserHttp.toViewLater(
bvid: IdUtils.av2bv(aid));
SmartDialog.showToast(res['msg']);
},
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
),
),
],
), ),
), ),
VideoStat(
videoItem: videoItem,
)
],
const Spacer(),
if (videoItem.goto == 'av' && crossAxisCount != 1)
WatchLater(
size: 24,
iconSize: 14,
callFn: () async {
int aid = videoItem.param;
var res =
await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
SmartDialog.showToast(res['msg']);
},
),
], ],
), ),
// Row(
// children: [
// const SizedBox(width: 1),
// StatView(
// theme: 'gray',
// view: videoItem.stat.view,
// ),
// const SizedBox(width: 10),
// StatDanMu(
// theme: 'gray',
// danmu: videoItem.stat.danmaku,
// ),
// const Spacer(),
// SizedBox(
// width: 24,
// height: 24,
// child: PopupMenuButton<String>(
// padding: EdgeInsets.zero,
// tooltip: '稍后再看',
// icon: Icon(
// Icons.more_vert_outlined,
// color: Theme.of(context).colorScheme.outline,
// size: 14,
// ),
// position: PopupMenuPosition.under,
// // constraints: const BoxConstraints(maxHeight: 35),
// onSelected: (String type) {},
// itemBuilder: (BuildContext context) =>
// <PopupMenuEntry<String>>[
// PopupMenuItem<String>(
// onTap: () async {
// var res =
// await UserHttp.toViewLater(bvid: videoItem.bvid);
// SmartDialog.showToast(res['msg']);
// },
// value: 'pause',
// height: 35,
// child: const Row(
// children: [
// Icon(Icons.watch_later_outlined, size: 16),
// SizedBox(width: 6),
// Text('稍后再看', style: TextStyle(fontSize: 13))
// ],
// ),
// ),
// ],
// ),
// ),
// ],
// ),
], ],
), ),
), ),
@@ -274,53 +250,77 @@ class VideoContent extends StatelessWidget {
} }
class VideoStat extends StatelessWidget { class VideoStat extends StatelessWidget {
final int? view; final dynamic videoItem;
final int? danmaku;
final int? duration;
const VideoStat( const VideoStat({
{Key? key, Key? key,
required this.view, required this.videoItem,
required this.danmaku, }) : super(key: key);
required this.duration})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Row(
height: 48, children: [
padding: const EdgeInsets.only(top: 22, left: 6, right: 6), Text(
decoration: const BoxDecoration( '${videoItem.stat.view}次观看',
gradient: LinearGradient( style: TextStyle(
begin: Alignment.topCenter, fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
end: Alignment.bottomCenter, color: Theme.of(context).colorScheme.outline,
colors: <Color>[ ),
Colors.transparent, ),
Colors.black54, Text(
], '${videoItem.stat.danmu}条弹幕',
tileMode: TileMode.mirror, style: TextStyle(
), fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
), color: Theme.of(context).colorScheme.outline,
child: Row( ),
mainAxisAlignment: MainAxisAlignment.spaceBetween, ),
children: [ ],
Row( );
children: [ }
StatView( }
theme: 'white',
view: view, class WatchLater extends StatelessWidget {
), final double? size;
const SizedBox(width: 6), final double? iconSize;
StatDanMu( final Function? callFn;
theme: 'white',
danmu: danmaku, const WatchLater({
), Key? key,
], required this.size,
required this.iconSize,
this.callFn,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: size,
height: size,
child: PopupMenuButton<String>(
padding: EdgeInsets.zero,
tooltip: '稍后再看',
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: iconSize,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
onTap: () => callFn!(),
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
),
), ),
Text(
Utils.timeFormat(duration!),
style: const TextStyle(fontSize: 11, color: Colors.white),
)
], ],
), ),
); );

View File

@@ -13,6 +13,7 @@ import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/router/app_pages.dart';
import 'package:pilipala/pages/main/view.dart'; import 'package:pilipala/pages/main/view.dart';
import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
@@ -25,9 +26,6 @@ void main() async {
.then((_) async { .then((_) async {
await GStrorage.init(); await GStrorage.init();
runApp(const MyApp()); runApp(const MyApp());
await Request.setCookie();
await Data.init();
await GStrorage.lazyInit();
// 小白条、导航栏沉浸 // 小白条、导航栏沉浸
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
@@ -35,6 +33,10 @@ void main() async {
systemNavigationBarDividerColor: Colors.transparent, systemNavigationBarDividerColor: Colors.transparent,
statusBarColor: Colors.transparent, statusBarColor: Colors.transparent,
)); ));
await Request.setCookie();
Data.init();
GStrorage.lazyInit();
PiliSchame.init();
}); });
} }

View File

@@ -1,17 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/live.dart'; import 'package:pilipala/http/live.dart';
import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/utils/storage.dart';
class LiveController extends GetxController { class LiveController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
int count = 12; int count = 12;
int _currentPage = 1; int _currentPage = 1;
int crossAxisCount = 2; RxInt crossAxisCount = 2.obs;
RxList<LiveItemModel> liveList = [LiveItemModel()].obs; RxList<LiveItemModel> liveList = [LiveItemModel()].obs;
bool isLoadingMore = false; bool isLoadingMore = false;
bool flag = false; bool flag = false;
OverlayEntry? popupDialog; OverlayEntry? popupDialog;
Box setting = GStrorage.setting;
@override
void onInit() {
super.onInit();
crossAxisCount.value =
setting.get(SettingBoxKey.enableSingleRow, defaultValue: false) ? 1 : 2;
}
// 获取推荐 // 获取推荐
Future queryLiveList(type) async { Future queryLiveList(type) async {

View File

@@ -129,14 +129,15 @@ class _LivePageState extends State<LivePage> {
} }
Widget contentGrid(ctr, liveList) { Widget contentGrid(ctr, liveList) {
double maxWidth = Get.size.width; // double maxWidth = Get.size.width;
int baseWidth = 500; // int baseWidth = 500;
int step = 300; // int step = 300;
int crossAxisCount = // int crossAxisCount =
maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2; // maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
if (maxWidth < 300) { // if (maxWidth < 300) {
crossAxisCount = 1; // crossAxisCount = 1;
} // }
int crossAxisCount = ctr.crossAxisCount.value;
return SliverGrid( return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// 行间距 // 行间距
@@ -147,13 +148,15 @@ class _LivePageState extends State<LivePage> {
crossAxisCount: crossAxisCount, crossAxisCount: crossAxisCount,
mainAxisExtent: mainAxisExtent:
Get.size.width / crossAxisCount / StyleString.aspectRatio + Get.size.width / crossAxisCount / StyleString.aspectRatio +
68 * MediaQuery.of(context).textScaleFactor, (crossAxisCount == 1 ? 48 : 68) *
MediaQuery.of(context).textScaleFactor,
), ),
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
return liveList!.isNotEmpty return liveList!.isNotEmpty
? LiveCardV( ? LiveCardV(
liveItem: liveList[index], liveItem: liveList[index],
crossAxisCount: crossAxisCount,
longPress: () { longPress: () {
_liveController.popupDialog = _liveController.popupDialog =
_createPopupDialog(liveList[index]); _createPopupDialog(liveList[index]);

View File

@@ -1,7 +1,6 @@
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/badge.dart';
import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/models/live/item.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,12 +8,14 @@ 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 Function()? longPress; final Function()? longPress;
final Function()? longPressEnd; final Function()? longPressEnd;
const LiveCardV({ const LiveCardV({
Key? key, Key? key,
required this.liveItem, required this.liveItem,
required this.crossAxisCount,
this.longPress, this.longPress,
this.longPressEnd, this.longPressEnd,
}) : super(key: key); }) : super(key: key);
@@ -23,7 +24,7 @@ class LiveCardV extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(liveItem.roomId); String heroTag = Utils.makeHeroTag(liveItem.roomId);
return Card( return Card(
elevation: 1, elevation: crossAxisCount == 1 ? 0 : 1,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
child: GestureDetector( child: GestureDetector(
@@ -45,12 +46,7 @@ class LiveCardV extends StatelessWidget {
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) {
@@ -66,24 +62,25 @@ class LiveCardV extends StatelessWidget {
height: maxHeight, height: maxHeight,
), ),
), ),
Positioned( if (crossAxisCount != 1)
left: 0, Positioned(
right: 0, left: 0,
bottom: 0, right: 0,
child: AnimatedOpacity( bottom: 0,
opacity: 1, child: AnimatedOpacity(
duration: const Duration(milliseconds: 200), opacity: 1,
child: VideoStat( duration: const Duration(milliseconds: 200),
liveItem: liveItem, child: VideoStat(
liveItem: liveItem,
),
), ),
), ),
),
], ],
); );
}), }),
), ),
), ),
LiveContent(liveItem: liveItem) LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
], ],
), ),
), ),
@@ -94,13 +91,18 @@ class LiveCardV extends StatelessWidget {
class LiveContent extends StatelessWidget { class LiveContent extends StatelessWidget {
final dynamic liveItem; final dynamic liveItem;
const LiveContent({Key? key, required this.liveItem}) : super(key: key); final int crossAxisCount;
const LiveContent(
{Key? key, required this.liveItem, required this.crossAxisCount})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Expanded( return Expanded(
flex: crossAxisCount == 1 ? 0 : 1,
child: Padding( child: Padding(
// 多列 padding: crossAxisCount == 1
padding: const EdgeInsets.fromLTRB(9, 9, 9, 8), ? const EdgeInsets.fromLTRB(9, 9, 9, 4)
: const EdgeInsets.fromLTRB(9, 8, 9, 8),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -112,29 +114,40 @@ class LiveContent extends StatelessWidget {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
letterSpacing: 0.3, letterSpacing: 0.3,
), ),
maxLines: 2, maxLines: crossAxisCount == 1 ? 1 : 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (crossAxisCount == 1) const SizedBox(height: 4),
Row( Row(
children: [ children: [
const PBadge( Text(
text: 'UP', liveItem.uname,
size: 'small', textAlign: TextAlign.start,
stack: 'normal', style: TextStyle(
fs: 9, fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
Expanded( if (crossAxisCount == 1) ...[
child: Text( Text(
liveItem.uname, '${liveItem!.areaName!}',
textAlign: TextAlign.start,
style: TextStyle( style: TextStyle(
fontSize: fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize, Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
), ),
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
) Text(
'${liveItem!.watchedShow!['text_small']}人观看',
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
]
], ],
), ),
], ],

View File

@@ -29,10 +29,10 @@ class LiveRoomController extends GetxController {
if (Get.arguments != null) { if (Get.arguments != null) {
liveItem = Get.arguments['liveItem']; liveItem = Get.arguments['liveItem'];
heroTag = Get.arguments['heroTag'] ?? ''; heroTag = Get.arguments['heroTag'] ?? '';
if (liveItem.pic != null && liveItem.pic != '') { if (liveItem != null && liveItem.pic != null && liveItem.pic != '') {
cover = liveItem.pic; cover = liveItem.pic;
} }
if (liveItem.cover != null && liveItem.cover != '') { if (liveItem != null && liveItem.cover != null && liveItem.cover != '') {
cover = liveItem.cover; cover = liveItem.cover;
} }
} }

View File

@@ -48,32 +48,35 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
appBar: AppBar( appBar: AppBar(
centerTitle: false, centerTitle: false,
titleSpacing: 0, titleSpacing: 0,
title: Row( title: _liveRoomController.liveItem != null
children: [ ? Row(
NetworkImgLayer( children: [
width: 34, NetworkImgLayer(
height: 34, width: 34,
type: 'avatar', height: 34,
src: _liveRoomController.liveItem.face, type: 'avatar',
), src: _liveRoomController.liveItem.face,
const SizedBox(width: 10), ),
Column( const SizedBox(width: 10),
crossAxisAlignment: CrossAxisAlignment.start, Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Text( children: [
_liveRoomController.liveItem.uname, Text(
style: const TextStyle(fontSize: 14), _liveRoomController.liveItem.uname,
), style: const TextStyle(fontSize: 14),
const SizedBox(height: 1), ),
if (_liveRoomController.liveItem.watchedShow != null) const SizedBox(height: 1),
Text( if (_liveRoomController.liveItem.watchedShow != null)
_liveRoomController.liveItem.watchedShow['text_large'] ?? Text(
'', _liveRoomController
style: const TextStyle(fontSize: 12)), .liveItem.watchedShow['text_large'] ??
], '',
), style: const TextStyle(fontSize: 12)),
], ],
), ),
],
)
: const SizedBox(),
// actions: [ // actions: [
// SizedBox( // SizedBox(
// height: 34, // height: 34,
@@ -94,21 +97,22 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
? PLVideoPlayer(controller: plPlayerController!) ? PLVideoPlayer(controller: plPlayerController!)
: const SizedBox(), : const SizedBox(),
), ),
if (_liveRoomController.liveItem.cover != null) // if (_liveRoomController.liveItem != null &&
Visibility( // _liveRoomController.liveItem.cover != null)
visible: isShowCover, // Visibility(
child: Positioned( // visible: isShowCover,
top: 0, // child: Positioned(
left: 0, // top: 0,
right: 0, // left: 0,
child: NetworkImgLayer( // right: 0,
type: 'emote', // child: NetworkImgLayer(
src: _liveRoomController.liveItem.cover, // type: 'emote',
width: Get.size.width, // src: _liveRoomController.liveItem.cover,
height: videoHeight, // width: Get.size.width,
), // height: videoHeight,
), // ),
), // ),
// ),
], ],
), ),
), ),

View File

@@ -12,10 +12,14 @@ class RcmdController extends GetxController {
bool isLoadingMore = true; bool isLoadingMore = true;
OverlayEntry? popupDialog; OverlayEntry? popupDialog;
Box recVideo = GStrorage.recVideo; Box recVideo = GStrorage.recVideo;
Box setting = GStrorage.setting;
RxInt crossAxisCount = 2.obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
crossAxisCount.value =
setting.get(SettingBoxKey.enableSingleRow, defaultValue: false) ? 1 : 2;
if (recVideo.get('cacheList') != null && if (recVideo.get('cacheList') != null &&
recVideo.get('cacheList').isNotEmpty) { recVideo.get('cacheList').isNotEmpty) {
List<RecVideoItemAppModel> list = []; List<RecVideoItemAppModel> list = [];

View File

@@ -142,31 +142,34 @@ class _RcmdPageState extends State<RcmdPage>
} }
Widget contentGrid(ctr, videoList) { Widget contentGrid(ctr, videoList) {
double maxWidth = Get.size.width; // double maxWidth = Get.size.width;
int baseWidth = 500; // int baseWidth = 500;
int step = 300; // int step = 300;
int crossAxisCount = // int crossAxisCount =
maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2; // maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
if (maxWidth < 300) { // if (maxWidth < 300) {
crossAxisCount = 1; // crossAxisCount = 1;
} // }
int crossAxisCount = ctr.crossAxisCount.value;
double mainAxisExtent =
(Get.size.width / crossAxisCount / StyleString.aspectRatio) +
68 * MediaQuery.of(context).textScaleFactor;
return SliverGrid( return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// 行间距 // 行间距
mainAxisSpacing: StyleString.cardSpace + 4, mainAxisSpacing: StyleString.safeSpace,
// 列间距 // 列间距
crossAxisSpacing: StyleString.cardSpace + 4, crossAxisSpacing: StyleString.safeSpace,
// 列数 // 列数
crossAxisCount: crossAxisCount, crossAxisCount: crossAxisCount,
mainAxisExtent: mainAxisExtent: mainAxisExtent,
(Get.size.width / crossAxisCount / StyleString.aspectRatio) +
68 * MediaQuery.of(context).textScaleFactor,
), ),
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
return videoList!.isNotEmpty return videoList!.isNotEmpty
? VideoCardV( ? VideoCardV(
videoItem: videoList[index], videoItem: videoList[index],
crossAxisCount: crossAxisCount,
longPress: () { longPress: () {
_rcmdController.popupDialog = _rcmdController.popupDialog =
_createPopupDialog(videoList[index]); _createPopupDialog(videoList[index]);

View File

@@ -21,6 +21,8 @@ class SSearchController extends GetxController {
Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间 Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间
String hintText = '搜索'; String hintText = '搜索';
RxString defaultSearch = '输入关键词搜索'.obs; RxString defaultSearch = '输入关键词搜索'.obs;
Box setting = GStrorage.setting;
bool enableHotKey = true;
@override @override
void onInit() { void onInit() {
@@ -38,6 +40,7 @@ class SSearchController extends GetxController {
} }
historyCacheList = histiryWord.get('cacheList') ?? []; historyCacheList = histiryWord.get('cacheList') ?? [];
historyList.value = historyCacheList; historyList.value = historyCacheList;
enableHotKey = setting.get(SettingBoxKey.enableHotKey, defaultValue: true);
} }
void onChange(value) { void onChange(value) {

View File

@@ -146,7 +146,9 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
// 搜索建议 // 搜索建议
_searchSuggest(), _searchSuggest(),
// 热搜 // 热搜
hotSearch(_searchController), Visibility(
visible: _searchController.enableHotKey,
child: hotSearch(_searchController)),
// 搜索历史 // 搜索历史
_history() _history()
], ],

View File

@@ -56,14 +56,20 @@ class SearchPanelController extends GetxController {
// 匹配输入内容如果是AV、BV号且有结果 直接跳转详情页 // 匹配输入内容如果是AV、BV号且有结果 直接跳转详情页
Map matchRes = IdUtils.matchAvorBv(input: keyword); Map matchRes = IdUtils.matchAvorBv(input: keyword);
List matchKeys = matchRes.keys.toList(); List matchKeys = matchRes.keys.toList();
if (matchKeys.isNotEmpty && searchType == SearchType.video) { String bvid = resultList.first.bvid;
String bvid = resultList.first.bvid; // keyword 可能输入纯数字
int aid = resultList.first.aid; int aid = resultList.first.aid;
if (matchKeys.isNotEmpty && searchType == SearchType.video ||
aid.toString() == keyword) {
String heroTag = Utils.makeHeroTag(bvid); String heroTag = Utils.makeHeroTag(bvid);
int cid = await SearchHttp.ab2c(aid: aid, bvid: bvid); int cid = await SearchHttp.ab2c(aid: aid, bvid: bvid);
if (matchKeys.first == 'BV' && matchRes[matchKeys.first] == bvid || if (matchKeys.isNotEmpty &&
matchKeys.first == 'AV' && matchRes[matchKeys.first] == aid) { matchKeys.first == 'BV' &&
matchRes[matchKeys.first] == bvid ||
matchKeys.isNotEmpty &&
matchKeys.first == 'AV' &&
matchRes[matchKeys.first] == aid ||
aid.toString() == keyword) {
Get.toNamed( Get.toNamed(
'/video?bvid=$bvid&cid=$cid', '/video?bvid=$bvid&cid=$cid',
arguments: {'videoItem': resultList.first, 'heroTag': heroTag}, arguments: {'videoItem': resultList.first, 'heroTag': heroTag},

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/models/common/dynamics_type.dart'; import 'package:pilipala/models/common/dynamics_type.dart';
import 'package:pilipala/models/common/reply_sort_type.dart'; import 'package:pilipala/models/common/reply_sort_type.dart';
@@ -47,6 +48,13 @@ class _ExtraSettingState extends State<ExtraSetting> {
), ),
body: ListView( body: ListView(
children: [ children: [
SetSwitchItem(
title: '大家都在搜',
subTitle: '是否展示「大家都在搜」',
setKey: SettingBoxKey.enableHotKey,
defaultVal: true,
callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
),
ListTile( ListTile(
dense: false, dense: false,
title: Text('评论展示', style: titleStyle), title: Text('评论展示', style: titleStyle),

View File

@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
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:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/models/common/theme_type.dart';
@@ -75,6 +76,13 @@ class _StyleSettingState extends State<StyleSetting> {
setKey: SettingBoxKey.iosTransition, setKey: SettingBoxKey.iosTransition,
defaultVal: false, defaultVal: false,
), ),
SetSwitchItem(
title: '首页单列',
subTitle: '每行展示一个内容卡片',
setKey: SettingBoxKey.enableSingleRow,
defaultVal: false,
callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
),
ListTile( ListTile(
dense: false, dense: false,
onTap: () { onTap: () {

View File

@@ -8,12 +8,14 @@ class SetSwitchItem extends StatefulWidget {
final String? subTitle; final String? subTitle;
final String? setKey; final String? setKey;
final bool? defaultVal; final bool? defaultVal;
final Function? callFn;
const SetSwitchItem({ const SetSwitchItem({
this.title, this.title,
this.subTitle, this.subTitle,
this.setKey, this.setKey,
this.defaultVal, this.defaultVal,
this.callFn,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@@ -32,12 +34,15 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false); val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false);
} }
void switchChange(value) { void switchChange(value) async {
val = value ?? !val; val = value ?? !val;
Setting.put(widget.setKey, val); await Setting.put(widget.setKey, val);
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) { if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
Utils.checkUpdata(); Utils.checkUpdata();
} }
if (widget.callFn != null) {
widget.callFn!.call(val);
}
setState(() {}); setState(() {});
} }

View File

@@ -209,27 +209,17 @@ class VideoDetailController extends GetxController
if (result['status']) { if (result['status']) {
data = result['data']; data = result['data'];
/// 优先顺序 省流模式 -> 设置中指定质量 -> 当前可选的最高质量
// firstVideo = data.dash!.video!.first;
// videoUrl = firstVideo.baseUrl!;
// //
// currentVideoQa = VideoQualityCode.fromCode(firstVideo.id!)!;
// /// 优先顺序 设置中指定质量 -> 当前可选的最高质量
// AudioItem firstAudio =
// data.dash!.audio!.isNotEmpty ? data.dash!.audio!.first : AudioItem();
// audioUrl = firstAudio.baseUrl ?? '';
List<VideoItem> allVideosList = data.dash!.video!; List<VideoItem> allVideosList = data.dash!.video!;
try { try {
// 当前可播放的最高质量视频 // 当前可播放的最高质量视频
int currentHighVideoQa = allVideosList.first.quality!.code; int currentHighVideoQa = allVideosList.first.quality!.code;
// // 使用预设的画质 当前可用的最高质量
int cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa, int cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
defaultValue: currentHighVideoQa); defaultValue: currentHighVideoQa);
int resVideoQa = currentHighVideoQa; int resVideoQa = currentHighVideoQa;
if (cacheVideoQa <= currentHighVideoQa) { if (cacheVideoQa <= currentHighVideoQa) {
// 如果预设的画质低于当前最高
List<int> numbers = data.acceptQuality! List<int> numbers = data.acceptQuality!
.where((e) => e <= currentHighVideoQa) .where((e) => e <= currentHighVideoQa)
.toList(); .toList();
@@ -250,10 +240,16 @@ class VideoDetailController extends GetxController
currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get(
SettingBoxKey.defaultDecode, SettingBoxKey.defaultDecode,
defaultValue: VideoDecodeFormats.values.last.code))!; defaultValue: VideoDecodeFormats.values.last.code))!;
print(currentDecodeFormats.description);
try { try {
// 当前视频没有对应格式返回第一个 // 当前视频没有对应格式返回第一个
currentDecodeFormats = supportDecodeFormats bool flag = false;
.contains(currentDecodeFormats) for (var i in supportDecodeFormats) {
if (i.startsWith(currentDecodeFormats.code)) {
flag = true;
}
}
currentDecodeFormats = flag
? currentDecodeFormats ? currentDecodeFormats
: VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!; : VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
} catch (e) { } catch (e) {
@@ -297,13 +293,6 @@ class VideoDetailController extends GetxController
} }
defaultST = Duration(milliseconds: data.lastPlayTime!); defaultST = Duration(milliseconds: data.lastPlayTime!);
await playerInit(); await playerInit();
// await playerInit(
// firstVideo,
// audioUrl,
// defaultST: Duration(milliseconds: data.lastPlayTime!),
// duration: data.timeLength ?? 0,
// );
} else { } else {
if (result['code'] == -404) { if (result['code'] == -404) {
isShowCover.value = false; isShowCover.value = false;

View File

@@ -602,9 +602,26 @@ InlineSpan buildContent(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () => Get.toNamed('/searchResult', parameters: { ..onTap = () {
'keyword': content.jumpUrl[matchStr]['title'] String appUrlSchema =
}), content.jumpUrl[matchStr]['app_url_schema'];
if (appUrlSchema == '') {
Get.toNamed(
'/webview',
parameters: {
'url': matchStr,
'type': 'url',
'pageTitle': ''
},
);
} else {
if (appUrlSchema.startsWith('bilibili://search')) {
Get.toNamed('/searchResult', parameters: {
'keyword': content.jumpUrl[matchStr]['title']
});
}
}
},
), ),
); );
spanChilds.add( spanChilds.add(

View File

@@ -149,7 +149,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
key: videoDetailController.scaffoldKey, key: videoDetailController.scaffoldKey,
// fix 1px black line // fix 1px black line
// backgroundColor: Colors.transparent, // backgroundColor: Colors.transparent,
backgroundColor: Theme.of(context).colorScheme.background, backgroundColor: Colors.black,
body: ExtendedNestedScrollView( body: ExtendedNestedScrollView(
controller: _extendNestCtr, controller: _extendNestCtr,
headerSliverBuilder: headerSliverBuilder:
@@ -162,11 +162,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
forceElevated: innerBoxIsScrolled, forceElevated: innerBoxIsScrolled,
expandedHeight: videoHeight, expandedHeight: videoHeight,
backgroundColor: backgroundColor: Colors.black,
MediaQuery.of(Get.context!).platformBrightness ==
Brightness.dark
? Colors.black
: Theme.of(context).colorScheme.background,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
background: Padding( background: Padding(
padding: EdgeInsets.only(top: statusBarHeight), padding: EdgeInsets.only(top: statusBarHeight),

View File

@@ -167,50 +167,46 @@ class _HeaderControlState extends State<HeaderControl> {
/// 选择倍速 /// 选择倍速
void showSetSpeedSheet() { void showSetSpeedSheet() {
showModalBottomSheet( double currentSpeed = widget.controller!.playbackSpeed;
context: context, SmartDialog.show(
elevation: 0, animationType: SmartAnimationType.centerFade_otherSlide,
backgroundColor: Colors.transparent, builder: (context) {
builder: (BuildContext context) { return AlertDialog(
return Container( title: const Text('播放速度'),
width: double.infinity, contentPadding: const EdgeInsets.fromLTRB(0, 20, 0, 20),
height: 450, content: StatefulBuilder(builder: (context, StateSetter setState) {
clipBehavior: Clip.hardEdge, return Column(
decoration: BoxDecoration( mainAxisSize: MainAxisSize.min,
color: Theme.of(context).colorScheme.background,
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
margin: const EdgeInsets.all(12),
child: Material(
child: ListView(
physics: const NeverScrollableScrollPhysics(),
children: [ children: [
const SizedBox( Text('$currentSpeed倍'),
height: 45, Slider(
child: Center( min: PlaySpeed.values.first.value,
child: Text('播放速度'), max: PlaySpeed.values.last.value,
), value: currentSpeed,
), divisions: PlaySpeed.values.length - 1,
for (var i in playSpeed) ...[ label: '${currentSpeed}x',
ListTile( onChanged: (double val) =>
onTap: () { {setState(() => currentSpeed = val)},
widget.controller!.setPlaybackSpeed(i.value); )
Get.back(result: {'playbackSpeed': i.value});
},
dense: true,
contentPadding: const EdgeInsets.only(left: 20, right: 20),
title: Text(i.description),
trailing: i.value == widget.controller!.playbackSpeed
? Icon(
Icons.done,
color: Theme.of(context).colorScheme.primary,
)
: null,
),
]
], ],
);
}),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
), ),
), TextButton(
onPressed: () async {
await SmartDialog.dismiss();
widget.controller!.setPlaybackSpeed(currentSpeed);
},
child: const Text('确定'),
),
],
); );
}, },
); );

View File

@@ -63,6 +63,13 @@ class WebviewController extends GetxController {
onWebResourceError: (WebResourceError error) {}, onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) { onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('bilibili://')) { if (request.url.startsWith('bilibili://')) {
if (request.url.startsWith('bilibili://video/')) {
String str = Uri.parse(request.url).pathSegments[0];
Get.offAndToNamed(
'/searchResult',
parameters: {'keyword': str},
);
}
return NavigationDecision.prevent; return NavigationDecision.prevent;
} }
return NavigationDecision.navigate; return NavigationDecision.navigate;

View File

@@ -2,11 +2,23 @@ enum PlaySpeed {
pointTwoFive, pointTwoFive,
pointFive, pointFive,
pointSevenFive, pointSevenFive,
one, one,
onePointTwoFive, onePointTwoFive,
onePointFive, onePointFive,
onePointSevenFive, onePointSevenFive,
two
two,
twoPointTwoFive,
twoPointFive,
twoPointSevenFive,
twhree,
threePointTwoFive,
threePointFive,
threePointSevenFive,
four,
} }
extension PlaySpeedExtension on PlaySpeed { extension PlaySpeedExtension on PlaySpeed {
@@ -17,8 +29,15 @@ extension PlaySpeedExtension on PlaySpeed {
'正常速度', '正常速度',
'1.25倍', '1.25倍',
'1.5倍', '1.5倍',
'1.75倍',
'2.0倍', '2.0倍',
'2.25倍',
'2.5倍',
'2.75倍',
'3.0倍',
'3.25倍',
'3.5倍',
'3.75倍',
'4.0倍'
]; ];
get description => _descList[index]; get description => _descList[index];
@@ -30,7 +49,15 @@ extension PlaySpeedExtension on PlaySpeed {
1.25, 1.25,
1.5, 1.5,
1.75, 1.75,
2.0 2.0,
2.25,
2.5,
2.75,
3.0,
3.25,
3.5,
3.75,
4.0,
]; ];
get value => _valueList[index]; get value => _valueList[index];
get defaultValue => _valueList[3]; get defaultValue => _valueList[3];

View File

@@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@@ -48,12 +49,17 @@ Future<void> enterFullScreen() async {
//退出全屏显示 //退出全屏显示
Future<void> exitFullScreen() async { Future<void> exitFullScreen() async {
dynamic document; dynamic document;
late SystemUiMode mode = SystemUiMode.edgeToEdge;
try { try {
if (kIsWeb) { if (kIsWeb) {
document.exitFullscreen(); document.exitFullscreen();
} else if (Platform.isAndroid || Platform.isIOS) { } else if (Platform.isAndroid || Platform.isIOS) {
if (Platform.isAndroid &&
(await DeviceInfoPlugin().androidInfo).version.sdkInt < 29) {
mode = SystemUiMode.manual;
}
await SystemChrome.setEnabledSystemUIMode( await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual, mode,
overlays: SystemUiOverlay.values, overlays: SystemUiOverlay.values,
); );
await SystemChrome.setPreferredOrientations([]); await SystemChrome.setPreferredOrientations([]);

View File

@@ -199,22 +199,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
context: Get.context!, context: Get.context!,
useSafeArea: false, useSafeArea: false,
builder: (context) => Dialog.fullscreen( builder: (context) => Dialog.fullscreen(
child: Scaffold( backgroundColor: Colors.black,
backgroundColor: Colors.black, child: PLVideoPlayer(
appBar: AppBar( controller: _,
primary: false, headerControl: _.headerControl,
toolbarHeight: 0,
backgroundColor: Colors.black,
systemOverlayStyle: SystemUiOverlayStyle.light,
), ),
body: SafeArea(
bottom: false,
child: PLVideoPlayer(
controller: _,
headerControl: _.headerControl,
),
),
),
), ),
); );
if (result == null) { if (result == null) {

View File

@@ -30,12 +30,12 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
elevation: 0, elevation: 0,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
primary: false, primary: false,
toolbarHeight: 73, toolbarHeight: 85,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
titleSpacing: 14, titleSpacing: 14,
title: Column( title: Column(
children: [ children: [
const SizedBox(height: 23), const SizedBox(height: 17),
Obx( Obx(
() { () {
final int value = _.sliderPosition.value.inSeconds; final int value = _.sliderPosition.value.inSeconds;
@@ -45,7 +45,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
return Container(); return Container();
} }
return Padding( return Padding(
padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5), padding: const EdgeInsets.only(left: 7, right: 5, bottom: 6),
child: ProgressBar( child: ProgressBar(
progress: Duration(seconds: value), progress: Duration(seconds: value),
buffered: Duration(seconds: buffer), buffered: Duration(seconds: buffer),

106
lib/utils/app_scheme.dart Normal file
View File

@@ -0,0 +1,106 @@
import 'package:appscheme/appscheme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'id_utils.dart';
import 'utils.dart';
class PiliSchame {
static AppScheme appScheme = AppSchemeImpl.getInstance() as AppScheme;
static void init() async {
///
SchemeEntity? value = await appScheme.getInitScheme();
if (value != null) {
_routePush(value);
}
///
appScheme.getLatestScheme().then((value) {
if (value != null) {}
});
/// 注册从外部打开的Scheme监听信息 #
appScheme.registerSchemeListener().listen((event) {
if (event != null) {
_routePush(event);
}
});
}
/// 路由跳转
static void _routePush(value) async {
String scheme = value.scheme;
String host = value.host;
String path = value.path;
if (scheme == 'bilibili') {
// bilibili://root
if (host == 'root') {
Navigator.popUntil(Get.context!, (route) => route.isFirst);
}
// bilibili://space/{uid}
else if (host == 'space') {
var mid = path.split('/').last;
Get.toNamed(
'/member?mid=$mid',
arguments: {'face': null},
);
}
// bilibili://video/{aid}
else if (host == 'video') {
var pathQuery = path.split('/').last;
int aid = int.parse(pathQuery);
String bvid = IdUtils.av2bv(aid);
int cid = await SearchHttp.ab2c(bvid: bvid);
String heroTag = Utils.makeHeroTag(aid);
Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: {
'pic': null,
'heroTag': heroTag,
});
}
// bilibili://live/{roomid}
else if (host == 'live') {
var roomId = path.split('/').last;
Get.toNamed('/liveRoom?roomid=$roomId',
arguments: {'liveItem': null, 'heroTag': roomId.toString()});
}
// bilibili://bangumi/season/${ssid}
else if (host == 'bangumi') {
if (path.startsWith('/season')) {
SmartDialog.showLoading(msg: '获取中...');
try {
var seasonId = path.split('/').last;
var result = await SearchHttp.bangumiInfo(
seasonId: int.parse(seasonId), epId: null);
if (result['status']) {
var bangumiDetail = result['data'];
int cid = bangumiDetail.episodes!.first.cid;
String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid);
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: {
'pic': bangumiDetail.cover,
'heroTag': heroTag,
'videoType': SearchType.media_bangumi,
},
),
);
}
} catch (e) {
SmartDialog.showToast('失败:${e.toString()}');
}
}
}
}
}
}

View File

@@ -105,6 +105,7 @@ class SettingBoxKey {
static const String autoUpdate = 'autoUpdate'; static const String autoUpdate = 'autoUpdate';
static const String replySortType = 'replySortType'; static const String replySortType = 'replySortType';
static const String defaultDynamicType = 'defaultDynamicType'; static const String defaultDynamicType = 'defaultDynamicType';
static const String enableHotKey = 'enableHotKey';
/// 外观 /// 外观
static const String themeMode = 'themeMode'; static const String themeMode = 'themeMode';
@@ -112,6 +113,7 @@ class SettingBoxKey {
static const String dynamicColor = 'dynamicColor'; // bool static const String dynamicColor = 'dynamicColor'; // bool
static const String customColor = 'customColor'; // 自定义主题色 static const String customColor = 'customColor'; // 自定义主题色
static const String iosTransition = 'iosTransition'; // ios路由 static const String iosTransition = 'iosTransition'; // ios路由
static const String enableSingleRow = 'enableSingleRow'; // 首页单列
} }
class LocalCacheKey { class LocalCacheKey {

View File

@@ -25,6 +25,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.7" version: "2.0.7"
appscheme:
dependency: "direct main"
description:
name: appscheme
sha256: b885b65219f3839ebafc937024a1bc5ce5a75b0e458fd249ef15e80e81235b6f
url: "https://pub.dev"
source: hosted
version: "1.0.8"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@@ -713,20 +721,20 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit name: media_kit
sha256: "0a89e7037002a62701ec319c375586849f9ef8e681820e1dd4a4ff7b843f7542" sha256: "66f04934bcadf592f24d829127471e4dc304de8e9bba5795ade2f3e95552ebfc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.4+1" version: "1.1.6"
media_kit_libs_android_video: media_kit_libs_android_video:
dependency: "direct main" dependency: transitive
description: description:
name: media_kit_libs_android_video name: media_kit_libs_android_video
sha256: "142d389bf3efcf8469594a9c7a06a92fc25843fc6c0c3247f76cdcf70b3b29de" sha256: "498a5062bc5f000bd23ada3be788ea886ab32c52f7a8252dde1264ca019b819b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.3"
media_kit_libs_ios_video: media_kit_libs_ios_video:
dependency: "direct main" dependency: transitive
description: description:
name: media_kit_libs_ios_video name: media_kit_libs_ios_video
sha256: fed403dc9d54462e51ee80e0cb23c12a53fadea9a8fa18aca2de9054176d1159 sha256: fed403dc9d54462e51ee80e0cb23c12a53fadea9a8fa18aca2de9054176d1159
@@ -734,31 +742,39 @@ packages:
source: hosted source: hosted
version: "1.1.3" version: "1.1.3"
media_kit_libs_linux: media_kit_libs_linux:
dependency: "direct main" dependency: transitive
description: description:
name: media_kit_libs_linux name: media_kit_libs_linux
sha256: "570bf18ebbd1221caec082657468be05d180510385d3515ec38e0be44fdcc859" sha256: "3b7c272179639a914dc8a50bf8a3f2df0e9a503bd727c88fab499dbdf6cb1eb8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.2"
media_kit_libs_macos_video: media_kit_libs_macos_video:
dependency: "direct main" dependency: transitive
description: description:
name: media_kit_libs_macos_video name: media_kit_libs_macos_video
sha256: c06e831f3c22a45296d375788d9bc07871b448f8e9ec98d77b11e5e118a83fb2 sha256: c06e831f3c22a45296d375788d9bc07871b448f8e9ec98d77b11e5e118a83fb2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.3" version: "1.1.3"
media_kit_libs_windows_video: media_kit_libs_video:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit_libs_windows_video name: media_kit_libs_video
sha256: f33aabd8414470d99e2c91dd98d605e6a5f1c4b8082dd933c10951bc961b9124 sha256: "48c8ace458f340e6b930c89c48141ea727b80aa0878f7a01904d7d439865f162"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.7" version: "1.0.0"
media_kit_libs_windows_video:
dependency: transitive
description:
name: media_kit_libs_windows_video
sha256: "923f068344d7d200184e0aaa2597f3de6c05982a3b1f18035d842ab53f2a1350"
url: "https://pub.dev"
source: hosted
version: "1.0.8"
media_kit_native_event_loop: media_kit_native_event_loop:
dependency: "direct main" dependency: transitive
description: description:
name: media_kit_native_event_loop name: media_kit_native_event_loop
sha256: e37ce6fb5fa71b8cf513c6a6cd591367743a342a385e7da621a047dd8ef6f4a4 sha256: e37ce6fb5fa71b8cf513c6a6cd591367743a342a385e7da621a047dd8ef6f4a4
@@ -769,10 +785,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit_video name: media_kit_video
sha256: e7fcbe426d42a78ad6696f8f557adb9cbdc012177829026d04992cc106a1c815 sha256: "809a3797da7d49fad85f139555b352dd615f9d2da6ae9f1745c6978963491bae"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.5" version: "1.1.7"
meta: meta:
dependency: transitive dependency: transitive
description: description:

View File

@@ -79,26 +79,14 @@ dependencies:
flutter_smart_dialog: ^4.9.3+2 flutter_smart_dialog: ^4.9.3+2
# 下滑关闭 # 下滑关闭
dismissible_page: ^1.0.2 dismissible_page: ^1.0.2
# 媒体播放
# flutter_meedu_media_kit:
# path: /Users/rr/Desktop/code/flutter_meedu_media_kit/package
# git:
# url: https://github.com/guozhigq/flutter_meedu_media_kit.git
# ref: feature-custom
# path: package
custom_sliding_segmented_control: ^1.7.5 custom_sliding_segmented_control: ^1.7.5
# 加密 # 加密
crypto: ^3.0.3 crypto: ^3.0.3
# 视频播放器 # 视频播放器
media_kit: ^1.1.4 # Primary package. media_kit: ^1.1.6
media_kit_video: ^1.1.5 # For video rendering. media_kit_video: ^1.1.7
media_kit_native_event_loop: ^1.0.7 # Support for higher number of concurrent instances & better performance. media_kit_libs_video: ^1.0.0
media_kit_libs_android_video: ^1.3.2 # Android package for video native libraries.
media_kit_libs_ios_video: ^1.1.3 # iOS package for video native libraries.
media_kit_libs_macos_video: ^1.1.3 # macOS package for video native libraries.
media_kit_libs_windows_video: ^1.0.7 # Windows package for video native libraries.
media_kit_libs_linux: ^1.1.1
# 音量、亮度、屏幕控制 # 音量、亮度、屏幕控制
flutter_volume_controller: ^1.2.7 flutter_volume_controller: ^1.2.7
@@ -119,6 +107,8 @@ dependencies:
easy_debounce: ^2.0.3 easy_debounce: ^2.0.3
# 高帧率 # 高帧率
flutter_displaymode: ^0.6.0 flutter_displaymode: ^0.6.0
# scheme跳转
appscheme: ^1.0.8
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: