diff --git a/lib/http/api.dart b/lib/http/api.dart
index 16f3e912..ec06cc3f 100644
--- a/lib/http/api.dart
+++ b/lib/http/api.dart
@@ -499,4 +499,10 @@ class Api {
/// 获取未读动态数
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
+
+ /// 用户动态主页
+ static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic';
+
+ /// 激活buvid3
+ static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';
}
diff --git a/lib/http/init.dart b/lib/http/init.dart
index 8928c61f..97defc8d 100644
--- a/lib/http/init.dart
+++ b/lib/http/init.dart
@@ -1,7 +1,9 @@
// ignore_for_file: avoid_print
import 'dart:async';
+import 'dart:convert';
import 'dart:developer';
import 'dart:io';
+import 'dart:math' show Random;
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
@@ -11,6 +13,7 @@ import 'package:hive/hive.dart';
import 'package:PiliPalaX/utils/id_utils.dart';
import '../utils/storage.dart';
import '../utils/utils.dart';
+import 'api.dart';
import 'constants.dart';
import 'interceptor.dart';
import 'interceptor_anonymity.dart';
@@ -25,6 +28,7 @@ class Request {
late bool enableSystemProxy;
late String systemProxyHost;
late String systemProxyPort;
+ static final RegExp spmPrefixExp = RegExp(r'');
/// 设置cookie
static setCookie() async {
@@ -53,13 +57,12 @@ class Request {
}
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
- if (cookie.isEmpty) {
- try {
- await Request().get(HttpString.baseUrl);
- } catch (e) {
- log("setCookie, ${e.toString()}");
- }
+ try {
+ await buvidActivate();
+ } catch (e) {
+ log("setCookie, ${e.toString()}");
}
+
final String cookieString = cookie
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
.join('; ');
@@ -89,6 +92,33 @@ class Request {
dio.options.headers['referer'] = 'https://www.bilibili.com/';
}
+ static Future buvidActivate() async {
+ var html = await Request().get(Api.dynamicSpmPrefix);
+ String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!;
+ Random rand = Random();
+ String rand_png_end = base64.encode(
+ List.generate(32, (_) => rand.nextInt(256)) +
+ List.filled(4, 0) +
+ [73, 69, 78, 68] +
+ List.generate(4, (_) => rand.nextInt(256))
+ );
+
+ String jsonData = json.encode({
+ '3064': 1,
+ '39c8': '${spmPrefix}.fp.risk',
+ '3c43': {
+ 'adca': 'Linux',
+ 'bfe9': rand_png_end.substring(rand_png_end.length - 50),
+ },
+ });
+
+ await Request().post(
+ Api.activateBuvidApi,
+ data: {'payload': jsonData},
+ options: Options(contentType: 'application/json')
+ );
+ }
+
/*
* config it and create
*/
diff --git a/lib/http/member.dart b/lib/http/member.dart
index 56fa4572..c966a5d6 100644
--- a/lib/http/member.dart
+++ b/lib/http/member.dart
@@ -94,6 +94,7 @@ class MemberHttp {
'dm_img_list': '[]',
'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2),
'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2),
+ 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}',
});
var res = await Request().get(
Api.memberArchive,
diff --git a/lib/models/video/reply/content.dart b/lib/models/video/reply/content.dart
index 9180ec97..d62a4bca 100644
--- a/lib/models/video/reply/content.dart
+++ b/lib/models/video/reply/content.dart
@@ -9,6 +9,7 @@ class ReplyContent {
this.vote,
this.richText,
this.isText,
+ this.topicsMeta,
});
String? message;
@@ -20,6 +21,7 @@ class ReplyContent {
Map? vote;
Map? richText;
bool? isText;
+ Map? topicsMeta;
ReplyContent.fromJson(Map json) {
message = json['message']
@@ -39,6 +41,7 @@ class ReplyContent {
richText = json['rich_text'] ?? {};
// 不包含@ 笔记 图片的时候,文字可折叠
isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty;
+ topicsMeta = json['topics_meta'] ?? {};
}
}
diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart
index aa1a90e5..3f40438f 100644
--- a/lib/pages/about/index.dart
+++ b/lib/pages/about/index.dart
@@ -236,11 +236,26 @@ class AboutController extends GetxController {
);
}
+ githubRelease() {
+ launchUrl(
+ Uri.parse('https://github.com/guozhigq/pilipala/release'),
+ mode: LaunchMode.externalApplication,
+ );
+ }
+
// 从网盘下载
panDownload() {
- launchUrl(
- Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
- mode: LaunchMode.externalApplication,
+ Clipboard.setData(
+ const ClipboardData(text: 'pili'),
+ );
+ SmartDialog.showToast(
+ '已复制提取码:pili',
+ displayTime: const Duration(milliseconds: 500),
+ ).then(
+ (value) => launchUrl(
+ Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
+ mode: LaunchMode.externalApplication,
+ ),
);
}
// 问题反馈
diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart
index c34efe28..8cdff1a7 100644
--- a/lib/pages/home/controller.dart
+++ b/lib/pages/home/controller.dart
@@ -26,6 +26,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
late List defaultTabs;
late List tabbarSort;
RxString defaultSearch = ''.obs;
+ late bool enableGradientBg;
@override
void onInit() {
@@ -40,6 +41,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) {
searchDefault();
}
+ enableGradientBg =
+ setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
}
void onRefresh() {
diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart
index 55b21253..e42a0ee1 100644
--- a/lib/pages/home/view.dart
+++ b/lib/pages/home/view.dart
@@ -48,38 +48,48 @@ class _HomePageState extends State
super.build(context);
Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
// 设置状态栏图标的亮度
- SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
- statusBarIconBrightness: currentBrightness == Brightness.light
- ? Brightness.dark
- : Brightness.light,
- ));
+ if (_homeController.enableGradientBg) {
+ SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
+ statusBarIconBrightness: currentBrightness == Brightness.light
+ ? Brightness.dark
+ : Brightness.light,
+ ));
+ }
return Scaffold(
extendBody: true,
extendBodyBehindAppBar: true,
body: Stack(
children: [
// gradient background
- Align(
- alignment: Alignment.topLeft,
- child: Opacity(
- opacity: 0.6,
- child: Container(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.primary.withOpacity(0.9),
- Theme.of(context).colorScheme.primary.withOpacity(0.5),
- Theme.of(context).colorScheme.surface
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- stops: const [0, 0.0034, 0.34]),
+ if (_homeController.enableGradientBg) ...[
+ Align(
+ alignment: Alignment.topLeft,
+ child: Opacity(
+ opacity: 0.6,
+ child: Container(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context)
+ .colorScheme
+ .primary
+ .withOpacity(0.9),
+ Theme.of(context)
+ .colorScheme
+ .primary
+ .withOpacity(0.5),
+ Theme.of(context).colorScheme.surface
+ ],
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ stops: const [0, 0.0034, 0.34]),
+ ),
),
),
),
- ),
+ ],
Column(
children: [
CustomAppBar(
@@ -90,7 +100,37 @@ class _HomePageState extends State
callback: showUserBottomSheet,
),
if (_homeController.tabs.length > 1) ...[
- const CustomTabs(),
+ if (_homeController.enableGradientBg) ...[
+ const CustomTabs(),
+ ] else ...[
+ const SizedBox(height: 4),
+ SizedBox(
+ width: double.infinity,
+ height: 42,
+ child: Align(
+ alignment: Alignment.center,
+ child: TabBar(
+ controller: _homeController.tabController,
+ tabs: [
+ for (var i in _homeController.tabs)
+ Tab(text: i['label'])
+ ],
+ isScrollable: true,
+ dividerColor: Colors.transparent,
+ enableFeedback: true,
+ splashBorderRadius: BorderRadius.circular(10),
+ tabAlignment: TabAlignment.center,
+ onTap: (value) {
+ feedBack();
+ if (_homeController.initialIndex.value == value) {
+ _homeController.tabsCtrList[value]().animateToTop();
+ }
+ _homeController.initialIndex.value = value;
+ },
+ ),
+ ),
+ ),
+ ],
] else ...[
const SizedBox(height: 6),
],
diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart
index 5364ebf3..7a477cc3 100644
--- a/lib/pages/live_room/controller.dart
+++ b/lib/pages/live_room/controller.dart
@@ -4,6 +4,8 @@ import 'package:PiliPalaX/http/live.dart';
import 'package:PiliPalaX/models/live/room_info.dart';
import 'package:PiliPalaX/plugin/pl_player/index.dart';
import '../../models/live/room_info_h5.dart';
+import '../../utils/storage.dart';
+import '../../utils/video_utils.dart';
class LiveRoomController extends GetxController {
String cover = '';
@@ -16,6 +18,7 @@ class LiveRoomController extends GetxController {
PlPlayerController plPlayerController =
PlPlayerController.getInstance(videoType: 'live');
Rx roomInfoH5 = RoomInfoH5Model().obs;
+ late bool enableCDN;
@override
void onInit() {
@@ -31,6 +34,8 @@ class LiveRoomController extends GetxController {
cover = liveItem.cover;
}
}
+ // CDN优化
+ enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
}
playerInit(source) async {
@@ -57,9 +62,11 @@ class LiveRoomController extends GetxController {
List codec =
res['data'].playurlInfo.playurl.stream.first.format.first.codec;
CodecItem item = codec.first;
- String videoUrl = (item.urlInfo?.first.host)! +
- item.baseUrl! +
- item.urlInfo!.first.extra!;
+ String videoUrl = enableCDN
+ ? VideoUtils.getCdnUrl(item)
+ : (item.urlInfo?.first.host)! +
+ item.baseUrl! +
+ item.urlInfo!.first.extra!;
await playerInit(videoUrl);
return res;
}
diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart
index a4baa01d..cb712904 100644
--- a/lib/pages/setting/style_setting.dart
+++ b/lib/pages/setting/style_setting.dart
@@ -102,6 +102,12 @@ class _StyleSettingState extends State {
defaultVal: true,
needReboot: true,
),
+ const SetSwitchItem(
+ title: '首页底栏背景渐变',
+ setKey: SettingBoxKey.enableGradientBg,
+ defaultVal: true,
+ needReboot: true,
+ ),
ListTile(
onTap: () async {
double? result = await showDialog(
diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart
index efeeb1f9..9803c900 100644
--- a/lib/pages/video/detail/reply/widgets/reply_item.dart
+++ b/lib/pages/video/detail/reply/widgets/reply_item.dart
@@ -507,6 +507,7 @@ InlineSpan buildContent(
// 构建正则表达式
final List specialTokens = [
...content.emote.keys,
+ ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [],
...content.atNameToMid.keys.map((e) => '@$e'),
...content.jumpUrl.keys.map((e) =>
e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')),
@@ -590,7 +591,7 @@ InlineSpan buildContent(
),
);
} else {
- // print("matchStr=$matchStr");
+ print("matchStr=$matchStr");
String appUrlSchema = '';
final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
defaultValue: false) as bool;
@@ -693,6 +694,23 @@ InlineSpan buildContent(
);
// 只显示一次
matchedStrs.add(matchStr);
+ } else if (content
+ .topicsMeta[matchStr.substring(1, matchStr.length - 1)] !=
+ null) {
+ spanChilds.add(
+ TextSpan(
+ text: matchStr,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ final String topic =
+ matchStr.substring(1, matchStr.length - 1);
+ Get.toNamed('/searchResult', parameters: {'keyword': topic});
+ },
+ ),
+ );
} else {
addPlainTextSpan(matchStr);
}
diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart
index 3b10b0ac..3cbe171a 100644
--- a/lib/utils/storage.dart
+++ b/lib/utils/storage.dart
@@ -141,6 +141,7 @@ class SettingBoxKey {
tabbarSort = 'tabbarSort', // 首页tabbar
dynamicBadgeMode = 'dynamicBadgeMode',
hiddenSettingUnlocked = 'hiddenSettingUnlocked';
+ enableGradientBg = 'enableGradientBg';
}
class LocalCacheKey {
diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart
index c73a94a7..88f30731 100644
--- a/lib/utils/utils.dart
+++ b/lib/utils/utils.dart
@@ -17,6 +17,8 @@ import '../http/index.dart';
import '../models/github/latest.dart';
class Utils {
+ static final Random random = Random();
+
static Future getCookiePath() async {
final Directory tempDir = await getApplicationSupportDirectory();
final String tempPath = "${tempDir.path}/.plpl/";
@@ -181,7 +183,7 @@ class Utils {
}
static String makeHeroTag(v) {
- return v.toString() + Random().nextInt(9999).toString();
+ return v.toString() + random.nextInt(9999).toString();
}
static int duration(String duration) {
@@ -340,18 +342,14 @@ class Utils {
return md5String;
}
- static String generateRandomString(int minLength, int maxLength) {
- const String printable =
- '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#\$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ';
-
- var random = Random();
- int length = minLength + random.nextInt(maxLength - minLength + 1);
- return List.generate(
- length, (index) => printable[random.nextInt(printable.length)]).join();
+ static List generateRandomBytes(int minLength, int maxLength) {
+ return List.generate(
+ random.nextInt(maxLength-minLength+1), (_) => random.nextInt(0x60) + 0x20
+ );
}
static String base64EncodeRandomString(int minLength, int maxLength) {
- String randomString = generateRandomString(minLength, maxLength);
- return base64.encode(utf8.encode(randomString));
+ List randomBytes = generateRandomBytes(minLength, maxLength);
+ return base64.encode(randomBytes);
}
}
diff --git a/lib/utils/video_utils.dart b/lib/utils/video_utils.dart
index 646892e3..d6612aa5 100644
--- a/lib/utils/video_utils.dart
+++ b/lib/utils/video_utils.dart
@@ -1,5 +1,7 @@
import 'package:PiliPalaX/models/video/play/url.dart';
+import '../models/live/room_info.dart';
+
class VideoUtils {
static String getCdnUrl(dynamic item) {
var backupUrl = "";
@@ -12,13 +14,20 @@ class VideoUtils {
} else if (item is AudioItem) {
backupUrl = item.backupUrl ?? "";
videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
+ } else if (item is CodecItem) {
+ backupUrl = (item.urlInfo?.first.host)! +
+ item.baseUrl! +
+ item.urlInfo!.first.extra!;
+ videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
} else {
return "";
}
/// issues #70
- if (videoUrl.contains(".mcdn.bilivideo") ||
- videoUrl.contains("/upgcxcode/")) {
+ if (videoUrl.contains(".mcdn.bilivideo")) {
+ videoUrl =
+ 'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}';
+ } else if (videoUrl.contains("/upgcxcode/")) {
//CDN列表
var cdnList = {
'ali': 'upos-sz-mirrorali.bilivideo.com',