opt: subtitle & video screenshot (#2364)

This commit is contained in:
My-Responsitories
2026-06-09 02:06:55 +00:00
committed by GitHub
parent 46d9f8ab1c
commit fe15024384
6 changed files with 68 additions and 77 deletions

View File

@@ -851,7 +851,7 @@ abstract final class VideoHttp {
..writeAll(
list.map(
(item) =>
'${item?['sid'] ?? 0}\n${_subtitleTimecode(item['from'])} --> ${_subtitleTimecode(item['to'])}\n${item['content'].trim()}',
'${_subtitleTimecode(item['from'])} --> ${_subtitleTimecode(item['to'])}\n${item['content'].trim()}',
),
'\n\n',
);

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'dart:math' show min;
import 'dart:ui';
@@ -54,12 +53,10 @@ import 'package:PiliPlus/services/download/download_service.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/connectivity_utils.dart';
import 'package:PiliPlus/utils/extension/context_ext.dart';
import 'package:PiliPlus/utils/extension/file_ext.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:PiliPlus/utils/extension/num_ext.dart';
import 'package:PiliPlus/utils/extension/size_ext.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/path_utils.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
@@ -75,7 +72,6 @@ import 'package:flutter_volume_controller/flutter_volume_controller.dart';
import 'package:get/get.dart';
import 'package:hive_ce/hive.dart';
import 'package:media_kit/media_kit.dart' hide Subtitle;
import 'package:path/path.dart' as path;
class VideoDetailController extends GetxController
with GetTickerProviderStateMixin, BlockMixin {
@@ -1081,19 +1077,8 @@ class VideoDetailController extends GetxController
final sub = subtitles[index - 1];
String subUri = subtitle.id;
File? file;
if (subtitle.isData) {
subUri = path.join(tmpDirPath, '${cid.value}-${sub.lan}.vtt');
file = File(subUri);
if (!file.existsSync()) {
await file.writeAsString(subtitle.id);
if (plPlayerController.videoPlayerController?.disposed == false) {
plPlayerController.videoPlayerController!.release.add(file.tryDel);
} else {
file.tryDel();
return;
}
}
subUri = 'memory://$subUri';
}
await plPlayerController.videoPlayerController?.setSubtitleTrack(
SubtitleTrack(subUri, sub.lanDoc, sub.lan, uri: true),

View File

@@ -350,12 +350,16 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
final res = await plPlayerController
.plPlayerController
.videoPlayerController
?.screenshot(format: .png);
?.screenshot();
if (res != null) {
final path =
'$tmpDirPath/${Utils.generateRandomString(8)}.png';
await File(path).writeAsBytes(res);
imageList.add(FilePicModel(path: path));
final png = await res.toByteData(format: .png);
if (png != null) {
final path =
'$tmpDirPath/${Utils.generateRandomString(8)}.png';
await File(path).writeAsBytes(png.buffer.asUint8List());
imageList.add(FilePicModel(path: path));
}
res.dispose();
} else {
debugPrint('null screenshot');
}

View File

@@ -1724,50 +1724,52 @@ class PlPlayerController with BlockConfigMixin {
videoShot = await VideoHttp.videoshot(bvid: bvid, cid: cid!);
}
void takeScreenshot() {
Future<void> takeScreenshot() async {
SmartDialog.showToast('截图中');
videoPlayerController?.screenshot(format: .png).then((value) {
if (value != null) {
SmartDialog.showToast('点击弹窗保存截图');
showDialog(
context: Get.context!,
builder: (context) => GestureDetector(
onTap: () {
Get.back();
final image = await videoPlayerController?.screenshot();
if (image != null) {
SmartDialog.showToast('点击弹窗保存截图');
showDialog(
context: Get.context!,
builder: (context) => GestureDetector(
onTap: () async {
final bytes = await image.toByteData(format: .png);
if (bytes != null) {
ImageUtils.saveByteImg(
bytes: value,
bytes: bytes.buffer.asUint8List(),
fileName: 'screenshot_${ImageUtils.time}',
);
},
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: min(DeviceUtils.size.width / 3, 350),
}
Get.back();
},
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: min(MediaQuery.widthOf(context) / 3, 350),
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: ThemeUtils.theme.colorScheme.surface,
),
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: ThemeUtils.theme.colorScheme.surface,
),
),
child: Padding(
padding: const EdgeInsets.all(5),
child: Image.memory(value),
),
child: Padding(
padding: const EdgeInsets.all(5),
child: RawImage(image: image),
),
),
),
),
),
);
} else {
SmartDialog.showToast('截图失败');
}
});
),
).whenComplete(image.dispose);
} else {
SmartDialog.showToast('截图失败');
}
}
void onPopInvokedWithResult(bool didPop, Object? result) {