Merge branch 'main' into feature-minePage
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:appscheme/appscheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -10,22 +11,29 @@ import 'url_utils.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
class PiliSchame {
|
||||
static late AppLinks appLinks;
|
||||
static AppScheme appScheme = AppSchemeImpl.getInstance()!;
|
||||
static Future<void> init() async {
|
||||
///
|
||||
final SchemeEntity? value = await appScheme.getInitScheme();
|
||||
if (value != null) {
|
||||
_routePush(value);
|
||||
}
|
||||
appLinks = AppLinks();
|
||||
appLinks.uriLinkStream.listen((Uri uri) {
|
||||
final String scheme = uri.scheme;
|
||||
if (RegExp(r'^pili', caseSensitive: false).hasMatch(scheme)) {
|
||||
piliScheme(uri);
|
||||
}
|
||||
});
|
||||
|
||||
appScheme.getInitScheme().then((SchemeEntity? value) {
|
||||
if (value != null) {
|
||||
_routePush(value);
|
||||
}
|
||||
});
|
||||
|
||||
/// 完整链接进入 b23.无效
|
||||
appScheme.getLatestScheme().then((SchemeEntity? value) {
|
||||
if (value != null) {
|
||||
_routePush(value);
|
||||
}
|
||||
});
|
||||
|
||||
/// 注册从外部打开的Scheme监听信息 #
|
||||
appScheme.registerSchemeListener().listen((SchemeEntity? event) {
|
||||
if (event != null) {
|
||||
_routePush(event);
|
||||
@ -36,88 +44,11 @@ class PiliSchame {
|
||||
/// 路由跳转
|
||||
static void _routePush(value) async {
|
||||
final String scheme = value.scheme;
|
||||
final String host = value.host;
|
||||
final String path = value.path;
|
||||
if (scheme == 'bilibili') {
|
||||
switch (host) {
|
||||
case 'root':
|
||||
Navigator.popUntil(
|
||||
Get.context!, (Route<dynamic> route) => route.isFirst);
|
||||
break;
|
||||
case 'space':
|
||||
final String mid = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/member?mid=$mid',
|
||||
arguments: <String, dynamic>{'face': null},
|
||||
);
|
||||
break;
|
||||
case 'video':
|
||||
String pathQuery = path.split('/').last;
|
||||
final numericRegex = RegExp(r'^[0-9]+$');
|
||||
if (numericRegex.hasMatch(pathQuery)) {
|
||||
pathQuery = 'AV$pathQuery';
|
||||
}
|
||||
Map map = IdUtils.matchAvorBv(input: pathQuery);
|
||||
if (map.containsKey('AV')) {
|
||||
_videoPush(map['AV'], null);
|
||||
} else if (map.containsKey('BV')) {
|
||||
_videoPush(null, map['BV']);
|
||||
} else {
|
||||
SmartDialog.showToast('投稿匹配失败');
|
||||
}
|
||||
break;
|
||||
case 'live':
|
||||
final String roomId = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/liveRoom?roomid=$roomId',
|
||||
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
|
||||
);
|
||||
break;
|
||||
case 'bangumi':
|
||||
if (path.startsWith('/season')) {
|
||||
final String seasonId = path.split('/').last;
|
||||
RoutePush.bangumiPush(int.parse(seasonId), null);
|
||||
}
|
||||
break;
|
||||
case 'opus':
|
||||
if (path.startsWith('/detail')) {
|
||||
var opusId = path.split('/').last;
|
||||
Get.toNamed('/opus', parameters: {
|
||||
'title': '',
|
||||
'id': opusId,
|
||||
'articleType': 'opus',
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'search':
|
||||
Get.toNamed('/searchResult', parameters: {'keyword': ''});
|
||||
break;
|
||||
case 'article':
|
||||
final String id = path.split('/').last.split('?').first;
|
||||
Get.toNamed(
|
||||
'/read',
|
||||
parameters: {
|
||||
'title': 'cv$id',
|
||||
'id': id,
|
||||
'dynamicType': 'read',
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'pgc':
|
||||
if (path.contains('ep')) {
|
||||
final String lastPathSegment = path.split('/').last;
|
||||
RoutePush.bangumiPush(
|
||||
null, int.parse(lastPathSegment.split('?').first));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SmartDialog.showToast('未匹配地址,请联系开发者');
|
||||
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
|
||||
break;
|
||||
}
|
||||
biliScheme(value);
|
||||
}
|
||||
if (scheme == 'https') {
|
||||
fullPathPush(value);
|
||||
httpsScheme(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +79,7 @@ class PiliSchame {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> fullPathPush(SchemeEntity value) async {
|
||||
static Future<void> httpsScheme(SchemeEntity value) async {
|
||||
// https://m.bilibili.com/bangumi/play/ss39708
|
||||
// https | m.bilibili.com | /bangumi/play/ss39708
|
||||
// final String scheme = value.scheme!;
|
||||
@ -175,6 +106,11 @@ class PiliSchame {
|
||||
if (lastPathSegment.contains('ep')) {
|
||||
RoutePush.bangumiPush(null, Utils.matchNum(lastPathSegment).first);
|
||||
}
|
||||
} else if (path.startsWith('/BV')) {
|
||||
final String bvid = path.split('?').first.split('/').last;
|
||||
_videoPush(null, bvid);
|
||||
} else if (path.startsWith('/av')) {
|
||||
_videoPush(Utils.matchNum(path.split('?').first).first, null);
|
||||
}
|
||||
} else if (host.contains('live')) {
|
||||
int roomId = int.parse(path!.split('/').last);
|
||||
@ -276,6 +212,140 @@ class PiliSchame {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> biliScheme(SchemeEntity value) async {
|
||||
final String host = value.host!;
|
||||
final String path = value.path!;
|
||||
switch (host) {
|
||||
case 'root':
|
||||
Navigator.popUntil(
|
||||
Get.context!, (Route<dynamic> route) => route.isFirst);
|
||||
break;
|
||||
case 'space':
|
||||
final String mid = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/member?mid=$mid',
|
||||
arguments: <String, dynamic>{'face': null},
|
||||
);
|
||||
break;
|
||||
case 'video':
|
||||
String pathQuery = path.split('/').last;
|
||||
final numericRegex = RegExp(r'^[0-9]+$');
|
||||
if (numericRegex.hasMatch(pathQuery)) {
|
||||
pathQuery = 'AV$pathQuery';
|
||||
}
|
||||
Map map = IdUtils.matchAvorBv(input: pathQuery);
|
||||
if (map.containsKey('AV')) {
|
||||
_videoPush(map['AV'], null);
|
||||
} else if (map.containsKey('BV')) {
|
||||
_videoPush(null, map['BV']);
|
||||
} else {
|
||||
SmartDialog.showToast('投稿匹配失败');
|
||||
}
|
||||
break;
|
||||
case 'live':
|
||||
final String roomId = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/liveRoom?roomid=$roomId',
|
||||
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
|
||||
);
|
||||
break;
|
||||
case 'bangumi':
|
||||
if (path.startsWith('/season')) {
|
||||
final String seasonId = path.split('/').last;
|
||||
RoutePush.bangumiPush(int.parse(seasonId), null);
|
||||
}
|
||||
break;
|
||||
case 'opus':
|
||||
if (path.startsWith('/detail')) {
|
||||
var opusId = path.split('/').last;
|
||||
Get.toNamed('/opus', parameters: {
|
||||
'title': '',
|
||||
'id': opusId,
|
||||
'articleType': 'opus',
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'search':
|
||||
Get.toNamed('/searchResult', parameters: {'keyword': ''});
|
||||
break;
|
||||
case 'article':
|
||||
final String id = path.split('/').last.split('?').first;
|
||||
Get.toNamed(
|
||||
'/read',
|
||||
parameters: {
|
||||
'title': 'cv$id',
|
||||
'id': id,
|
||||
'dynamicType': 'read',
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'pgc':
|
||||
if (path.contains('ep')) {
|
||||
final String lastPathSegment = path.split('/').last;
|
||||
RoutePush.bangumiPush(
|
||||
null, int.parse(lastPathSegment.split('?').first));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SmartDialog.showToast('未匹配地址,请联系开发者');
|
||||
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> piliScheme(Uri value) async {
|
||||
final String host = value.host;
|
||||
final String path = value.path;
|
||||
final String arg = path.split('/').last;
|
||||
switch (host) {
|
||||
case 'home':
|
||||
case 'root':
|
||||
Get.toNamed('/');
|
||||
break;
|
||||
case 'member':
|
||||
if (arg != '') {
|
||||
final int? mid = int.tryParse(arg);
|
||||
if (mid == null) {
|
||||
SmartDialog.showToast('用户id有误');
|
||||
return;
|
||||
}
|
||||
Get.toNamed<dynamic>(
|
||||
'/member?mid=$mid',
|
||||
arguments: <String, dynamic>{'face': null},
|
||||
);
|
||||
} else {
|
||||
Get.toNamed('/mine');
|
||||
}
|
||||
break;
|
||||
case 'search':
|
||||
if (arg != '') {
|
||||
final String encodedArg = Uri.decodeComponent(arg);
|
||||
Get.toNamed('/searchResult', parameters: {'keyword': encodedArg});
|
||||
} else {
|
||||
Get.toNamed('/search');
|
||||
}
|
||||
break;
|
||||
case 'setting':
|
||||
Get.toNamed('/setting');
|
||||
break;
|
||||
case 'fav':
|
||||
Get.toNamed('/fav');
|
||||
break;
|
||||
case 'history':
|
||||
Get.toNamed('/history');
|
||||
break;
|
||||
case 'later':
|
||||
Get.toNamed('/later');
|
||||
break;
|
||||
case 'msg':
|
||||
Get.toNamed('/whisper');
|
||||
break;
|
||||
default:
|
||||
Get.toNamed('/');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _handleEpisodePath(String lastPathSegment, String redirectUrl) {
|
||||
final String seasonId = _extractIdFromPath(lastPathSegment);
|
||||
RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
@ -50,13 +51,17 @@ class CacheManage {
|
||||
Future<double> getTotalSizeOfFilesInDir(final FileSystemEntity file) async {
|
||||
if (file is File) {
|
||||
int length = await file.length();
|
||||
return double.parse(length.toString());
|
||||
return length.toDouble();
|
||||
}
|
||||
if (file is Directory) {
|
||||
final List<FileSystemEntity> children = file.listSync();
|
||||
double total = 0;
|
||||
for (final FileSystemEntity child in children) {
|
||||
total += await getTotalSizeOfFilesInDir(child);
|
||||
try {
|
||||
await for (final FileSystemEntity child in file.list()) {
|
||||
total += await getTotalSizeOfFilesInDir(child);
|
||||
}
|
||||
} catch (e) {
|
||||
// 处理错误,例如记录日志或显示错误消息
|
||||
print('读取目录时出错: $e');
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@ -77,16 +82,17 @@ class CacheManage {
|
||||
|
||||
// 清除缓存
|
||||
Future<bool> clearCacheAll() async {
|
||||
bool cleanStatus = await SmartDialog.show(
|
||||
useSystem: true,
|
||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||
bool? cleanStatus = await showDialog<bool>(
|
||||
context: Get.context!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: (() => {SmartDialog.dismiss()}),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
@ -94,40 +100,45 @@ class CacheManage {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
SmartDialog.dismiss();
|
||||
SmartDialog.showLoading(msg: '正在清除...');
|
||||
try {
|
||||
// 清除缓存 图片缓存
|
||||
await clearLibraryCache();
|
||||
SmartDialog.dismiss().then((res) {
|
||||
SmartDialog.showToast('清除完成');
|
||||
});
|
||||
} catch (err) {
|
||||
SmartDialog.dismiss();
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: const Text('确认'),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).then((res) {
|
||||
return true;
|
||||
});
|
||||
return cleanStatus;
|
||||
);
|
||||
if (cleanStatus != null && cleanStatus) {
|
||||
SmartDialog.showLoading(msg: '正在清除...');
|
||||
try {
|
||||
// 清除缓存 图片缓存
|
||||
await clearLibraryCache();
|
||||
SmartDialog.dismiss().then((res) {
|
||||
SmartDialog.showToast('清除完成');
|
||||
});
|
||||
} catch (err) {
|
||||
SmartDialog.dismiss();
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
}
|
||||
return cleanStatus!;
|
||||
}
|
||||
|
||||
/// 清除 Documents 目录下的 DioCache.db
|
||||
Future clearApplicationCache() async {
|
||||
Directory directory = await getApplicationDocumentsDirectory();
|
||||
if (directory.existsSync()) {
|
||||
String dioCacheFileName =
|
||||
'${directory.path}${Platform.pathSeparator}DioCache.db';
|
||||
var dioCacheFile = File(dioCacheFileName);
|
||||
if (dioCacheFile.existsSync()) {
|
||||
dioCacheFile.delete();
|
||||
Future<void> clearApplicationCache() async {
|
||||
try {
|
||||
Directory directory = await getApplicationDocumentsDirectory();
|
||||
if (directory.existsSync()) {
|
||||
String dioCacheFileName =
|
||||
'${directory.path}${Platform.pathSeparator}DioCache.db';
|
||||
File dioCacheFile = File(dioCacheFileName);
|
||||
if (await dioCacheFile.exists()) {
|
||||
await dioCacheFile.delete();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 处理错误,例如记录日志或显示错误消息
|
||||
print('清除缓存时出错: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ class GlobalDataCache {
|
||||
late FullScreenGestureMode fullScreenGestureMode;
|
||||
late bool enablePlayerControlAnimation;
|
||||
late List<String> actionTypeSort;
|
||||
String? wWebid;
|
||||
|
||||
/// 播放器相关
|
||||
// 弹幕开关
|
||||
@ -59,7 +60,7 @@ class GlobalDataCache {
|
||||
defaultValue: 10); // 设置全局变量
|
||||
fullScreenGestureMode = FullScreenGestureMode.values[setting.get(
|
||||
SettingBoxKey.fullScreenGestureMode,
|
||||
defaultValue: FullScreenGestureMode.values.last.index) as int];
|
||||
defaultValue: FullScreenGestureMode.fromBottomtoTop.index)];
|
||||
enablePlayerControlAnimation = setting
|
||||
.get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true);
|
||||
actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort,
|
||||
|
||||
@ -1,21 +1,51 @@
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
class SubTitleUtils {
|
||||
// 格式整理
|
||||
static String convertToWebVTT(List jsonData) {
|
||||
String webVTTContent = 'WEBVTT FILE\n\n';
|
||||
static Future<String> convertToWebVTT(List jsonData) async {
|
||||
final receivePort = ReceivePort();
|
||||
await Isolate.spawn(_convertToWebVTTIsolate, receivePort.sendPort);
|
||||
|
||||
for (int i = 0; i < jsonData.length; i++) {
|
||||
final item = jsonData[i];
|
||||
double from = double.parse(item['from'].toString());
|
||||
double to = double.parse(item['to'].toString());
|
||||
int sid = (item['sid'] ?? 0) as int;
|
||||
String content = item['content'] as String;
|
||||
final sendPort = await receivePort.first as SendPort;
|
||||
final response = ReceivePort();
|
||||
sendPort.send([jsonData, response.sendPort]);
|
||||
|
||||
webVTTContent += '$sid\n';
|
||||
webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n';
|
||||
webVTTContent += '$content\n\n';
|
||||
return await response.first as String;
|
||||
}
|
||||
|
||||
static void _convertToWebVTTIsolate(SendPort sendPort) async {
|
||||
final port = ReceivePort();
|
||||
sendPort.send(port.sendPort);
|
||||
|
||||
await for (final message in port) {
|
||||
final List jsonData = message[0];
|
||||
final SendPort replyTo = message[1];
|
||||
|
||||
String webVTTContent = 'WEBVTT FILE\n\n';
|
||||
int chunkSize = 100; // 每次处理100条数据
|
||||
int totalChunks = (jsonData.length / chunkSize).ceil();
|
||||
|
||||
for (int chunk = 0; chunk < totalChunks; chunk++) {
|
||||
int start = chunk * chunkSize;
|
||||
int end = start + chunkSize;
|
||||
if (end > jsonData.length) end = jsonData.length;
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
final item = jsonData[i];
|
||||
double from = double.parse(item['from'].toString());
|
||||
double to = double.parse(item['to'].toString());
|
||||
int sid = (item['sid'] ?? 0) as int;
|
||||
String content = item['content'] as String;
|
||||
|
||||
webVTTContent += '$sid\n';
|
||||
webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n';
|
||||
webVTTContent += '$content\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
replyTo.send(webVTTContent);
|
||||
}
|
||||
|
||||
return webVTTContent;
|
||||
}
|
||||
|
||||
static String formatTime(num seconds) {
|
||||
|
||||
@ -306,7 +306,7 @@ class Utils {
|
||||
onPressed: () async {
|
||||
await SmartDialog.dismiss();
|
||||
launchUrl(
|
||||
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
|
||||
Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user