mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-13 12:50:10 +08:00
@@ -53,6 +53,7 @@ import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/storage_utils.dart';
|
||||
import 'package:PiliPlus/utils/subtitle_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:PiliPlus/utils/video_utils.dart';
|
||||
import 'package:battery_plus/battery_plus.dart';
|
||||
@@ -705,7 +706,7 @@ class HeaderControlState extends State<HeaderControl>
|
||||
if (!mounted) return;
|
||||
String sub = buffer.toString();
|
||||
sub = await compute<List, String>(
|
||||
VideoHttp.processList,
|
||||
SubtitleUtils.json2Vtt,
|
||||
jsonDecode(sub)['body'],
|
||||
);
|
||||
if (!mounted) return;
|
||||
@@ -1138,35 +1139,61 @@ class HeaderControlState extends State<HeaderControl>
|
||||
);
|
||||
}
|
||||
|
||||
Future<_SubtitleFormat?> _showFormatDialog() {
|
||||
return showDialog<_SubtitleFormat>(
|
||||
context: context,
|
||||
builder: (context) => SimpleDialog(
|
||||
title: const Text('选择格式'),
|
||||
children: [
|
||||
DialogOption(
|
||||
onPressed: () => Get.back(result: _SubtitleFormat.json),
|
||||
child: const Text('JSON'),
|
||||
),
|
||||
DialogOption(
|
||||
onPressed: () => Get.back(result: _SubtitleFormat.vtt),
|
||||
child: const Text('WEBVTT'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onExportSubtitle() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
SubtitleFormat format = .vtt;
|
||||
final subtitles = videoDetailCtr.subtitles;
|
||||
final secondary = ColorScheme.of(context).secondary;
|
||||
return SimpleDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
clipBehavior: .hardEdge,
|
||||
contentPadding: const .only(bottom: 12),
|
||||
titlePadding: const .fromLTRB(20, 20, 20, 12),
|
||||
title: const Text('保存字幕'),
|
||||
title: Row(
|
||||
children: [
|
||||
const Expanded(child: Text('保存字幕')),
|
||||
const Text('格式: ', style: TextStyle(fontSize: 14)),
|
||||
Builder(
|
||||
builder: (context) => PopupMenuButton<SubtitleFormat>(
|
||||
tooltip: '',
|
||||
initialValue: format,
|
||||
onSelected: (value) {
|
||||
format = value;
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
itemBuilder: (_) => SubtitleFormat.values
|
||||
.map(
|
||||
(e) => PopupMenuItem(
|
||||
value: e,
|
||||
height: 35,
|
||||
child: Text(e.label),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: Padding(
|
||||
padding: const .symmetric(horizontal: 2, vertical: 5),
|
||||
child: Text.rich(
|
||||
style: .new(fontSize: 14, color: secondary),
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: format.label),
|
||||
WidgetSpan(
|
||||
alignment: .middle,
|
||||
child: Icon(
|
||||
size: 14,
|
||||
MdiIcons.unfoldMoreHorizontal,
|
||||
color: secondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
children: List.generate(subtitles.length, (i) {
|
||||
final item = subtitles[i];
|
||||
return DialogOption(
|
||||
@@ -1174,27 +1201,33 @@ class HeaderControlState extends State<HeaderControl>
|
||||
Get.back();
|
||||
final url = item.subtitleUrl;
|
||||
if (url == null || url.isEmpty) return;
|
||||
final format = await _showFormatDialog();
|
||||
if (format == null) return;
|
||||
try {
|
||||
final Uint8List bytes;
|
||||
switch (format) {
|
||||
case .vtt:
|
||||
var subtitle = videoDetailCtr.vttSubtitles[i];
|
||||
case .vtt || .srt:
|
||||
var subtitle = format == .vtt
|
||||
? videoDetailCtr.vttSubtitles[i]?.id
|
||||
: null;
|
||||
if (subtitle == null) {
|
||||
final res = await VideoHttp.vttSubtitles(
|
||||
item.subtitleUrl!,
|
||||
format: format,
|
||||
);
|
||||
if (res == null) return;
|
||||
subtitle = (isData: true, id: res);
|
||||
videoDetailCtr.vttSubtitles[i] = subtitle;
|
||||
subtitle = res;
|
||||
if (format == .vtt) {
|
||||
videoDetailCtr.vttSubtitles[i] = (
|
||||
isData: true,
|
||||
id: res,
|
||||
);
|
||||
}
|
||||
}
|
||||
bytes = utf8.encode(subtitle.id);
|
||||
bytes = utf8.encode(subtitle);
|
||||
case .json:
|
||||
final res = await Request.dio.get<Uint8List>(
|
||||
url.http2https,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
responseType: .bytes,
|
||||
headers: Constants.baseHeaders,
|
||||
extra: {'account': const NoAccount()},
|
||||
),
|
||||
@@ -1207,13 +1240,14 @@ class HeaderControlState extends State<HeaderControl>
|
||||
),
|
||||
);
|
||||
}
|
||||
String name =
|
||||
'${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.${format.name}';
|
||||
final videoDetail = introController.videoDetail.value;
|
||||
final name =
|
||||
'${videoDetail.title}-${videoDetail.owner?.name}(${videoDetail.owner?.mid})-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.${format.name}'
|
||||
.replaceAll(
|
||||
Platform.isWindows ? RegExp(r'[<>:/\\|?*"]') : '/',
|
||||
'_',
|
||||
);
|
||||
// Reserved characters may not be used in file names. See: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
|
||||
name = name.replaceAll(
|
||||
Platform.isWindows ? RegExp(r'[<>:/\\|?*"]') : '/',
|
||||
'_',
|
||||
);
|
||||
StorageUtils.saveBytes2File(
|
||||
name: name,
|
||||
bytes: bytes,
|
||||
@@ -1224,10 +1258,7 @@ class HeaderControlState extends State<HeaderControl>
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
item.lanDoc ?? item.lan,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
child: Text(item.lanDoc ?? item.lan),
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -2083,5 +2114,3 @@ class HeaderControlState extends State<HeaderControl>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum _SubtitleFormat { json, vtt }
|
||||
|
||||
Reference in New Issue
Block a user