Compare commits
2 Commits
v1.0.23.05
...
fix-pip
Author | SHA1 | Date | |
---|---|---|---|
9e8e8f73c2 | |||
7902cef80f |
2
.github/workflows/beta_ci.yml
vendored
2
.github/workflows/beta_ci.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "x-main"
|
- "main"
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "**.md"
|
- "**.md"
|
||||||
- "**.txt"
|
- "**.txt"
|
||||||
|
@ -26,13 +26,13 @@
|
|||||||
Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[✓] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale
|
[✓] Flutter (Channel stable, 3.16.4, on macOS 14.1.2 23B92 darwin-arm64, locale
|
||||||
zh-Hans-CN)
|
zh-Hans-CN)
|
||||||
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||||
[✓] Xcode - develop for iOS and macOS (Xcode 15.1)
|
[✓] Xcode - develop for iOS and macOS (Xcode 15.1)
|
||||||
[✓] Chrome - develop for the web
|
[✓] Chrome - develop for the web
|
||||||
[✓] Android Studio (version 2022.3)
|
[✓] Android Studio (version 2022.3)
|
||||||
[✓] VS Code (version 1.87.2)
|
[✓] VS Code (version 1.85.1)
|
||||||
[✓] Connected device (3 available)
|
[✓] Connected device (3 available)
|
||||||
[✓] Network resources
|
[✓] Network resources
|
||||||
|
|
||||||
@ -44,9 +44,6 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码
|
|||||||
## 技术交流
|
## 技术交流
|
||||||
|
|
||||||
Telegram: https://t.me/+lm_oOVmF0RJiODk1
|
Telegram: https://t.me/+lm_oOVmF0RJiODk1
|
||||||
|
|
||||||
Tg Beta版本:@PiliPala_Beta
|
|
||||||
|
|
||||||
QQ频道: https://pd.qq.com/s/365esodk3
|
QQ频道: https://pd.qq.com/s/365esodk3
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
## 1.0.22
|
|
||||||
|
|
||||||
明天晚上或者后天晚上应该会更新
|
|
||||||
|
|
||||||
### 功能
|
|
||||||
+ 字幕
|
|
||||||
+ 全屏时选集
|
|
||||||
+ 动态转发
|
|
||||||
+ 评论视频并转发
|
|
||||||
+ 收藏夹删除
|
|
||||||
+ 合集显示封面
|
|
||||||
+ 底部导航栏编辑、排序功能
|
|
||||||
+ 历史记录进度条展示
|
|
||||||
+ 直播画质切换
|
|
||||||
+ 排行榜功能
|
|
||||||
+ 视频详情页推荐视频开关
|
|
||||||
+ 显示联合投稿up
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ 收藏夹个数错误
|
|
||||||
+ 封面保存权限问题
|
|
||||||
+ 合集最后1p未展示
|
|
||||||
+ up主页关注按钮触发灰屏
|
|
||||||
|
|
||||||
### 优化
|
|
||||||
+ 视频简介查看逻辑
|
|
||||||
|
|
||||||
更多更新日志可在Github上查看
|
|
||||||
问题反馈、功能建议请查看「关于」页面。
|
|
@ -1,15 +0,0 @@
|
|||||||
## 1.0.23
|
|
||||||
|
|
||||||
### 功能
|
|
||||||
+ 封面下载
|
|
||||||
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ 全屏问题
|
|
||||||
+ 视频播放器灰屏问题
|
|
||||||
+ 评论区点击区域问题
|
|
||||||
+ 动态详情跳转异常问题
|
|
||||||
|
|
||||||
|
|
||||||
更多更新日志可在Github上查看
|
|
||||||
问题反馈、功能建议请查看「关于」页面。
|
|
@ -34,7 +34,6 @@ void main() async {
|
|||||||
.then((_) async {
|
.then((_) async {
|
||||||
await GStrorage.init();
|
await GStrorage.init();
|
||||||
await setupServiceLocator();
|
await setupServiceLocator();
|
||||||
clearLogs();
|
|
||||||
Request();
|
Request();
|
||||||
await Request.setCookie();
|
await Request.setCookie();
|
||||||
RecommendFilter();
|
RecommendFilter();
|
||||||
|
@ -47,23 +47,18 @@ class Vip {
|
|||||||
this.status,
|
this.status,
|
||||||
this.dueDate,
|
this.dueDate,
|
||||||
this.label,
|
this.label,
|
||||||
this.nicknameColor,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
int? type;
|
int? type;
|
||||||
int? status;
|
int? status;
|
||||||
int? dueDate;
|
int? dueDate;
|
||||||
Map? label;
|
Map? label;
|
||||||
int? nicknameColor;
|
|
||||||
|
|
||||||
Vip.fromJson(Map<String, dynamic> json) {
|
Vip.fromJson(Map<String, dynamic> json) {
|
||||||
type = json['type'];
|
type = json['type'];
|
||||||
status = json['status'];
|
status = json['status'];
|
||||||
dueDate = json['due_date'];
|
dueDate = json['due_date'];
|
||||||
label = json['label'];
|
label = json['label'];
|
||||||
nicknameColor = json['nickname_color'] == ''
|
|
||||||
? null
|
|
||||||
: int.parse("0xFF${json['nickname_color'].replaceAll('#', '')}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,8 +281,8 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
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 != null && data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => Stack(
|
() => Stack(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
@ -302,14 +302,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.titleMedium!
|
.titleMedium!
|
||||||
.copyWith(
|
.copyWith(fontWeight: FontWeight.bold),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: _memberController.memberInfo.value
|
|
||||||
.vip!.nicknameColor !=
|
|
||||||
null
|
|
||||||
? Color(_memberController.memberInfo
|
|
||||||
.value.vip!.nicknameColor!)
|
|
||||||
: null),
|
|
||||||
)),
|
)),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
if (_memberController.memberInfo.value.sex == '女')
|
if (_memberController.memberInfo.value.sex == '女')
|
||||||
|
@ -18,32 +18,45 @@ class MemberSeasonsPanel extends StatelessWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
MemberSeasonsList item = data!.seasonsList![index];
|
MemberSeasonsList item = data!.seasonsList![index];
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: 12, right: 4),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
Padding(
|
||||||
onTap: () => Get.toNamed(
|
padding: const EdgeInsets.only(bottom: 12, left: 4),
|
||||||
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
|
child: Row(
|
||||||
title: Text(
|
children: [
|
||||||
item.meta!.name!,
|
Text(
|
||||||
maxLines: 1,
|
item.meta!.name!,
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines: 1,
|
||||||
style: Theme.of(context).textTheme.titleSmall!,
|
style: Theme.of(context).textTheme.titleSmall!,
|
||||||
),
|
),
|
||||||
dense: true,
|
const SizedBox(width: 10),
|
||||||
leading: PBadge(
|
PBadge(
|
||||||
stack: 'relative',
|
stack: 'relative',
|
||||||
size: 'small',
|
size: 'small',
|
||||||
text: item.meta!.total.toString(),
|
text: item.meta!.total.toString(),
|
||||||
),
|
),
|
||||||
trailing: const Icon(
|
const Spacer(),
|
||||||
Icons.arrow_forward,
|
SizedBox(
|
||||||
size: 20,
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () => Get.toNamed(
|
||||||
|
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.arrow_forward,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
|
@ -44,7 +44,6 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_tempValue = value as T;
|
_tempValue = value as T;
|
||||||
});
|
});
|
||||||
Navigator.pop(context, _tempValue);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -52,6 +51,19 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, _tempValue),
|
||||||
|
child: const Text('确定'),
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ class VideoDetailController extends GetxController
|
|||||||
late bool enableCDN;
|
late bool enableCDN;
|
||||||
late int? cacheVideoQa;
|
late int? cacheVideoQa;
|
||||||
late String cacheDecode;
|
late String cacheDecode;
|
||||||
late int defaultAudioQa;
|
late int cacheAudioQa;
|
||||||
|
|
||||||
PersistentBottomSheetController? replyReplyBottomSheetCtr;
|
PersistentBottomSheetController? replyReplyBottomSheetCtr;
|
||||||
RxList<SubTitileContentModel> subtitleContents =
|
RxList<SubTitileContentModel> subtitleContents =
|
||||||
@ -146,7 +146,7 @@ class VideoDetailController extends GetxController
|
|||||||
// 预设的解码格式
|
// 预设的解码格式
|
||||||
cacheDecode = setting.get(SettingBoxKey.defaultDecode,
|
cacheDecode = setting.get(SettingBoxKey.defaultDecode,
|
||||||
defaultValue: VideoDecodeFormats.values.last.code);
|
defaultValue: VideoDecodeFormats.values.last.code);
|
||||||
defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
||||||
defaultValue: AudioQuality.hiRes.code);
|
defaultValue: AudioQuality.hiRes.code);
|
||||||
oid.value = IdUtils.bv2av(Get.parameters['bvid']!);
|
oid.value = IdUtils.bv2av(Get.parameters['bvid']!);
|
||||||
getSubtitle();
|
getSubtitle();
|
||||||
@ -353,9 +353,9 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
if (audiosList.isNotEmpty) {
|
if (audiosList.isNotEmpty) {
|
||||||
final List<int> numbers = audiosList.map((map) => map.id!).toList();
|
final List<int> numbers = audiosList.map((map) => map.id!).toList();
|
||||||
int closestNumber = Utils.findClosestNumber(defaultAudioQa, numbers);
|
int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
|
||||||
if (!numbers.contains(defaultAudioQa) &&
|
if (!numbers.contains(cacheAudioQa) &&
|
||||||
numbers.any((e) => e > defaultAudioQa)) {
|
numbers.any((e) => e > cacheAudioQa)) {
|
||||||
closestNumber = 30280;
|
closestNumber = 30280;
|
||||||
}
|
}
|
||||||
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber);
|
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber);
|
||||||
|
@ -525,18 +525,14 @@ InlineSpan buildContent(
|
|||||||
if (jumpUrlKeysList.isNotEmpty) {
|
if (jumpUrlKeysList.isNotEmpty) {
|
||||||
patternStr += '|${jumpUrlKeysList.join('|')}';
|
patternStr += '|${jumpUrlKeysList.join('|')}';
|
||||||
}
|
}
|
||||||
RegExp bv23Regex = RegExp(r'https://b23\.tv/[a-zA-Z0-9]{7}');
|
|
||||||
final RegExp pattern = RegExp(patternStr);
|
final RegExp pattern = RegExp(patternStr);
|
||||||
List<String> matchedStrs = [];
|
List<String> matchedStrs = [];
|
||||||
void addPlainTextSpan(str) {
|
void addPlainTextSpan(str) {
|
||||||
spanChilds.add(
|
spanChilds.add(TextSpan(
|
||||||
TextSpan(
|
|
||||||
text: str,
|
text: str,
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () =>
|
..onTap = () =>
|
||||||
replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem),
|
replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分割文本并处理每个部分
|
// 分割文本并处理每个部分
|
||||||
@ -738,36 +734,8 @@ InlineSpan buildContent(
|
|||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
onNonMatch: (String nonMatchStr) {
|
onNonMatch: (String nonMatchStr) {
|
||||||
return nonMatchStr.splitMapJoin(
|
addPlainTextSpan(nonMatchStr);
|
||||||
bv23Regex,
|
return nonMatchStr;
|
||||||
onMatch: (Match match) {
|
|
||||||
String matchStr = match[0]!;
|
|
||||||
spanChilds.add(
|
|
||||||
TextSpan(
|
|
||||||
text: ' $matchStr ',
|
|
||||||
style: isVideoPage
|
|
||||||
? TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () => Get.toNamed(
|
|
||||||
'/webview',
|
|
||||||
parameters: {
|
|
||||||
'url': matchStr,
|
|
||||||
'type': 'url',
|
|
||||||
'pageTitle': matchStr
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
onNonMatch: (String nonMatchOtherStr) {
|
|
||||||
addPlainTextSpan(nonMatchOtherStr);
|
|
||||||
return nonMatchOtherStr;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -142,10 +142,9 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double _keyboardHeight = EdgeInsets.fromViewPadding(
|
double keyboardHeight = EdgeInsets.fromViewPadding(
|
||||||
View.of(context).viewInsets, View.of(context).devicePixelRatio)
|
View.of(context).viewInsets, View.of(context).devicePixelRatio)
|
||||||
.bottom;
|
.bottom;
|
||||||
print('_keyboardHeight: $_keyboardHeight');
|
|
||||||
return Container(
|
return Container(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -236,11 +235,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: toolbarType == 'input'
|
height: toolbarType == 'input' ? keyboardHeight : emoteHeight,
|
||||||
? (_keyboardHeight > keyboardHeight
|
|
||||||
? _keyboardHeight
|
|
||||||
: keyboardHeight)
|
|
||||||
: emoteHeight,
|
|
||||||
child: EmotePanel(
|
child: EmotePanel(
|
||||||
onChoose: (package, emote) => onChooseEmote(package, emote),
|
onChoose: (package, emote) => onChooseEmote(package, emote),
|
||||||
),
|
),
|
||||||
|
@ -17,7 +17,6 @@ import 'package:pilipala/plugin/pl_player/index.dart';
|
|||||||
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/services/shutdown_timer_service.dart';
|
import 'package:pilipala/services/shutdown_timer_service.dart';
|
||||||
import '../../../../http/danmaku.dart';
|
|
||||||
import '../../../../models/common/search_type.dart';
|
import '../../../../models/common/search_type.dart';
|
||||||
import '../../../../models/video_detail_res.dart';
|
import '../../../../models/video_detail_res.dart';
|
||||||
import '../introduction/index.dart';
|
import '../introduction/index.dart';
|
||||||
@ -53,7 +52,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
final Box<dynamic> videoStorage = GStrorage.video;
|
final Box<dynamic> videoStorage = GStrorage.video;
|
||||||
late List<double> speedsList;
|
late List<double> speedsList;
|
||||||
double buttonSpace = 8;
|
double buttonSpace = 8;
|
||||||
RxBool isFullScreen = false.obs;
|
bool showTitle = false;
|
||||||
late String heroTag;
|
late String heroTag;
|
||||||
late VideoIntroController videoIntroController;
|
late VideoIntroController videoIntroController;
|
||||||
late VideoDetailData videoDetail;
|
late VideoDetailData videoDetail;
|
||||||
@ -70,8 +69,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fullScreenStatusListener() {
|
void fullScreenStatusListener() {
|
||||||
widget.videoDetailCtr!.plPlayerController.isFullScreen.listen((bool val) {
|
widget.videoDetailCtr!.plPlayerController.isFullScreen
|
||||||
isFullScreen.value = val;
|
.listen((bool isFullScreen) {
|
||||||
|
if (isFullScreen) {
|
||||||
|
showTitle = true;
|
||||||
|
} else {
|
||||||
|
showTitle = false;
|
||||||
|
}
|
||||||
|
|
||||||
/// TODO setState() called after dispose()
|
/// TODO setState() called after dispose()
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -214,87 +218,6 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 发送弹幕
|
|
||||||
void showShootDanmakuSheet() {
|
|
||||||
final TextEditingController textController = TextEditingController();
|
|
||||||
bool isSending = false; // 追踪是否正在发送
|
|
||||||
showDialog(
|
|
||||||
context: Get.context!,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
// TODO: 支持更多类型和颜色的弹幕
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('发送弹幕(测试)'),
|
|
||||||
content: StatefulBuilder(
|
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
|
||||||
return TextField(
|
|
||||||
controller: textController,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Get.back(),
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
StatefulBuilder(
|
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
|
||||||
return TextButton(
|
|
||||||
onPressed: isSending
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final String msg = textController.text;
|
|
||||||
if (msg.isEmpty) {
|
|
||||||
SmartDialog.showToast('弹幕内容不能为空');
|
|
||||||
return;
|
|
||||||
} else if (msg.length > 100) {
|
|
||||||
SmartDialog.showToast('弹幕内容不能超过100个字符');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
isSending = true; // 开始发送,更新状态
|
|
||||||
});
|
|
||||||
//修改按钮文字
|
|
||||||
final dynamic res = await DanmakaHttp.shootDanmaku(
|
|
||||||
oid: widget.videoDetailCtr!.cid.value,
|
|
||||||
msg: textController.text,
|
|
||||||
bvid: widget.videoDetailCtr!.bvid,
|
|
||||||
progress:
|
|
||||||
widget.controller!.position.value.inMilliseconds,
|
|
||||||
type: 1,
|
|
||||||
);
|
|
||||||
setState(() {
|
|
||||||
isSending = false; // 发送结束,更新状态
|
|
||||||
});
|
|
||||||
if (res['status']) {
|
|
||||||
SmartDialog.showToast('发送成功');
|
|
||||||
// 发送成功,自动预览该弹幕,避免重新请求
|
|
||||||
// TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现
|
|
||||||
widget.controller!.danmakuController!.addItems([
|
|
||||||
DanmakuItem(
|
|
||||||
msg,
|
|
||||||
color: Colors.white,
|
|
||||||
time: widget
|
|
||||||
.controller!.position.value.inMilliseconds,
|
|
||||||
type: DanmakuItemType.scroll,
|
|
||||||
isSend: true,
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
Get.back();
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast('发送失败,错误信息为${res['msg']}');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(isSending ? '发送中...' : '发送'),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 定时关闭
|
/// 定时关闭
|
||||||
void scheduleExit() async {
|
void scheduleExit() async {
|
||||||
const List<int> scheduleTimeChoices = [
|
const List<int> scheduleTimeChoices = [
|
||||||
@ -1156,7 +1079,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(width: buttonSpace),
|
SizedBox(width: buttonSpace),
|
||||||
if (isFullScreen.value &&
|
if (showTitle &&
|
||||||
isLandscape &&
|
isLandscape &&
|
||||||
widget.videoType == SearchType.video) ...[
|
widget.videoType == SearchType.video) ...[
|
||||||
Column(
|
Column(
|
||||||
@ -1208,43 +1131,6 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// ),
|
// ),
|
||||||
// fuc: () => _.screenshot(),
|
// fuc: () => _.screenshot(),
|
||||||
// ),
|
// ),
|
||||||
if (isFullScreen.value) ...[
|
|
||||||
SizedBox(
|
|
||||||
width: 56,
|
|
||||||
height: 34,
|
|
||||||
child: TextButton(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () => showShootDanmakuSheet(),
|
|
||||||
child: const Text(
|
|
||||||
'发弹幕',
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 34,
|
|
||||||
height: 34,
|
|
||||||
child: Obx(
|
|
||||||
() => IconButton(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
_.isOpenDanmu.value = !_.isOpenDanmu.value;
|
|
||||||
},
|
|
||||||
icon: Icon(
|
|
||||||
_.isOpenDanmu.value
|
|
||||||
? Icons.subtitles_outlined
|
|
||||||
: Icons.subtitles_off_outlined,
|
|
||||||
size: 19,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
SizedBox(width: buttonSpace),
|
SizedBox(width: buttonSpace),
|
||||||
if (Platform.isAndroid) ...<Widget>[
|
if (Platform.isAndroid) ...<Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -51,7 +51,7 @@ class Utils {
|
|||||||
}
|
}
|
||||||
if (time < 3600) {
|
if (time < 3600) {
|
||||||
if (time == 0) {
|
if (time == 0) {
|
||||||
return '00:00';
|
return time;
|
||||||
}
|
}
|
||||||
final int minute = time ~/ 60;
|
final int minute = time ~/ 60;
|
||||||
final double res = time / 60;
|
final double res = time / 60;
|
||||||
@ -208,35 +208,17 @@ class Utils {
|
|||||||
|
|
||||||
static int findClosestNumber(int target, List<int> numbers) {
|
static int findClosestNumber(int target, List<int> numbers) {
|
||||||
int minDiff = 127;
|
int minDiff = 127;
|
||||||
int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数
|
late int closestNumber;
|
||||||
|
|
||||||
// 向下查找
|
|
||||||
try {
|
try {
|
||||||
for (int number in numbers) {
|
for (int number in numbers) {
|
||||||
if (number < target) {
|
int diff = (number - target).abs();
|
||||||
int diff = target - number; // 计算目标值与当前整数的差值
|
|
||||||
|
|
||||||
if (diff < minDiff) {
|
if (diff < minDiff) {
|
||||||
minDiff = diff;
|
minDiff = diff;
|
||||||
closestNumber = number;
|
closestNumber = number;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
// 向上查找
|
|
||||||
if (closestNumber == 0) {
|
|
||||||
try {
|
|
||||||
for (int number in numbers) {
|
|
||||||
int diff = (number - target).abs();
|
|
||||||
|
|
||||||
if (diff < minDiff) {
|
|
||||||
minDiff = diff;
|
|
||||||
closestNumber = number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
return closestNumber;
|
return closestNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.23+1023
|
version: 1.0.21+1021
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
Reference in New Issue
Block a user