mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
@@ -16,7 +16,6 @@ import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
@@ -30,11 +29,6 @@ class VideoCardV extends StatelessWidget {
|
||||
this.onRemove,
|
||||
});
|
||||
|
||||
bool isStringNumeric(String str) {
|
||||
RegExp numericRegex = RegExp(r'^\d+$');
|
||||
return numericRegex.hasMatch(str);
|
||||
}
|
||||
|
||||
Future<void> onPushDetail(String heroTag) async {
|
||||
String? goto = videoItem.goto;
|
||||
switch (goto) {
|
||||
@@ -58,31 +52,7 @@ class VideoCardV extends StatelessWidget {
|
||||
// 动态
|
||||
case 'picture':
|
||||
try {
|
||||
String type = 'picture';
|
||||
String uri = videoItem.uri!;
|
||||
String id = '';
|
||||
if (uri.startsWith('bilibili://article/')) {
|
||||
type = 'read';
|
||||
RegExp regex = RegExp(r'\d+');
|
||||
Match match = regex.firstMatch(uri)!;
|
||||
String matchedNumber = match.group(0)!;
|
||||
videoItem.param = int.parse(matchedNumber);
|
||||
id = '${videoItem.param}';
|
||||
}
|
||||
if (uri.startsWith('http')) {
|
||||
String id = Uri.parse(uri).path.split('/')[1];
|
||||
if (isStringNumeric(id)) {
|
||||
PageUtils.pushDynFromId(id: id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Get.toNamed(
|
||||
'/articlePage',
|
||||
parameters: {
|
||||
'id': id,
|
||||
'type': type,
|
||||
},
|
||||
);
|
||||
PiliScheme.routePushFromUrl(videoItem.uri!);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ class Request {
|
||||
static late AccountManager accountManager;
|
||||
static late final Dio dio;
|
||||
factory Request() => _instance;
|
||||
// static final _rand = Random();
|
||||
// static final RegExp _spmPrefixExp = RegExp(r'<meta name="spm_prefix" content="([^"]+?)">');
|
||||
|
||||
/// 设置cookie
|
||||
static Future<void> setCookie() async {
|
||||
|
||||
@@ -125,9 +125,8 @@ class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
|
||||
initialValue: filter,
|
||||
onChanged: (value) => filter = value,
|
||||
keyboardType: isUid ? TextInputType.number : null,
|
||||
inputFormatters: isUid
|
||||
? [FilteringTextInputFormatter.allow(RegExp(r'\d+'))]
|
||||
: null,
|
||||
inputFormatters:
|
||||
isUid ? [FilteringTextInputFormatter.digitsOnly] : null,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
@@ -342,9 +342,8 @@ class _LiveDmBlockPageState extends State<LiveDmBlockPage> {
|
||||
onChanged: (val) => value = val,
|
||||
decoration: isKeyword ? null : const InputDecoration(hintText: 'UID'),
|
||||
keyboardType: isKeyword ? null : TextInputType.number,
|
||||
inputFormatters: isKeyword
|
||||
? null
|
||||
: [FilteringTextInputFormatter.allow(RegExp(r'\d+'))],
|
||||
inputFormatters:
|
||||
isKeyword ? null : [FilteringTextInputFormatter.digitsOnly],
|
||||
),
|
||||
onConfirm: () {
|
||||
if (value.isNotEmpty) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||
import 'package:PiliPlus/pages/live_search/child/controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -35,11 +36,9 @@ class LiveSearchController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
late final regex = RegExp(r'^\d+$');
|
||||
|
||||
void submit() {
|
||||
if (editingController.text.isNotEmpty) {
|
||||
if (regex.hasMatch(editingController.text)) {
|
||||
if (IdUtils.digitOnlyRegExp.hasMatch(editingController.text)) {
|
||||
Get.toNamed('/liveRoom?roomid=${editingController.text}');
|
||||
} else {
|
||||
hasData.value = true;
|
||||
|
||||
@@ -141,46 +141,39 @@ class MemberVideoCtr
|
||||
}
|
||||
|
||||
Future<void> toViewPlayAll() async {
|
||||
if (episodicButton.value.text == '继续播放' &&
|
||||
episodicButton.value.uri?.isNotEmpty == true) {
|
||||
final params = Uri.parse(episodicButton.value.uri!).queryParameters;
|
||||
String? oid = params['oid'];
|
||||
if (oid != null) {
|
||||
var bvid = IdUtils.av2bv(int.parse(oid));
|
||||
var cid = await SearchHttp.ab2c(aid: oid, bvid: bvid);
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(oid),
|
||||
'sourceType': 'archive',
|
||||
'mediaId': seasonId ?? seriesId ?? mid,
|
||||
'oid': oid,
|
||||
'favTitle':
|
||||
'$username: ${title ?? episodicButton.value.text ?? '播放全部'}',
|
||||
if (seriesId == null) 'count': count.value,
|
||||
if (seasonId != null || seriesId != null)
|
||||
'mediaType': params['page_type'],
|
||||
'desc': params['desc'] == '1',
|
||||
'sortField': params['sort_field'],
|
||||
'isContinuePlaying': true,
|
||||
},
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadingState.value.isSuccess) {
|
||||
List<SpaceArchiveItem>? list = loadingState.value.data;
|
||||
|
||||
if (list.isNullOrEmpty) return;
|
||||
|
||||
if (episodicButton.value.text == '继续播放') {
|
||||
String? oid = RegExp(r'oid=(\d+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1);
|
||||
if (oid != null) {
|
||||
var bvid = IdUtils.av2bv(int.parse(oid));
|
||||
var cid = await SearchHttp.ab2c(aid: oid, bvid: bvid);
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(oid),
|
||||
'sourceType': 'archive',
|
||||
'mediaId': seasonId ?? seriesId ?? mid,
|
||||
'oid': oid,
|
||||
'favTitle':
|
||||
'$username: ${title ?? episodicButton.value.text ?? '播放全部'}',
|
||||
if (seriesId == null) 'count': count.value,
|
||||
if (seasonId != null || seriesId != null)
|
||||
'mediaType': RegExp(r'page_type=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1),
|
||||
'desc': RegExp(r'desc=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1) ==
|
||||
'1',
|
||||
'sortField': RegExp(r'sort_field=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1),
|
||||
'isContinuePlaying': true,
|
||||
},
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (SpaceArchiveItem element in list!) {
|
||||
if (element.cid == null) {
|
||||
continue;
|
||||
@@ -207,9 +200,8 @@ class MemberVideoCtr
|
||||
'$username: ${title ?? episodicButton.value.text ?? '播放全部'}',
|
||||
if (seriesId == null) 'count': count.value,
|
||||
if (seasonId != null || seriesId != null)
|
||||
'mediaType': RegExp(r'page_type=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1),
|
||||
'mediaType': Uri.parse(episodicButton.value.uri!)
|
||||
.queryParameters['page_type'],
|
||||
'desc': desc,
|
||||
if (type == ContributeType.video)
|
||||
'sortField': order.value == 'click' ? 2 : 1,
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:PiliPlus/http/search.dart';
|
||||
import 'package:PiliPlus/models/search/suggest.dart';
|
||||
import 'package:PiliPlus/models_new/search/search_rcmd/data.dart';
|
||||
import 'package:PiliPlus/models_new/search/search_trending/data.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -25,7 +26,6 @@ class SSearchController extends GetxController {
|
||||
|
||||
// uid
|
||||
final RxBool showUidBtn = false.obs;
|
||||
late final digitOnlyRegExp = RegExp(r'^\d+$');
|
||||
|
||||
// history
|
||||
final RxBool recordSearchHistory = Pref.recordSearchHistory.obs;
|
||||
@@ -82,7 +82,7 @@ class SSearchController extends GetxController {
|
||||
}
|
||||
|
||||
void validateUid() {
|
||||
showUidBtn.value = digitOnlyRegExp.hasMatch(controller.text);
|
||||
showUidBtn.value = IdUtils.digitOnlyRegExp.hasMatch(controller.text);
|
||||
}
|
||||
|
||||
void onChange(String value) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/models/common/search_type.dart';
|
||||
import 'package:PiliPlus/models/search/result.dart';
|
||||
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
|
||||
class SearchAllController
|
||||
extends SearchPanelController<SearchAllData, dynamic> {
|
||||
@@ -61,15 +62,14 @@ class SearchAllController
|
||||
}
|
||||
|
||||
void jump2Video() {
|
||||
if (RegExp(r'^av\d+$', caseSensitive: false).hasMatch(keyword)) {
|
||||
if (IdUtils.avRegexExact.hasMatch(keyword)) {
|
||||
hasJump2Video = true;
|
||||
PiliScheme.videoPush(
|
||||
int.parse(keyword.substring(2)),
|
||||
null,
|
||||
showDialog: false,
|
||||
);
|
||||
} else if (RegExp(r'^bv[a-z\d]{10}$', caseSensitive: false)
|
||||
.hasMatch(keyword)) {
|
||||
} else if (IdUtils.bvRegexExact.hasMatch(keyword)) {
|
||||
hasJump2Video = true;
|
||||
PiliScheme.videoPush(null, keyword, showDialog: false);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class SearchArticleController
|
||||
|
||||
void jump2Article() {
|
||||
String? cvid = RegExp(r'^cv(id)?(\d+)$', caseSensitive: false)
|
||||
.firstMatch(keyword)
|
||||
.matchAsPrefix(keyword)
|
||||
?.group(2);
|
||||
if (cvid != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -71,15 +72,14 @@ class SearchVideoController
|
||||
}
|
||||
|
||||
void jump2Video() {
|
||||
if (RegExp(r'^av\d+$', caseSensitive: false).hasMatch(keyword)) {
|
||||
if (IdUtils.avRegexExact.hasMatch(keyword)) {
|
||||
hasJump2Video = true;
|
||||
PiliScheme.videoPush(
|
||||
int.parse(keyword.substring(2)),
|
||||
null,
|
||||
showDialog: false,
|
||||
);
|
||||
} else if (RegExp(r'^bv[a-z\d]{10}$', caseSensitive: false)
|
||||
.hasMatch(keyword)) {
|
||||
} else if (IdUtils.bvRegexExact.hasMatch(keyword)) {
|
||||
hasJump2Video = true;
|
||||
PiliScheme.videoPush(null, keyword, showDialog: false);
|
||||
}
|
||||
|
||||
@@ -74,9 +74,7 @@ List<SettingsModel> get extraSettings => [
|
||||
onChanged: (value) {
|
||||
dynamicPeriod = int.tryParse(value) ?? 5;
|
||||
},
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'\d+')),
|
||||
],
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
decoration: const InputDecoration(suffixText: 'min'),
|
||||
),
|
||||
actions: [
|
||||
@@ -187,9 +185,7 @@ List<SettingsModel> get extraSettings => [
|
||||
onChanged: (value) {
|
||||
replyLengthLimit = value;
|
||||
},
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'\d+')),
|
||||
],
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
decoration: const InputDecoration(suffixText: '行'),
|
||||
),
|
||||
actions: [
|
||||
|
||||
@@ -184,9 +184,7 @@ SettingsModel getVideoFilterSelectModel({
|
||||
autofocus: true,
|
||||
onChanged: (value) => valueStr = value,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'\d+')),
|
||||
],
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
decoration: InputDecoration(suffixText: suffix),
|
||||
),
|
||||
actions: [
|
||||
|
||||
@@ -515,7 +515,6 @@ List<SettingsModel> get styleSettings => [
|
||||
title: '滑动动画弹簧参数',
|
||||
leading: const Icon(Icons.chrome_reader_mode_outlined),
|
||||
onTap: (setState) {
|
||||
final numberRegExp = RegExp(r'[\d\.]+');
|
||||
List<String> springDescription = CustomSpringDescription
|
||||
.springDescription
|
||||
.map((i) => i.toString())
|
||||
@@ -538,7 +537,7 @@ List<SettingsModel> get styleSettings => [
|
||||
springDescription[index] = value;
|
||||
},
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(numberRegExp)
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+'))
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
labelText: const [
|
||||
|
||||
@@ -632,56 +632,27 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
);
|
||||
}
|
||||
|
||||
static final RegExp urlRegExp = RegExp(
|
||||
'${Constants.urlRegex.pattern}|av\\d+|bv[a-z\\d]{10}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
InlineSpan buildContent(ThemeData theme, VideoDetailData content) {
|
||||
final List descV2 = content.descV2!;
|
||||
if (content.descV2.isNullOrEmpty) {
|
||||
return const TextSpan();
|
||||
}
|
||||
// type
|
||||
// 1 普通文本
|
||||
// 2 @用户
|
||||
final List<TextSpan> spanChildren = List.generate(descV2.length, (index) {
|
||||
final currentDesc = descV2[index];
|
||||
final List<TextSpan> spanChildren = content.descV2!.map((currentDesc) {
|
||||
switch (currentDesc.type) {
|
||||
case 1:
|
||||
final List<InlineSpan> spanChildren = <InlineSpan>[];
|
||||
final RegExp urlRegExp = RegExp(
|
||||
'${Constants.urlRegex.pattern}|av\\d+|bv[a-z\\d]{10}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
(currentDesc.rawText as String).splitMapJoin(
|
||||
urlRegExp,
|
||||
onMatch: (Match match) {
|
||||
String matchStr = match[0]!;
|
||||
if (RegExp(r'^av\d+$', caseSensitive: false).hasMatch(matchStr)) {
|
||||
try {
|
||||
int aid = int.parse(matchStr.substring(2));
|
||||
IdUtils.av2bv(aid);
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: matchStr,
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => PiliScheme.videoPush(aid, null),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
spanChildren.add(TextSpan(text: matchStr));
|
||||
}
|
||||
} else if (RegExp(r'^bv[a-z\d]{10}$', caseSensitive: false)
|
||||
.hasMatch(matchStr)) {
|
||||
try {
|
||||
IdUtils.bv2av(matchStr);
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: matchStr,
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => PiliScheme.videoPush(null, matchStr),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
spanChildren.add(TextSpan(text: matchStr));
|
||||
}
|
||||
} else {
|
||||
if (matchStr.toLowerCase().startsWith('http')) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: matchStr,
|
||||
@@ -696,6 +667,35 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (matchStr.startsWith('av')) {
|
||||
try {
|
||||
int aid = int.parse(matchStr.substring(2));
|
||||
IdUtils.av2bv(aid);
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: matchStr,
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => PiliScheme.videoPush(aid, null),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
spanChildren.add(TextSpan(text: matchStr));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
IdUtils.bv2av(matchStr);
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: matchStr,
|
||||
style: TextStyle(color: theme.colorScheme.primary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => PiliScheme.videoPush(null, matchStr),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
spanChildren.add(TextSpan(text: matchStr));
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
@@ -716,7 +716,7 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
default:
|
||||
return const TextSpan();
|
||||
}
|
||||
});
|
||||
}).toList();
|
||||
return TextSpan(children: spanChildren);
|
||||
}
|
||||
|
||||
|
||||
@@ -631,13 +631,12 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
.hasMatch(matchStr)) {
|
||||
UrlUtils.matchUrlPush(matchStr, '');
|
||||
} else {
|
||||
RegExpMatch? firstMatch = RegExp(
|
||||
RegExpMatch? match = RegExp(
|
||||
r'^cv(\d+)$|/read/cv(\d+)|note-app/view\?cvid=(\d+)',
|
||||
caseSensitive: false,
|
||||
).firstMatch(matchStr);
|
||||
String? cvid = firstMatch?.group(1) ??
|
||||
firstMatch?.group(2) ??
|
||||
firstMatch?.group(3);
|
||||
String? cvid =
|
||||
match?.group(1) ?? match?.group(2) ?? match?.group(3);
|
||||
if (cvid != null) {
|
||||
Get.toNamed(
|
||||
'/articlePage',
|
||||
|
||||
@@ -171,9 +171,7 @@ class _WebviewPageState extends State<WebviewPage> {
|
||||
callback: (args) async {
|
||||
WebUri? uri = await controller.getUrl();
|
||||
if (uri != null) {
|
||||
String? oid = RegExp(r'oid=(\d+)')
|
||||
.firstMatch(uri.toString())
|
||||
?.group(1);
|
||||
String? oid = uri.queryParameters['oid'];
|
||||
if (oid != null) {
|
||||
PiliScheme.videoPush(int.parse(oid), null);
|
||||
}
|
||||
|
||||
@@ -289,12 +289,9 @@ class ChatItem extends StatelessWidget {
|
||||
for (var i in content['sub_cards'])
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
RegExp bvRegex =
|
||||
RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false);
|
||||
Iterable<Match> matches = bvRegex.allMatches(i['jump_url']);
|
||||
if (matches.isNotEmpty) {
|
||||
Match match = matches.first;
|
||||
String bvid = match.group(0)!;
|
||||
String? bvid =
|
||||
IdUtils.bvRegex.firstMatch(i['jump_url'])?.group(0);
|
||||
if (bvid != null) {
|
||||
try {
|
||||
SmartDialog.showLoading();
|
||||
final int? cid = await SearchHttp.ab2c(bvid: bvid);
|
||||
@@ -612,15 +609,16 @@ class ChatItem extends StatelessWidget {
|
||||
content['content'].splitMapJoin(
|
||||
RegExp(r"\[[^\[\]]+\]"),
|
||||
onMatch: (Match match) {
|
||||
final String emojiKey = match[0]!;
|
||||
final size = emojiMap[emojiKey]!['size'];
|
||||
if (emojiMap.containsKey(emojiKey)) {
|
||||
final emojiKey = match[0]!;
|
||||
final emoji = emojiMap[emojiKey];
|
||||
if (emoji != null) {
|
||||
final size = emoji['size'];
|
||||
children.add(
|
||||
WidgetSpan(
|
||||
child: NetworkImgLayer(
|
||||
width: size,
|
||||
height: size,
|
||||
src: emojiMap[emojiKey]!['url'],
|
||||
src: emoji['url'],
|
||||
type: ImageType.emote,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -17,6 +17,7 @@ class PiliScheme {
|
||||
static late AppLinks appLinks;
|
||||
static StreamSubscription? listener;
|
||||
static final uriDigitRegExp = RegExp(r'/(\d+)');
|
||||
static final _prefixRegex = RegExp(r'^\S+://');
|
||||
|
||||
static void init() {
|
||||
// Register our protocol only on Windows platform
|
||||
@@ -40,7 +41,7 @@ class PiliScheme {
|
||||
try {
|
||||
if (url.startsWith('//')) {
|
||||
url = 'https:$url';
|
||||
} else if (!RegExp(r'^\S+://').hasMatch(url)) {
|
||||
} else if (!_prefixRegex.hasMatch(url)) {
|
||||
url = 'https://$url';
|
||||
}
|
||||
return routePush(
|
||||
@@ -160,9 +161,7 @@ class PiliScheme {
|
||||
// to video
|
||||
// bilibili://video/12345678?page=0&h5awaken=random
|
||||
String? aid = uriDigitRegExp.firstMatch(path)?.group(1);
|
||||
String? bvid = RegExp(r'/(BV[a-z\d]{10})', caseSensitive: false)
|
||||
.firstMatch(path)
|
||||
?.group(1);
|
||||
String? bvid = IdUtils.bvRegex.firstMatch(path)?.group(0);
|
||||
if (aid != null || bvid != null) {
|
||||
if (queryParameters['cid'] != null) {
|
||||
bvid ??= IdUtils.av2bv(int.parse(aid!));
|
||||
@@ -349,7 +348,7 @@ class PiliScheme {
|
||||
// bilibili://following/detail/832703053858603029 (dynId)
|
||||
// bilibili://following/detail/12345678?comment_root_id=654321\u0026comment_on=1
|
||||
String? cvid = RegExp(r'^/detail/cv(\d+)', caseSensitive: false)
|
||||
.firstMatch(path)
|
||||
.matchAsPrefix(path)
|
||||
?.group(1);
|
||||
if (cvid != null) {
|
||||
PageUtils.toDupNamed(
|
||||
@@ -473,12 +472,8 @@ class PiliScheme {
|
||||
parameters: parameters,
|
||||
);
|
||||
default:
|
||||
String? aid = RegExp(r'^av(\d+)', caseSensitive: false)
|
||||
.firstMatch(path)
|
||||
?.group(1);
|
||||
String? bvid = RegExp(r'^BV[a-z\d]{10}', caseSensitive: false)
|
||||
.firstMatch(path)
|
||||
?.group(0);
|
||||
String? aid = IdUtils.avRegexExact.matchAsPrefix(path)?.group(1);
|
||||
String? bvid = IdUtils.bvRegexExact.matchAsPrefix(path)?.group(0);
|
||||
if (aid != null || bvid != null) {
|
||||
videoPush(
|
||||
aid != null ? int.parse(aid) : null,
|
||||
@@ -621,9 +616,7 @@ class PiliScheme {
|
||||
.firstMatch(path)
|
||||
?.group(1);
|
||||
String? bvid = uri.queryParameters['bvid'] ??
|
||||
RegExp(r'/(BV[a-z\d]{10})', caseSensitive: false)
|
||||
.firstMatch(path)
|
||||
?.group(1);
|
||||
IdUtils.bvRegex.firstMatch(path)?.group(0);
|
||||
if (bvid != null) {
|
||||
if (mediaId != null) {
|
||||
final int? cid = await SearchHttp.ab2c(bvid: bvid);
|
||||
@@ -651,15 +644,11 @@ class PiliScheme {
|
||||
case 'bangumi':
|
||||
// www.bilibili.com/bangumi/play/ep{eid}?start_progress={offset}&thumb_up_dm_id={dmid}
|
||||
if (kDebugMode) debugPrint('番剧');
|
||||
String? id = RegExp(r'(ss|ep)\d+').firstMatch(path)?.group(0);
|
||||
if (id != null) {
|
||||
bool isSeason = id.startsWith('ss');
|
||||
id = id.substring(2);
|
||||
PageUtils.viewPgc(
|
||||
seasonId: isSeason ? id : null,
|
||||
epId: isSeason ? null : id,
|
||||
progress: uri.queryParameters['start_progress'],
|
||||
);
|
||||
bool hasMatch = PageUtils.viewPgcFromUri(
|
||||
path,
|
||||
progress: uri.queryParameters['start_progress'],
|
||||
);
|
||||
if (hasMatch) {
|
||||
return true;
|
||||
}
|
||||
launchURL();
|
||||
|
||||
@@ -15,8 +15,12 @@ class IdUtils {
|
||||
'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf';
|
||||
static final invData = {for (var (i, c) in data.codeUnits.indexed) c: i};
|
||||
|
||||
static final bvRegex = RegExp(r'bv(1[0-9A-Za-z]{9})', caseSensitive: false);
|
||||
static final bvRegex = RegExp(r'bv[0-9a-zA-Z]{10}', caseSensitive: false);
|
||||
static final bvRegexExact =
|
||||
RegExp(r'^bv[0-9a-zA-Z]{10}$', caseSensitive: false);
|
||||
static final avRegex = RegExp(r'av(\d+)', caseSensitive: false);
|
||||
static final avRegexExact = RegExp(r'^av(\d+)$', caseSensitive: false);
|
||||
static final digitOnlyRegExp = RegExp(r'^\d+$');
|
||||
|
||||
static void swap<T>(List<T> list, int idx1, int idx2) {
|
||||
final idx1Value = list[idx1];
|
||||
@@ -58,12 +62,12 @@ class IdUtils {
|
||||
if (input == null || input.isEmpty) {
|
||||
return result;
|
||||
}
|
||||
String? bvid = bvRegex.firstMatch(input)?.group(1);
|
||||
String? bvid = bvRegex.firstMatch(input)?.group(0);
|
||||
|
||||
late String? aid = avRegex.firstMatch(input)?.group(1);
|
||||
|
||||
if (bvid != null) {
|
||||
result['BV'] = 'BV$bvid';
|
||||
result['BV'] = bvid;
|
||||
} else if (aid != null) {
|
||||
result['AV'] = int.parse(aid);
|
||||
}
|
||||
|
||||
@@ -232,14 +232,13 @@ class ImageUtil {
|
||||
}
|
||||
}
|
||||
|
||||
static final regExp =
|
||||
static final _thumbRegex =
|
||||
RegExp(r'(@(\d+[a-z]_?)*)(\..*)?$', caseSensitive: false);
|
||||
|
||||
static String thumbnailUrl(String? src, [int? quality]) {
|
||||
if (src != null && quality != 100) {
|
||||
bool hasMatch = false;
|
||||
src = src.splitMapJoin(
|
||||
regExp,
|
||||
_thumbRegex,
|
||||
onMatch: (Match match) {
|
||||
hasMatch = true;
|
||||
String suffix = match.group(3) ?? '.webp';
|
||||
|
||||
@@ -102,9 +102,7 @@ class PageUtils {
|
||||
autofocus: true,
|
||||
onChanged: (value) => duration = value,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'\d+')),
|
||||
],
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
decoration: const InputDecoration(suffixText: 'min'),
|
||||
),
|
||||
actions: [
|
||||
@@ -414,24 +412,13 @@ class PageUtils {
|
||||
|
||||
/// 专栏文章查看
|
||||
case 'DYNAMIC_TYPE_ARTICLE':
|
||||
String? url = item.modules.moduleDynamic?.major?.opus?.jumpUrl;
|
||||
if (url != null) {
|
||||
if (url.contains('opus') || url.contains('read')) {
|
||||
RegExp digitRegExp = RegExp(r'\d+');
|
||||
Iterable<Match> matches = digitRegExp.allMatches(url);
|
||||
String number = matches.first.group(0)!;
|
||||
toDupNamed(
|
||||
'/articlePage',
|
||||
parameters: {
|
||||
'id': number,
|
||||
'type': url.split('//').last.split('/')[1],
|
||||
},
|
||||
);
|
||||
} else {
|
||||
handleWebview('https:$url');
|
||||
}
|
||||
}
|
||||
|
||||
toDupNamed(
|
||||
'/articlePage',
|
||||
parameters: {
|
||||
'id': item.idStr,
|
||||
'type': 'opus',
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'DYNAMIC_TYPE_PGC':
|
||||
if (kDebugMode) debugPrint('番剧');
|
||||
@@ -675,14 +662,16 @@ class PageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static bool viewPgcFromUri(String uri) {
|
||||
String? id = RegExp(r'(ep|ss)\d+').firstMatch(uri)?.group(0);
|
||||
if (id != null) {
|
||||
bool isSeason = id.startsWith('ss');
|
||||
id = id.substring(2);
|
||||
static final _pgcRegex = RegExp(r'(ep|ss)(\d+)');
|
||||
static bool viewPgcFromUri(String uri, {String? progress}) {
|
||||
RegExpMatch? match = _pgcRegex.firstMatch(uri);
|
||||
if (match != null) {
|
||||
bool isSeason = match.group(1) == 'ss';
|
||||
String id = match.group(2)!;
|
||||
viewPgc(
|
||||
seasonId: isSeason ? id : null,
|
||||
epId: isSeason ? null : id,
|
||||
progress: progress,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ class Utils {
|
||||
return absolutePaths.join(':');
|
||||
}
|
||||
|
||||
static final numericRegex = RegExp(r'^[\d\.]+$');
|
||||
static bool isStringNumeric(String str) {
|
||||
RegExp numericRegex = RegExp(r'^[\d\.]+$');
|
||||
return numericRegex.hasMatch(str);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user