mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-20 10:30:13 +08:00
feat: export vtt
This commit is contained in:
@@ -1147,61 +1147,99 @@ class HeaderControlState extends State<HeaderControl>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<_SubtitleFormat?> _showFormatDialog() {
|
||||||
|
return showDialog<_SubtitleFormat>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => SimpleDialog(
|
||||||
|
title: const Text('选择格式'),
|
||||||
|
children: [
|
||||||
|
SimpleDialogOption(
|
||||||
|
onPressed: () => Get.back(result: _SubtitleFormat.json),
|
||||||
|
child: const Text('JSON'),
|
||||||
|
),
|
||||||
|
SimpleDialogOption(
|
||||||
|
onPressed: () => Get.back(result: _SubtitleFormat.vtt),
|
||||||
|
child: const Text('WEBVTT'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void onExportSubtitle() {
|
void onExportSubtitle() {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SimpleDialog(
|
builder: (context) {
|
||||||
clipBehavior: Clip.hardEdge,
|
final subtitles = videoDetailCtr.subtitles;
|
||||||
contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12),
|
return SimpleDialog(
|
||||||
title: const Text('保存字幕'),
|
clipBehavior: Clip.hardEdge,
|
||||||
children: videoDetailCtr.subtitles
|
contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12),
|
||||||
.map(
|
title: const Text('保存字幕'),
|
||||||
(item) => SimpleDialogOption(
|
children: List.generate(subtitles.length, (i) {
|
||||||
onPressed: () async {
|
final item = subtitles[i];
|
||||||
Get.back();
|
return SimpleDialogOption(
|
||||||
final url = item.subtitleUrl;
|
onPressed: () async {
|
||||||
if (url == null || url.isEmpty) return;
|
Get.back();
|
||||||
try {
|
final url = item.subtitleUrl;
|
||||||
final res = await Request.dio.get<Uint8List>(
|
if (url == null || url.isEmpty) return;
|
||||||
url.http2https,
|
final format = await _showFormatDialog();
|
||||||
options: Options(
|
if (format == null) return;
|
||||||
responseType: ResponseType.bytes,
|
try {
|
||||||
headers: Constants.baseHeaders,
|
final Uint8List bytes;
|
||||||
extra: {'account': const NoAccount()},
|
switch (format) {
|
||||||
),
|
case .vtt:
|
||||||
);
|
var subtitle = videoDetailCtr.vttSubtitles[i];
|
||||||
if (res.statusCode == 200) {
|
if (subtitle == null) {
|
||||||
final bytes = Uint8List.fromList(
|
final res = await VideoHttp.vttSubtitles(
|
||||||
|
item.subtitleUrl!,
|
||||||
|
);
|
||||||
|
if (res == null) return;
|
||||||
|
subtitle = (isData: true, id: res);
|
||||||
|
videoDetailCtr.vttSubtitles[i] = subtitle;
|
||||||
|
}
|
||||||
|
bytes = utf8.encode(subtitle.id);
|
||||||
|
case .json:
|
||||||
|
final res = await Request.dio.get<Uint8List>(
|
||||||
|
url.http2https,
|
||||||
|
options: Options(
|
||||||
|
responseType: ResponseType.bytes,
|
||||||
|
headers: Constants.baseHeaders,
|
||||||
|
extra: {'account': const NoAccount()},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (res.statusCode != 200) return;
|
||||||
|
bytes = Uint8List.fromList(
|
||||||
Request.responseBytesDecoder(
|
Request.responseBytesDecoder(
|
||||||
res.data!,
|
res.data!,
|
||||||
res.headers.map,
|
res.headers.map,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
String name =
|
|
||||||
'${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.json';
|
|
||||||
if (Platform.isWindows) {
|
|
||||||
// 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(
|
|
||||||
RegExp(r'[<>:/\\|?*"]'),
|
|
||||||
'',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
StorageUtils.saveBytes2File(
|
|
||||||
name: name,
|
|
||||||
bytes: bytes,
|
|
||||||
allowedExtensions: const ['json'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
Utils.reportError(e, s);
|
|
||||||
SmartDialog.showToast(e.toString());
|
|
||||||
}
|
}
|
||||||
},
|
String name =
|
||||||
child: Text(item.lanDoc!, style: const TextStyle(fontSize: 14)),
|
'${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.${format.name}';
|
||||||
|
// 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,
|
||||||
|
allowedExtensions: [format.name],
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
Utils.reportError(e, s);
|
||||||
|
SmartDialog.showToast(e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
item.lanDoc!,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
.toList(),
|
}),
|
||||||
),
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2055,3 +2093,5 @@ class HeaderControlState extends State<HeaderControl>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum _SubtitleFormat { json, vtt }
|
||||||
|
|||||||
Reference in New Issue
Block a user