diff --git a/lib/common/widgets/video_card/video_card_v.dart b/lib/common/widgets/video_card/video_card_v.dart index 2443a30e2..1a4d2d373 100644 --- a/lib/common/widgets/video_card/video_card_v.dart +++ b/lib/common/widgets/video_card/video_card_v.dart @@ -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 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()); } diff --git a/lib/http/init.dart b/lib/http/init.dart index d53c69310..f73a73976 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -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''); /// 设置cookie static Future setCookie() async { diff --git a/lib/pages/danmaku_block/view.dart b/lib/pages/danmaku_block/view.dart index 1d43952b5..d688bca5d 100644 --- a/lib/pages/danmaku_block/view.dart +++ b/lib/pages/danmaku_block/view.dart @@ -125,9 +125,8 @@ class _DanmakuBlockPageState extends State { initialValue: filter, onChanged: (value) => filter = value, keyboardType: isUid ? TextInputType.number : null, - inputFormatters: isUid - ? [FilteringTextInputFormatter.allow(RegExp(r'\d+'))] - : null, + inputFormatters: + isUid ? [FilteringTextInputFormatter.digitsOnly] : null, ) ], ), diff --git a/lib/pages/live_dm_block/view.dart b/lib/pages/live_dm_block/view.dart index 664116dad..fe0c396b8 100644 --- a/lib/pages/live_dm_block/view.dart +++ b/lib/pages/live_dm_block/view.dart @@ -342,9 +342,8 @@ class _LiveDmBlockPageState extends State { 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) { diff --git a/lib/pages/live_search/controller.dart b/lib/pages/live_search/controller.dart index 64bd2c8af..5dd3d154f 100644 --- a/lib/pages/live_search/controller.dart +++ b/lib/pages/live_search/controller.dart @@ -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; diff --git a/lib/pages/member_video/controller.dart b/lib/pages/member_video/controller.dart index 138286af3..04cc7aa8a 100644 --- a/lib/pages/member_video/controller.dart +++ b/lib/pages/member_video/controller.dart @@ -141,46 +141,39 @@ class MemberVideoCtr } Future 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? 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, diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart index dd8caa75b..83234e7bd 100644 --- a/lib/pages/search/controller.dart +++ b/lib/pages/search/controller.dart @@ -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) { diff --git a/lib/pages/search_panel/all/controller.dart b/lib/pages/search_panel/all/controller.dart index 8777ea16e..feb63a527 100644 --- a/lib/pages/search_panel/all/controller.dart +++ b/lib/pages/search_panel/all/controller.dart @@ -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 { @@ -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); } diff --git a/lib/pages/search_panel/article/controller.dart b/lib/pages/search_panel/article/controller.dart index 9bfeee4d3..429b3779b 100644 --- a/lib/pages/search_panel/article/controller.dart +++ b/lib/pages/search_panel/article/controller.dart @@ -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((_) { diff --git a/lib/pages/search_panel/video/controller.dart b/lib/pages/search_panel/video/controller.dart index 75a73f3e2..5c8462b73 100644 --- a/lib/pages/search_panel/video/controller.dart +++ b/lib/pages/search_panel/video/controller.dart @@ -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); } diff --git a/lib/pages/setting/models/extra_settings.dart b/lib/pages/setting/models/extra_settings.dart index 48bb4f1bb..c04d7d6dc 100644 --- a/lib/pages/setting/models/extra_settings.dart +++ b/lib/pages/setting/models/extra_settings.dart @@ -74,9 +74,7 @@ List 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 get extraSettings => [ onChanged: (value) { replyLengthLimit = value; }, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'\d+')), - ], + inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: const InputDecoration(suffixText: '行'), ), actions: [ diff --git a/lib/pages/setting/models/model.dart b/lib/pages/setting/models/model.dart index 029d40c58..6a2e1e42e 100644 --- a/lib/pages/setting/models/model.dart +++ b/lib/pages/setting/models/model.dart @@ -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: [ diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index 388bcba1f..17505e1e1 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -515,7 +515,6 @@ List get styleSettings => [ title: '滑动动画弹簧参数', leading: const Icon(Icons.chrome_reader_mode_outlined), onTap: (setState) { - final numberRegExp = RegExp(r'[\d\.]+'); List springDescription = CustomSpringDescription .springDescription .map((i) => i.toString()) @@ -538,7 +537,7 @@ List get styleSettings => [ springDescription[index] = value; }, inputFormatters: [ - FilteringTextInputFormatter.allow(numberRegExp) + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')) ], decoration: InputDecoration( labelText: const [ diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index b7a991adc..6f70b9311 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -632,56 +632,27 @@ class _VideoInfoState extends State { ); } + 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 spanChildren = List.generate(descV2.length, (index) { - final currentDesc = descV2[index]; + final List spanChildren = content.descV2!.map((currentDesc) { switch (currentDesc.type) { case 1: final List spanChildren = []; - 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 { }, ), ); + } 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 { default: return const TextSpan(); } - }); + }).toList(); return TextSpan(children: spanChildren); } diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index 6e0662dea..1f0109c88 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -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', diff --git a/lib/pages/webview/view.dart b/lib/pages/webview/view.dart index 737e08b69..f8f50b665 100644 --- a/lib/pages/webview/view.dart +++ b/lib/pages/webview/view.dart @@ -171,9 +171,7 @@ class _WebviewPageState extends State { 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); } diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 889ca097c..2d4a41d61 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -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 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, ), ), diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 7d8818c30..43d3ee829 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -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(); diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index f06e2d543..33a1e1c52 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -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(List 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); } diff --git a/lib/utils/image_util.dart b/lib/utils/image_util.dart index 88c779f06..6ceda2bce 100644 --- a/lib/utils/image_util.dart +++ b/lib/utils/image_util.dart @@ -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'; diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index 9d5b9e7df..4370dab7d 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -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 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; } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 68b14c579..cd55efaa6 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -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); }