Skip to content

Commit

Permalink
add bilibili extenal subtitles support
Browse files Browse the repository at this point in the history
  • Loading branch information
Predidit committed Mar 11, 2024
1 parent f6c46c0 commit 7f78c70
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 17 deletions.
34 changes: 28 additions & 6 deletions lib/pages/player/player_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ abstract class _PlayerController with Store {
late AudioItem firstAudio;
late String videoUrl;
late String audioUrl;
String subUrl = '';
String subContext = '';
late Duration defaultST;

late Player mediaPlayer;
Expand Down Expand Up @@ -105,6 +107,7 @@ abstract class _PlayerController with Store {
// Todo
videoSource: videoUrl,
audioSource: audioUrl,
subFiles: subUrl,
type: DataSourceType.network,
httpHeaders: {
'user-agent':
Expand Down Expand Up @@ -238,12 +241,13 @@ abstract class _PlayerController with Store {

// 字幕
if (dataSource.subFiles != '' && dataSource.subFiles != null) {
await pp.setProperty(
'sub-files',
UniversalPlatform.isWindows
? dataSource.subFiles!.replaceAll(';', '\\;')
: dataSource.subFiles!.replaceAll(':', '\\:'),
);
debugPrint('发现可用字幕, 尝试加载');
subContext = await VideoRequest.getSub(subUrl);
debugPrint('字幕转换并加载完成');
// await pp.setProperty(
// 'sub-files',
// subContext
// );
await pp.setProperty("subs-with-matching-audio", "no");
await pp.setProperty("sub-forced-only", "yes");
await pp.setProperty("blend-subtitles", "video");
Expand Down Expand Up @@ -273,6 +277,18 @@ abstract class _PlayerController with Store {
Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders),
play: false,
);

// 设定字幕
await mediaPlayer.setSubtitleTrack(SubtitleTrack.data(subContext));
// debugPrint('待设定字幕为 $subContext');
// await mediaPlayer.setSubtitleTrack(
// SubtitleTrack.uri(
// 'https://www.iandevlin.com/html5test/webvtt/upc-video-subtitles-en.vtt',
// title: 'English',
// language: 'en',
// ),
// );

// 音轨
// player.setAudioTrack(
// AudioTrack.uri(dataSource.audioSource!),
Expand All @@ -284,7 +300,13 @@ abstract class _PlayerController with Store {
//获得视频详细
Future queryVideoUrl() async {
var result = await VideoRequest.videoUrl(cid: cid, bvid: bvid);
try {
subUrl = await VideoRequest.subUrl(cid: cid, bvid: bvid);
} catch (e) {
debugPrint(e.toString());
}
debugPrint('已从服务器得到响应');
debugPrint('播放器加载字幕 $subUrl');
if (result['status']) {
data = result['data'];
debugPrint('响应合法');
Expand Down
28 changes: 27 additions & 1 deletion lib/pages/player/player_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,33 @@ class _PlayerItemState extends State<PlayerItem> {
? MediaQuery.of(context).size.width * 9.0 / 32.0
: MediaQuery.of(context).size.width * 9.0 / 16.0,
child: playerController.dataStatus == 'loaded'
? Video(controller: playerController.videoController)
? Video(
controller: playerController.videoController,
subtitleViewConfiguration: SubtitleViewConfiguration(
style: TextStyle(
color: Colors.pink, // 深粉色字体
fontSize: 48.0, // 较大的字号
background: Paint()..color = Colors.transparent, // 背景透明
decoration: TextDecoration.none, // 无下划线
fontWeight: FontWeight.bold, // 字体加粗
shadows: const [
// 显眼的包边
Shadow(
offset: Offset(1.0, 1.0),
blurRadius: 3.0,
color: Color.fromARGB(255, 255, 255, 255),
),
Shadow(
offset: Offset(-1.0, -1.0),
blurRadius: 3.0,
color: Color.fromARGB(125, 255, 255, 255),
),
],
),
textAlign: TextAlign.center,
padding: const EdgeInsets.all(24.0),
),
)
: SizedBox(
child: Center(
child: CircularProgressIndicator(),
Expand Down
5 changes: 4 additions & 1 deletion lib/request/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class Api {
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/videostream_url.md
static const String videoUrl = '/x/player/wbi/playurl';

// 字幕
static const String subUrl = '/x/player/v2';

// 视频详情
// 竖屏 https://api.bilibili.com/x/web-interface/view?aid=527403921
// https://api.bilibili.com/x/web-interface/view/detail 获取视频超详细信息(web端)
Expand Down Expand Up @@ -316,7 +319,7 @@ class Api {
'https://api.github.com/repos/Predidit/BiliNeo/releases/latest';

// 当前版本
static const String version = '1.0.0';
static const String version = '1.0.1';

static const sourceUrl = "https://github.com/Predidit/BiliNeo";

Expand Down
44 changes: 44 additions & 0 deletions lib/request/video.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,54 @@
import 'package:flutter/material.dart';
import 'dart:convert';

import 'api.dart';
import 'request.dart';
import 'package:hive/hive.dart';
import 'package:bilineo/utils/wbisign.dart';
import 'package:bilineo/pages/player/player_url.dart';
import 'package:bilineo/utils/utils.dart';

class VideoRequest {
// Todo 获取字幕
static Future subUrl({int? avid, String? bvid, required int cid}) async {
Map<String, dynamic> data = {
'cid': cid,
'bvid': bvid,
};
if (avid != null) {
data['avid'] = avid;
}
Map params = await WbiSign().makSign({
...data,
'fourk': 1,
'voice_balance': 1,
'gaia_source': 'pre-load',
'web_location': 1550101,
});
try {
var res = await Request().get(Api.subUrl, data: params);
Map<String, dynamic> jsonMap = json.decode(res.toString());
List<dynamic> subtitles = jsonMap['data']['subtitle']['subtitles'];
String subtitleUrl = subtitles.firstWhere(
(subtitle) => subtitle['lan'].startsWith('zh'),
orElse: () => null)['subtitle_url'];
subtitleUrl = 'https:' + subtitleUrl;
debugPrint(subtitleUrl);
return subtitleUrl;
} catch (e) {
debugPrint('查询字幕失败 ${e.toString()}');
return '';
}
}

static Future getSub(String subUrl) async{
var res = await Request().get(subUrl);
final jsonData = json.decode(res.toString());
final webvttString = Utils.jsonToWebVTT(jsonData);
// debugPrint(webvttString);
return webvttString;
}

static Future videoUrl(
{int? avid, String? bvid, required int cid, int? qn}) async {
Map<String, dynamic> data = {
Expand Down
34 changes: 26 additions & 8 deletions lib/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,10 @@ class Utils {
// 检查更新
static Future<bool> checkUpdata() async {
return true;
}
}

// 下载适用于当前系统的安装包
static Future matchVersion(data) async {

}
static Future matchVersion(data) async {}

static Future<String> latest() async {
var resp = await Dio().get<Map<String, dynamic>>(Api.latestApp);
Expand All @@ -242,7 +240,7 @@ class Utils {
} else {
throw resp.data?["message"];
}
}
}

// 时间戳转时间
static tampToSeektime(number) {
Expand Down Expand Up @@ -270,13 +268,33 @@ class Utils {
}

static List<int> generateRandomBytes(int minLength, int maxLength) {
return List<int>.generate(
random.nextInt(maxLength-minLength+1), (_) => random.nextInt(0x60) + 0x20
);
return List<int>.generate(random.nextInt(maxLength - minLength + 1),
(_) => random.nextInt(0x60) + 0x20);
}

static String base64EncodeRandomString(int minLength, int maxLength) {
List<int> randomBytes = generateRandomBytes(minLength, maxLength);
return base64.encode(randomBytes);
}

static String jsonToWebVTT(Map<String, dynamic> json) {
var webvttContent = 'WEBVTT FILE\n\n';
int i = 1;
for (var entry in json['body']) {
final startTime = formatTime(entry['from']);
final endTime = formatTime(entry['to']);
final content = entry['content'];
webvttContent +=
'${i.toString()}\n$startTime --> $endTime\n$content\n\n';
i = i + 1;
}
return webvttContent;
}

static String formatTime(double seconds) {
final hours = seconds ~/ 3600;
final minutes = (seconds % 3600) ~/ 60;
final secs = seconds % 60;
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${secs.toStringAsFixed(3).replaceAll('.', '.')}';
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
# 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.
version: 1.0.0+1
version: 1.0.1+1

environment:
sdk: '>=3.2.6 <4.0.0'
Expand Down

0 comments on commit 7f78c70

Please sign in to comment.