feat: load file sub

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-11-10 11:22:07 +08:00
parent ffd4f9ee73
commit 963181fef2
5 changed files with 136 additions and 55 deletions

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
@@ -12,12 +13,14 @@ import 'package:PiliPlus/http/danmaku.dart';
import 'package:PiliPlus/http/danmaku_block.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/live.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/common/super_resolution_type.dart';
import 'package:PiliPlus/models/common/video/audio_quality.dart';
import 'package:PiliPlus/models/common/video/cdn_type.dart';
import 'package:PiliPlus/models/common/video/video_decode_type.dart';
import 'package:PiliPlus/models/common/video/video_quality.dart';
import 'package:PiliPlus/models/video/play/url.dart';
import 'package:PiliPlus/models_new/video/video_play_info/subtitle.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
import 'package:PiliPlus/pages/danmaku/dnamaku_model.dart';
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
@@ -43,6 +46,7 @@ import 'package:PiliPlus/utils/utils.dart';
import 'package:PiliPlus/utils/video_utils.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:dio/dio.dart';
import 'package:file_picker/file_picker.dart';
import 'package:floating/floating.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -592,17 +596,66 @@ class HeaderControlState extends State<HeaderControl> {
leading: const Icon(CustomIcons.dm_settings, size: 20),
title: const Text('弹幕设置', style: titleStyle),
),
if (!videoDetailCtr.isFileSource)
ListTile(
dense: true,
onTap: () {
Get.back();
showSetSubtitle();
},
leading: const Icon(Icons.subtitles_outlined, size: 20),
title: const Text('字幕设置', style: titleStyle),
),
if (videoDetailCtr.subtitles.isNotEmpty)
ListTile(
dense: true,
onTap: () {
Get.back();
showSetSubtitle();
},
leading: const Icon(Icons.subtitles_outlined, size: 20),
title: const Text('字幕设置', style: titleStyle),
),
ListTile(
dense: true,
onTap: () async {
Get.back();
try {
final FilePickerResult? file = await FilePicker.platform
.pickFiles();
if (file != null) {
final first = file.files.first;
final path = first.path;
if (path != null) {
final file = File(path);
final stream = file.openRead().transform(
utf8.decoder,
);
final buffer = StringBuffer();
await for (final chunk in stream) {
if (!mounted) return;
buffer.write(chunk);
}
if (!mounted) return;
String sub = buffer.toString();
final name = first.name;
if (name.endsWith('.json')) {
sub = await compute<List, String>(
VideoHttp.processList,
jsonDecode(sub)['body'],
);
if (!mounted) return;
}
final length = videoDetailCtr.subtitles.length;
videoDetailCtr
..subtitles.add(
Subtitle(
lan: '',
lanDoc: name.split('.').firstOrNull ?? name,
),
)
..vttSubtitles[length] = sub
..setSubtitle(length + 1);
}
}
} catch (e) {
SmartDialog.showToast('加载失败: $e');
}
},
leading: const Icon(Icons.file_open_outlined, size: 20),
title: const Text('加载字幕', style: titleStyle),
),
if (!videoDetailCtr.isFileSource &&
videoDetailCtr.subtitles.isNotEmpty)
ListTile(
dense: true,
onTap: () {
@@ -1065,9 +1118,11 @@ class HeaderControlState extends State<HeaderControl> {
dense: true,
onTap: () async {
Get.back();
final url = item.subtitleUrl;
if (url == null || url.isEmpty) return;
try {
final res = await Request.dio.get<Uint8List>(
item.subtitleUrl!.http2https,
url.http2https,
options: Options(
responseType: ResponseType.bytes,
headers: Constants.baseHeaders,