opt ua

opt subtitle

opt playertype

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-07-26 18:41:11 +08:00
parent a05ecd020b
commit 3a78ead3a6
28 changed files with 218 additions and 221 deletions

View File

@@ -290,15 +290,6 @@ class Request {
token.cancel("cancelled");
}
static String headerUa({String type = 'mob'}) {
return switch (type) {
'mob' =>
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
_ =>
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15',
};
}
static String responseDecoder(
List<int> responseBytes,
RequestOptions options,

View File

@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/models/common/live_search_type.dart';
import 'package:PiliPlus/models_new/live/live_area_list/area_item.dart';
import 'package:PiliPlus/models_new/live/live_area_list/area_list.dart';
@@ -114,7 +115,7 @@ class LiveHttp {
options: Options(
headers: {
'referer': 'https://live.bilibili.com/$roomId',
'user-agent': Request.headerUa(type: 'pc'),
'user-agent': UaType.pc.ua,
},
),
);

View File

@@ -6,6 +6,7 @@ import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/models/common/member/contribute_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/models/member/info.dart';
@@ -280,7 +281,7 @@ class MemberHttp {
headers: {
'origin': 'https://space.bilibili.com',
'referer': 'https://space.bilibili.com/$mid/dynamic',
'user-agent': Request.headerUa(type: 'pc'),
'user-agent': UaType.pc.ua,
},
),
);
@@ -352,7 +353,7 @@ class MemberHttp {
queryParameters: params,
options: Options(
headers: {
HttpHeaders.userAgentHeader: Request.headerUa(type: 'pc'),
HttpHeaders.userAgentHeader: UaType.pc.ua,
HttpHeaders.refererHeader: '${HttpString.spaceBaseUrl}/$mid',
'origin': HttpString.spaceBaseUrl,
},

11
lib/http/ua_type.dart Normal file
View File

@@ -0,0 +1,11 @@
enum UaType {
mob(
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
),
pc(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15',
);
final String ua;
const UaType(this.ua);
}

View File

@@ -6,6 +6,7 @@ import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/login.dart';
import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/models/common/account_type.dart';
import 'package:PiliPlus/models/home/rcmd/result.dart';
import 'package:PiliPlus/models/model_hot_video_item.dart';
@@ -367,7 +368,7 @@ class VideoHttp {
headers: {
'origin': 'https://www.bilibili.com',
'referer': 'https://www.bilibili.com/bangumi/play/ss$seasonId',
'user-agent': Request.headerUa(type: 'pc'),
'user-agent': UaType.pc.ua,
},
),
);
@@ -395,7 +396,7 @@ class VideoHttp {
headers: {
'origin': 'https://www.bilibili.com',
'referer': 'https://www.bilibili.com/video/$bvid',
'user-agent': Request.headerUa(type: 'pc'),
'user-agent': UaType.pc.ua,
},
),
);
@@ -593,7 +594,7 @@ class VideoHttp {
'extend_content': jsonEncode({
"entity": "user",
"entity_id": mid,
'fp': Request.headerUa(type: 'pc'),
'fp': UaType.pc.ua,
}),
'csrf': Accounts.main.csrf,
},
@@ -602,7 +603,7 @@ class VideoHttp {
headers: {
'origin': 'https://space.bilibili.com',
'referer': 'https://space.bilibili.com/$mid/dynamic',
'user-agent': Request.headerUa(type: 'pc'),
'user-agent': UaType.pc.ua,
},
),
);

View File

@@ -0,0 +1,13 @@
enum SettingType {
privacySetting('隐私设置'),
recommendSetting('推荐流设置'),
videoSetting('音视频设置'),
playSetting('播放器设置'),
styleSetting('外观设置'),
extraSetting('其它设置'),
webdavSetting('WebDAV 设置'),
about('关于');
final String title;
const SettingType(this.title);
}

View File

@@ -1,21 +1,9 @@
enum SubtitlePrefType { off, on, withoutAi, auto }
enum SubtitlePrefType {
off('默认不显示字幕'),
on('优先选择非自动生成(ai)字幕'),
withoutAi('跳过自动生成(ai)字幕,选择第一个可用字幕'),
auto('静音时等同第二项,非静音时等同第三项');
extension SubtitlePrefTypeExt on SubtitlePrefType {
String get description => const [
'默认不显示字幕',
'优先选择非自动生成(ai)字幕',
'跳过自动生成(ai)字幕,选择第一个可用字幕',
'静音时等同第二项,非静音时等同第三项',
][index];
static const List<String> _codeList = ['off', 'on', 'withoutAi', 'auto'];
String get code => _codeList[index];
static SubtitlePrefType? fromCode(String code) {
final index = _codeList.indexOf(code);
if (index != -1) {
return SubtitlePrefType.values[index];
}
return null;
}
final String desc;
const SubtitlePrefType(this.desc);
}

View File

@@ -31,7 +31,7 @@ abstract class CommonIntroController extends GetxController {
Future queryVideoInFolder();
Future<void> actionFavVideo({String type = 'choose'});
Future<void> actionFavVideo({bool isQuick = false});
late final enableQuickFav = Pref.enableQuickFav;
int? quickFavId;
@@ -54,7 +54,7 @@ abstract class CommonIntroController extends GetxController {
}
// 收藏
void showFavBottomSheet(BuildContext context, {type = 'tap'}) {
void showFavBottomSheet(BuildContext context, {bool isLongPress = false}) {
if (!accountService.isLogin.value) {
SmartDialog.showToast('账号未登录');
return;
@@ -63,12 +63,12 @@ abstract class CommonIntroController extends GetxController {
// 点按 收藏至默认文件夹
// 长按选择文件夹
if (enableQuickFav) {
if (type == 'tap') {
actionFavVideo(type: 'default');
if (!isLongPress) {
actionFavVideo(isQuick: true);
} else {
PageUtils.showFavBottomSheet(context: context, ctr: this);
}
} else if (type != 'longPress') {
} else if (!isLongPress) {
PageUtils.showFavBottomSheet(context: context, ctr: this);
}
}

View File

@@ -93,7 +93,7 @@ class DynamicsController extends GetxController
}
late bool isQuerying = false;
Future<void> queryFollowUp({String type = 'init'}) async {
Future<void> queryFollowUp() async {
if (isQuerying) return;
isQuerying = true;
if (!accountService.isLogin.value) {

View File

@@ -36,7 +36,7 @@ class LiveRoomController extends GetxController {
// 静音状态
RxBool volumeOff = false.obs;
PlPlayerController plPlayerController = PlPlayerController.getInstance(
videoType: 'live',
isLive: true,
);
Rx<RoomInfoH5Data?> roomInfoH5 = Rx<RoomInfoH5Data?>(null);
@@ -80,7 +80,7 @@ class LiveRoomController extends GetxController {
},
),
autoplay: true,
direction: isPortrait.value ? 'vertical' : 'horizontal',
isVertical: isPortrait.value,
);
}

View File

@@ -143,7 +143,7 @@ class LiveHeaderControl extends StatelessWidget {
if ((await floating.isPipAvailable) == true) {
plPlayerController.hiddenControls(false);
floating.enable(
plPlayerController.direction.value == 'vertical'
plPlayerController.isVertical
? const EnableManual(
aspectRatio: Rational.vertical(),
)

View File

@@ -95,22 +95,22 @@ List<SettingsModel> get playSettings => [
title: '自动启用字幕',
leading: const Icon(Icons.closed_caption_outlined),
getSubtitle: () =>
'当前选择偏好:${SubtitlePrefTypeExt.fromCode(Pref.subtitlePreference)!.description}',
'当前选择偏好:${SubtitlePrefType.values[Pref.subtitlePreferenceV2].desc}',
onTap: (setState) async {
String? result = await showDialog(
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
return SelectDialog<int>(
title: '字幕选择偏好',
value: Pref.subtitlePreference,
value: Pref.subtitlePreferenceV2,
values: SubtitlePrefType.values
.map((e) => (e.code, e.description))
.map((e) => (e.index, e.desc))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.subtitlePreference, result);
await GStorage.setting.put(SettingBoxKey.subtitlePreferenceV2, result);
setState();
}
},

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/http/login.dart';
import 'package:PiliPlus/models/common/setting_type.dart';
import 'package:PiliPlus/pages/about/view.dart';
import 'package:PiliPlus/pages/login/controller.dart';
import 'package:PiliPlus/pages/setting/extra_setting.dart';
@@ -18,14 +19,12 @@ import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class _SettingsModel {
final String name;
final String title;
final SettingType type;
final String? subtitle;
final IconData icon;
const _SettingsModel({
required this.name,
required this.title,
required this.type,
this.subtitle,
required this.icon,
});
@@ -39,55 +38,47 @@ class SettingPage extends StatefulWidget {
}
class _SettingPageState extends State<SettingPage> {
late String _type = 'privacySetting';
late SettingType _type = SettingType.privacySetting;
final RxBool _noAccount = Accounts.accountMode.isEmpty.obs;
bool get _isPortrait => context.orientation == Orientation.portrait;
late bool _isPortrait;
final List<_SettingsModel> _items = [
const _SettingsModel(
name: 'privacySetting',
title: '隐私设置',
type: SettingType.privacySetting,
subtitle: '黑名单、无痕模式',
icon: Icons.privacy_tip_outlined,
),
const _SettingsModel(
name: 'recommendSetting',
title: '推荐流设置',
type: SettingType.recommendSetting,
subtitle: '推荐来源web/app、刷新保留内容、过滤器',
icon: Icons.explore_outlined,
),
const _SettingsModel(
name: 'videoSetting',
title: '音视频设置',
type: SettingType.videoSetting,
subtitle: '画质、音质、解码、缓冲、音频输出等',
icon: Icons.video_settings_outlined,
),
const _SettingsModel(
name: 'playSetting',
title: '播放器设置',
type: SettingType.playSetting,
subtitle: '双击/长按、全屏、后台播放、弹幕、字幕、底部进度条等',
icon: Icons.touch_app_outlined,
),
const _SettingsModel(
name: 'styleSetting',
title: '外观设置',
type: SettingType.styleSetting,
subtitle: '横屏适配(平板)、侧栏、列宽、首页、动态红点、主题、字号、图片、帧率等',
icon: Icons.style_outlined,
),
const _SettingsModel(
name: 'extraSetting',
title: '其它设置',
type: SettingType.extraSetting,
subtitle: '震动、搜索、收藏、ai、评论、动态、代理、更新检查等',
icon: Icons.extension_outlined,
),
const _SettingsModel(
name: 'webdavSetting',
title: 'WebDAV 设置',
type: SettingType.webdavSetting,
icon: MdiIcons.databaseCogOutline,
),
const _SettingsModel(
name: 'about',
title: '关于',
type: SettingType.about,
icon: Icons.info_outline,
),
];
@@ -95,21 +86,10 @@ class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
_isPortrait = context.orientation == Orientation.portrait;
return Scaffold(
appBar: AppBar(
title: _isPortrait
? const Text('设置')
: Text(switch (_type) {
'privacySetting' => '隐私设置',
'recommendSetting' => '推荐流设置',
'videoSetting' => '音视频设置',
'playSetting' => '播放器设置',
'styleSetting' => '外观设置',
'extraSetting' => '其它设置',
'webdavSetting' => 'WebDAV 设置',
'about' => '关于',
_ => '设置',
}),
title: _isPortrait ? const Text('设置') : Text(_type.title),
),
body: _isPortrait
? _buildList(theme)
@@ -136,21 +116,28 @@ class _SettingPageState extends State<SettingPage> {
removeLeft: true,
removeTop: true,
child: switch (_type) {
'privacySetting' => const PrivacySetting(
SettingType.privacySetting => const PrivacySetting(
showAppBar: false,
),
'recommendSetting' => const RecommendSetting(
SettingType.recommendSetting => const RecommendSetting(
showAppBar: false,
),
'videoSetting' => const VideoSetting(showAppBar: false),
'playSetting' => const PlaySetting(showAppBar: false),
'styleSetting' => const StyleSetting(showAppBar: false),
'extraSetting' => const ExtraSetting(showAppBar: false),
'webdavSetting' => const WebDavSettingPage(
SettingType.videoSetting => const VideoSetting(
showAppBar: false,
),
'about' => const AboutPage(showAppBar: false),
_ => const SizedBox.shrink(),
SettingType.playSetting => const PlaySetting(
showAppBar: false,
),
SettingType.styleSetting => const StyleSetting(
showAppBar: false,
),
SettingType.extraSetting => const ExtraSetting(
showAppBar: false,
),
SettingType.webdavSetting => const WebDavSettingPage(
showAppBar: false,
),
SettingType.about => const AboutPage(showAppBar: false),
},
),
),
@@ -159,20 +146,20 @@ class _SettingPageState extends State<SettingPage> {
);
}
void _toPage(String name) {
void _toPage(SettingType type) {
if (_isPortrait) {
Get.toNamed('/$name');
Get.toNamed('/${type.name}');
} else {
_type = name;
_type = type;
setState(() {});
}
}
Color? _getTileColor(ThemeData theme, String name) {
Color? _getTileColor(ThemeData theme, SettingType type) {
if (_isPortrait) {
return null;
} else {
return name == _type ? theme.colorScheme.onInverseSurface : null;
return type == _type ? theme.colorScheme.onInverseSurface : null;
}
}
@@ -188,10 +175,10 @@ class _SettingPageState extends State<SettingPage> {
.sublist(0, _items.length - 1)
.map(
(item) => ListTile(
tileColor: _getTileColor(theme, item.name),
onTap: () => _toPage(item.name),
tileColor: _getTileColor(theme, item.type),
onTap: () => _toPage(item.type),
leading: Icon(item.icon),
title: Text(item.title, style: titleStyle),
title: Text(item.type.title, style: titleStyle),
subtitle: item.subtitle == null
? null
: Text(item.subtitle!, style: subTitleStyle),
@@ -212,10 +199,10 @@ class _SettingPageState extends State<SettingPage> {
),
),
ListTile(
tileColor: _getTileColor(theme, _items.last.name),
onTap: () => _toPage(_items.last.name),
tileColor: _getTileColor(theme, _items.last.type),
onTap: () => _toPage(_items.last.type),
leading: Icon(_items.last.icon),
title: Text(_items.last.title, style: titleStyle),
title: Text(_items.last.type.title, style: titleStyle),
),
SizedBox(height: MediaQuery.paddingOf(context).bottom + 80),
],

View File

@@ -18,6 +18,7 @@ import 'package:PiliPlus/models/common/sponsor_block/segment_model.dart';
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
import 'package:PiliPlus/models/common/video/audio_quality.dart';
import 'package:PiliPlus/models/common/video/subtitle_pref_type.dart';
import 'package:PiliPlus/models/common/video/video_decode_type.dart';
import 'package:PiliPlus/models/common/video/video_quality.dart';
import 'package:PiliPlus/models/video/play/url.dart';
@@ -69,7 +70,7 @@ class VideoDetailController extends GetxController
final heroTag = Get.arguments['heroTag'];
final RxString cover = ''.obs;
// 视频类型 默认投稿视频
final videoType = Get.arguments['videoType'] ?? SearchType.video;
final SearchType videoType = Get.arguments['videoType'] ?? SearchType.video;
/// tabs相关配置
late TabController tabCtr;
@@ -122,7 +123,7 @@ class VideoDetailController extends GetxController
StreamSubscription<Duration>? positionSubscription;
late final scrollKey = GlobalKey<ExtendedNestedScrollViewState>();
late final RxString direction = 'horizontal'.obs;
late final RxBool isVertical = false.obs;
late final RxDouble scrollRatio = 0.0.obs;
late final ScrollController scrollCtr = ScrollController()
..addListener(scrollListener);
@@ -150,21 +151,17 @@ class VideoDetailController extends GetxController
}
void setVideoHeight() {
String direction = firstVideo.width != null && firstVideo.height != null
? firstVideo.width! > firstVideo.height!
? 'horizontal'
: 'vertical'
: 'horizontal';
final isVertical = firstVideo.width != null && firstVideo.height != null
? firstVideo.width! < firstVideo.height!
: false;
if (!scrollCtr.hasClients) {
videoHeight = direction == 'vertical' ? maxVideoHeight : minVideoHeight;
this.direction.value = direction;
videoHeight = isVertical ? maxVideoHeight : minVideoHeight;
this.isVertical.value = isVertical;
return;
}
if (this.direction.value != direction) {
this.direction.value = direction;
double videoHeight = direction == 'vertical'
? maxVideoHeight
: minVideoHeight;
if (this.isVertical.value != isVertical) {
this.isVertical.value = isVertical;
double videoHeight = isVertical ? maxVideoHeight : minVideoHeight;
if (this.videoHeight != videoHeight) {
if (videoHeight > this.videoHeight) {
// current minVideoHeight
@@ -1140,7 +1137,7 @@ class VideoDetailController extends GetxController
? null
: Duration(milliseconds: data.timeLength!)),
// 宽>高 水平 否则 垂直
direction: direction.value,
isVertical: isVertical.value,
bvid: bvid,
cid: cid.value,
autoplay: autoplay ?? autoPlay.value,
@@ -1562,12 +1559,13 @@ class VideoDetailController extends GetxController
int idx = 0;
subtitles.value = playInfo.subtitle!.subtitles!;
String preference = Pref.subtitlePreference;
if (preference != 'off') {
SubtitlePrefType preference =
SubtitlePrefType.values[Pref.subtitlePreferenceV2];
if (preference != SubtitlePrefType.off) {
idx = subtitles.indexWhere((i) => !i.lan!.startsWith('ai')) + 1;
if (idx == 0) {
if (preference == 'on' ||
(preference == 'auto' &&
if (preference == SubtitlePrefType.on ||
(preference == SubtitlePrefType.auto &&
(await FlutterVolumeController.getVolume() ?? 0) <= 0)) {
idx = 1;
}

View File

@@ -34,7 +34,7 @@ class PgcIntroController extends CommonIntroController {
? int.tryParse(Get.parameters['epId']!)
: null;
late dynamic type =
late final String pgcType =
Get.parameters['type'] == '1' || Get.parameters['type'] == '4'
? '追番'
: '追剧';
@@ -139,9 +139,9 @@ class PgcIntroController extends CommonIntroController {
// (取消)收藏 pgc
@override
Future<void> actionFavVideo({String type = 'choose'}) async {
Future<void> actionFavVideo({bool isQuick = false}) async {
// 收藏至默认文件夹
if (type == 'default') {
if (isQuick) {
SmartDialog.showLoading(msg: '请求中');
queryVideoInFolder().then((res) async {
if (res['status']) {

View File

@@ -176,7 +176,8 @@ class _PgcIntroPageState extends State<PgcIntroPage>
if (isFollowed) {
showPgcFollowDialog(
context: context,
type: pgcIntroController.type,
type:
pgcIntroController.pgcType,
followStatus: followStatus,
onUpdateStatus: (followStatus) {
if (followStatus == -1) {
@@ -195,8 +196,8 @@ class _PgcIntroPageState extends State<PgcIntroPage>
},
child: Text(
isFollowed
? '${pgcIntroController.type}'
: '${pgcIntroController.type}',
? '${pgcIntroController.pgcType}'
: pgcIntroController.pgcType,
),
);
},
@@ -317,7 +318,7 @@ class _PgcIntroPageState extends State<PgcIntroPage>
onTap: () => pgcIntroController.showFavBottomSheet(context),
onLongPress: () => pgcIntroController.showFavBottomSheet(
context,
type: 'longPress',
isLongPress: true,
),
selectStatus: pgcIntroController.hasFav.value,
semanticsLabel: '收藏',

View File

@@ -358,9 +358,9 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
// (取消)收藏
@override
Future<void> actionFavVideo({String type = 'choose'}) async {
Future<void> actionFavVideo({bool isQuick = false}) async {
// 收藏至默认文件夹
if (type == 'default') {
if (isQuick) {
SmartDialog.showLoading(msg: '请求中');
queryVideoInFolder().then((res) async {
if (res['status']) {

View File

@@ -579,7 +579,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
onTap: () => videoIntroController.showFavBottomSheet(context),
onLongPress: () => videoIntroController.showFavBottomSheet(
context,
type: 'longPress',
isLongPress: true,
),
selectStatus: videoIntroController.hasFav.value,
semanticsLabel: '收藏',

View File

@@ -940,8 +940,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
final double width = size.width;
final double height = size.height;
final padding = MediaQuery.paddingOf(context);
if (enableVerticalExpand &&
videoDetailController.direction.value == 'vertical') {
if (enableVerticalExpand && videoDetailController.isVertical.value) {
final double videoHeight =
height - (removeSafeArea ? 0 : padding.vertical);
final double videoWidth = videoHeight * 9 / 16;
@@ -1026,8 +1025,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
final double width = size.width;
final double height = size.height;
final padding = MediaQuery.paddingOf(context);
if (enableVerticalExpand &&
videoDetailController.direction.value == 'vertical') {
if (enableVerticalExpand && videoDetailController.isVertical.value) {
final double videoHeight = height - (removeSafeArea ? 0 : padding.top);
final double videoWidth = videoHeight * 9 / 16;
return Row(

View File

@@ -2342,7 +2342,7 @@ class HeaderControlState extends State<HeaderControl> {
onTap: () => videoIntroController
.showFavBottomSheet(context),
onLongPress: () => videoIntroController
.showFavBottomSheet(context, type: 'longPress'),
.showFavBottomSheet(context, isLongPress: true),
selectStatus: videoIntroController.hasFav.value,
semanticsLabel: '收藏',
needAnim: true,
@@ -2442,7 +2442,7 @@ class HeaderControlState extends State<HeaderControl> {
onTap: () =>
pgcIntroController.showFavBottomSheet(context),
onLongPress: () => pgcIntroController
.showFavBottomSheet(context, type: 'longPress'),
.showFavBottomSheet(context, isLongPress: true),
selectStatus: pgcIntroController.hasFav.value,
semanticsLabel: '收藏',
needAnim: true,

View File

@@ -1,6 +1,6 @@
import 'dart:io';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/models/common/webview_menu_type.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
@@ -22,7 +22,7 @@ class WebviewPage extends StatefulWidget {
// note
final int? oid;
final String? title;
final String? uaType;
final UaType? uaType;
@override
State<WebviewPage> createState() => _WebviewPageState();
@@ -30,7 +30,8 @@ class WebviewPage extends StatefulWidget {
class _WebviewPageState extends State<WebviewPage> {
late final String _url = widget.url ?? Get.parameters['url'] ?? '';
late final uaType = widget.uaType ?? Get.parameters['uaType'] ?? 'mob';
late final UaType uaType =
widget.uaType ?? UaType.values.byName(Get.parameters['uaType'] ?? 'mob');
final RxString title = ''.obs;
final RxDouble progress = 1.0.obs;
bool? _inApp;
@@ -158,7 +159,7 @@ class _WebviewPageState extends State<WebviewPage> {
useHybridComposition: false,
algorithmicDarkeningAllowed: true,
useShouldOverrideUrlLoading: true,
userAgent: Request.headerUa(type: uaType),
userAgent: uaType.ua,
mixedContentMode: MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW,
),
initialUrlRequest: URLRequest(

View File

@@ -93,9 +93,9 @@ class PlPlayerController {
final RxBool _controlsLock = false.obs;
final RxBool _isFullScreen = false.obs;
// 默认投稿视频格式
static final RxString _videoType = 'archive'.obs;
bool _isLive = false;
final RxString _direction = 'horizontal'.obs;
bool _isVertical = false;
final Rx<BoxFit> _videoFit = Rx(BoxFit.contain);
late StreamSubscription<DataStatus> _dataListenerForVideoFit;
@@ -241,12 +241,12 @@ class PlPlayerController {
RxBool get isFullScreen => _isFullScreen;
/// 全屏方向
RxString get direction => _direction;
bool get isVertical => _isVertical;
RxInt get playerCount => _playerCount;
///
RxString get videoType => _videoType;
bool get isLive => _isLive;
/// 弹幕开关
RxBool enableShowDanmaku = Pref.enableShowDanmaku.obs;
@@ -467,7 +467,8 @@ class PlPlayerController {
Box video = GStorage.video;
// 添加一个私有构造函数
PlPlayerController._() {
PlPlayerController._({bool isLive = false}) {
_isLive = isLive;
if (!Accounts.get(AccountType.heartbeat).isLogin || Pref.historyPause) {
enableHeart = false;
}
@@ -492,13 +493,10 @@ class PlPlayerController {
}
// 获取实例 传参
static PlPlayerController getInstance({
String videoType = 'archive',
}) {
static PlPlayerController getInstance({bool isLive = false}) {
// 如果实例尚未创建,则创建一个新实例
_instance ??= PlPlayerController._();
_instance ??= PlPlayerController._(isLive: isLive);
_instance!._playerCount.value += 1;
_videoType.value = videoType;
return _instance!;
}
@@ -520,7 +518,7 @@ class PlPlayerController {
int? height,
Duration? duration,
// 方向
String? direction,
bool? isVertical,
// 记录历史记录
String bvid = '',
int cid = 0,
@@ -544,7 +542,7 @@ class PlPlayerController {
// 初始化数据加载状态
dataStatus.status.value = DataStatus.loading;
// 初始化全屏方向
_direction.value = direction ?? 'horizontal';
_isVertical = isVertical ?? false;
_bvid = bvid;
_cid = cid;
_epid = epid;
@@ -697,12 +695,8 @@ class PlPlayerController {
configuration: PlayerConfiguration(
// 默认缓冲 4M 大小
bufferSize: Pref.expandBuffer
? (videoType.value == 'live'
? 64 * 1024 * 1024
: 32 * 1024 * 1024)
: (videoType.value == 'live'
? 16 * 1024 * 1024
: 4 * 1024 * 1024),
? (isLive ? 64 * 1024 * 1024 : 32 * 1024 * 1024)
: (isLive ? 16 * 1024 * 1024 : 4 * 1024 * 1024),
),
);
var pp = player.platform as NativePlayer;
@@ -833,7 +827,7 @@ class PlPlayerController {
Future<void> _initializePlayer() async {
if (_instance == null) return;
// 设置倍速
if (videoType.value == 'live') {
if (isLive) {
await setPlaybackSpeed(1.0);
} else {
if (_videoPlayerController?.state.rate != _playbackSpeed.value) {
@@ -891,7 +885,7 @@ class PlPlayerController {
videoPlayerServiceHandler.onStatusChange(
playerStatus.status.value,
isBuffering.value,
videoType.value == 'live',
isLive,
);
/// 触发回调事件
@@ -941,12 +935,12 @@ class PlPlayerController {
videoPlayerServiceHandler.onStatusChange(
playerStatus.status.value,
event,
videoType.value == 'live',
isLive,
);
}),
videoPlayerController!.stream.error.listen((String event) {
// 直播的错误提示没有参考价值,均不予显示
if (videoType.value == 'live') return;
if (isLive) return;
if (event.startsWith("Failed to open https://") ||
event.startsWith("Can not open external file https://") ||
//tcp: ffurl_read returned 0xdfb9b0bb
@@ -957,11 +951,12 @@ class PlPlayerController {
const Duration(milliseconds: 10000),
() {
Future.delayed(const Duration(milliseconds: 3000), () async {
if (kDebugMode) {
debugPrint("isBuffering.value: ${isBuffering.value}");
}
if (kDebugMode)
debugPrint("_buffered.value: ${_buffered.value}");
// if (kDebugMode) {
// debugPrint("isBuffering.value: ${isBuffering.value}");
// }
// if (kDebugMode) {
// debugPrint("_buffered.value: ${_buffered.value}");
// }
if (isBuffering.value && _buffered.value == Duration.zero) {
SmartDialog.showToast(
'视频链接打开失败,重试中',
@@ -998,7 +993,7 @@ class PlPlayerController {
videoPlayerServiceHandler.onStatusChange(
event,
isBuffering.value,
videoType.value == 'live',
isLive,
);
}),
onPositionChanged.listen((Duration event) {
@@ -1286,7 +1281,7 @@ class PlPlayerController {
});
}
// fill不应该在竖屏视频生效
} else if (attr == BoxFit.fill && direction.value == 'vertical') {
} else if (attr == BoxFit.fill && isVertical) {
attr = BoxFit.contain;
}
_videoFit.value = attr;
@@ -1312,7 +1307,7 @@ class PlPlayerController {
/// 设置长按倍速状态 live模式下禁用
Future<void> setLongPressStatus(bool val) async {
if (videoType.value == 'live') {
if (isLive) {
return;
}
if (controlsLock.value) {
@@ -1376,10 +1371,9 @@ class PlPlayerController {
return;
}
if (mode == FullScreenMode.vertical ||
(mode == FullScreenMode.auto && direction.value == 'vertical') ||
(mode == FullScreenMode.auto && isVertical) ||
(mode == FullScreenMode.ratio &&
(Get.height / Get.width < 1.25 ||
direction.value == 'vertical'))) {
(Get.height / Get.width < 1.25 || isVertical))) {
await verticalScreenForTwoSeconds();
} else {
await landScape();
@@ -1445,7 +1439,7 @@ class PlPlayerController {
return;
}
}
if (videoType.value == 'live') {
if (isLive) {
return;
}
bool isComplete =
@@ -1512,9 +1506,9 @@ class PlPlayerController {
..put(SettingBoxKey.subtitleFontWeight, subtitleFontWeight);
}
Future<void> dispose({String type = 'single'}) async {
Future<void> dispose() async {
// 每次减1最后销毁
if (type == 'single' && playerCount.value > 1) {
if (playerCount.value > 1) {
_playerCount.value -= 1;
_heartDuration = 0;
if (!Get.previousRoute.startsWith('/video')) {

View File

@@ -0,0 +1 @@
enum DoubleTapType { left, center, right }

View File

@@ -0,0 +1,10 @@
// ignore_for_file: constant_identifier_names
enum GestureType {
left,
center,
right,
horizontal,
center_up,
center_down,
}

View File

@@ -16,8 +16,10 @@ import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/plugin/pl_player/models/bottom_control_type.dart';
import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart';
import 'package:PiliPlus/plugin/pl_player/models/double_tap_type.dart';
import 'package:PiliPlus/plugin/pl_player/models/duration.dart';
import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart';
import 'package:PiliPlus/plugin/pl_player/models/gesture_type.dart';
import 'package:PiliPlus/plugin/pl_player/utils.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/app_bar_ani.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/backward_seek.dart';
@@ -110,7 +112,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
late final RxBool showRestoreScaleBtn = false.obs;
Offset _initialFocalPoint = Offset.zero;
String? _gestureType;
GestureType? _gestureType;
//播放器放缩
bool interacting = false;
@@ -138,20 +140,20 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
}
void doubleTapFuc(String type) {
void doubleTapFuc(DoubleTapType type) {
if (!plPlayerController.enableQuickDouble) {
onDoubleTapCenter();
return;
}
switch (type) {
case 'left':
case DoubleTapType.left:
// 双击左边区域 👈
onDoubleTapSeekBackward();
break;
case 'center':
case DoubleTapType.center:
onDoubleTapCenter();
break;
case 'right':
case DoubleTapType.right:
// 双击右边区域 👈
onDoubleTapSeekForward();
break;
@@ -770,7 +772,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
if (cumulativeDelta.distance < 1) return;
if (cumulativeDelta.dx.abs() >
3 * cumulativeDelta.dy.abs()) {
_gestureType = 'horizontal';
_gestureType = GestureType.horizontal;
} else if (cumulativeDelta.dy.abs() >
3 * cumulativeDelta.dx.abs()) {
if (!plPlayerController.enableSlideVolumeBrightness &&
@@ -787,19 +789,19 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
return;
}
// 左边区域
_gestureType = 'left';
_gestureType = GestureType.left;
} else if (tapPosition < sectionWidth * 2) {
if (!plPlayerController.enableSlideFS) {
return;
}
// 全屏
_gestureType = 'center';
_gestureType = GestureType.center;
} else {
if (!plPlayerController.enableSlideVolumeBrightness) {
return;
}
// 右边区域
_gestureType = 'right';
_gestureType = GestureType.right;
}
} else {
return;
@@ -808,9 +810,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
Offset delta = details.focalPointDelta;
if (_gestureType == 'horizontal') {
if (_gestureType == GestureType.horizontal) {
// live模式下禁用
if (plPlayerController.videoType.value == 'live') return;
if (plPlayerController.isLive) return;
final int curSliderPosition =
plPlayerController.sliderPosition.value.inMilliseconds;
@@ -884,14 +886,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
} catch (_) {}
}
} else if (_gestureType == 'left') {
} else if (_gestureType == GestureType.left) {
// 左边区域 👈
final double level = maxHeight * 3;
final double brightness =
_brightnessValue.value - delta.dy / level;
final double result = brightness.clamp(0.0, 1.0);
setBrightness(result);
} else if (_gestureType == 'center') {
} else if (_gestureType == GestureType.center) {
// 全屏
const double threshold = 2.5; // 滑动阈值
double cumulativeDy =
@@ -902,7 +904,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
if (cumulativeDy > threshold) {
_gestureType = 'center_down';
_gestureType = GestureType.center_down;
if (isFullScreen ^
plPlayerController.fullScreenGestureReverse) {
fullScreenTrigger(
@@ -911,7 +913,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
// if (kDebugMode) debugPrint('center_down:$cumulativeDy');
} else if (cumulativeDy < -threshold) {
_gestureType = 'center_up';
_gestureType = GestureType.center_up;
if (!isFullScreen ^
plPlayerController.fullScreenGestureReverse) {
fullScreenTrigger(
@@ -920,7 +922,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
// if (kDebugMode) debugPrint('center_up:$cumulativeDy');
}
} else if (_gestureType == 'right') {
} else if (_gestureType == GestureType.right) {
// 右边区域
final double level = maxHeight * 0.5;
EasyThrottle.throttle(
@@ -975,25 +977,25 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
final double tapPosition = details.localPosition.dx;
final double sectionWidth = maxWidth / 3;
late String gestureType;
late GestureType gestureType;
if (tapPosition < sectionWidth) {
if (!plPlayerController.enableSlideVolumeBrightness) {
return;
}
// 左边区域
gestureType = 'left';
gestureType = GestureType.left;
} else if (tapPosition < sectionWidth * 2) {
if (!plPlayerController.enableSlideFS) {
return;
}
// 全屏
gestureType = 'center';
gestureType = GestureType.center;
} else {
if (!plPlayerController.enableSlideVolumeBrightness) {
return;
}
// 右边区域
gestureType = 'right';
gestureType = GestureType.right;
}
if (_gestureType != null && _gestureType != gestureType) {
@@ -1001,14 +1003,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
_gestureType = gestureType;
if (_gestureType == 'left') {
if (_gestureType == GestureType.left) {
// 左边区域 👈
final double level = maxHeight * 3;
final double brightness =
_brightnessValue.value - details.delta.dy / level;
final double result = brightness.clamp(0.0, 1.0);
setBrightness(result);
} else if (_gestureType == 'center') {
} else if (_gestureType == GestureType.center) {
// 全屏
const double threshold = 2.5; // 滑动阈值
double cumulativeDy =
@@ -1019,7 +1021,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
if (cumulativeDy > threshold) {
_gestureType = 'center_down';
_gestureType = GestureType.center_down;
if (isFullScreen ^
plPlayerController.fullScreenGestureReverse) {
fullScreenTrigger(
@@ -1028,7 +1030,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
// if (kDebugMode) debugPrint('center_down:$cumulativeDy');
} else if (cumulativeDy < -threshold) {
_gestureType = 'center_up';
_gestureType = GestureType.center_up;
if (!isFullScreen ^
plPlayerController.fullScreenGestureReverse) {
fullScreenTrigger(
@@ -1037,7 +1039,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
// if (kDebugMode) debugPrint('center_up:$cumulativeDy');
}
} else if (_gestureType == 'right') {
} else if (_gestureType == GestureType.right) {
// 右边区域
final double level = maxHeight * 0.5;
EasyThrottle.throttle(
@@ -1065,19 +1067,19 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
if (plPlayerController.controlsLock.value) {
return;
}
if (plPlayerController.videoType.value == 'live') {
doubleTapFuc('center');
if (plPlayerController.isLive) {
doubleTapFuc(DoubleTapType.center);
return;
}
final double tapPosition = details.localPosition.dx;
final double sectionWidth = maxWidth / 4;
String type = 'left';
DoubleTapType type;
if (tapPosition < sectionWidth) {
type = 'left';
type = DoubleTapType.left;
} else if (tapPosition < sectionWidth * 3) {
type = 'center';
type = DoubleTapType.center;
} else {
type = 'right';
type = DoubleTapType.right;
}
doubleTapFuc(type);
},
@@ -1426,7 +1428,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
}
if (plPlayerController.videoType.value == 'live') {
if (plPlayerController.isLive) {
return const SizedBox.shrink();
}
@@ -1551,9 +1553,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
SafeArea(
child: Obx(
() => Visibility(
visible:
plPlayerController.videoType.value != 'live' &&
isFullScreen,
visible: !plPlayerController.isLive && isFullScreen,
child: Align(
alignment: Alignment.centerLeft,
child: FractionalTranslation(
@@ -1895,7 +1895,7 @@ Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
try {
double scale =
plPlayerController.isFullScreen.value &&
plPlayerController.direction.value == 'horizontal'
!plPlayerController.isVertical
? 4
: 2.5;
// offset

View File

@@ -134,7 +134,7 @@ class SettingBoxKey {
directExitOnBack = 'directExitOnBack',
quickFavId = 'quickFavId';
static const String subtitlePreference = 'subtitlePreference',
static const String subtitlePreferenceV2 = 'subtitlePreferenceV2',
enableDragSubtitle = 'enableDragSubtitle',
subtitlePaddingH = 'subtitlePaddingH',
subtitlePaddingB = 'subtitlePaddingB',

View File

@@ -186,9 +186,9 @@ class Pref {
defaultValue: BtmProgressBehavior.alwaysShow.index,
);
static String get subtitlePreference => _setting.get(
SettingBoxKey.subtitlePreference,
defaultValue: SubtitlePrefType.off.code,
static int get subtitlePreferenceV2 => _setting.get(
SettingBoxKey.subtitlePreferenceV2,
defaultValue: SubtitlePrefType.off.index,
);
static bool get useRelativeSlide =>

View File

@@ -3,6 +3,7 @@ import 'dart:io' show Platform;
import 'package:PiliPlus/build_config.dart';
import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
@@ -22,7 +23,7 @@ class Update {
final res = await Request().get(
Api.latestApp,
options: Options(
headers: {'user-agent': Request.headerUa()},
headers: {'user-agent': UaType.mob.ua},
extra: {'account': NoAccount()},
),
);