mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: 调整设置,支持导入导出,代码优化
This commit is contained in:
@@ -57,11 +57,7 @@ class ListSheet {
|
|||||||
}
|
}
|
||||||
SmartDialog.showToast('切换到:$title');
|
SmartDialog.showToast('切换到:$title');
|
||||||
bottomSheetController.close();
|
bottomSheetController.close();
|
||||||
print(episode.runtimeType.toString());
|
|
||||||
if (episode.runtimeType.toString() == "EpisodeItem") {
|
if (episode.runtimeType.toString() == "EpisodeItem") {
|
||||||
print(episode.bvid);
|
|
||||||
print(episode.cid);
|
|
||||||
print(episode.aid);
|
|
||||||
changeFucCall(episode.bvid, episode.cid, episode.aid);
|
changeFucCall(episode.bvid, episode.cid, episode.aid);
|
||||||
} else {
|
} else {
|
||||||
changeFucCall(bvid!, episode.cid, aid!);
|
changeFucCall(bvid!, episode.cid, aid!);
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
final int defaultImgQuality = GlobalData().imgQuality;
|
final int defaultImgQuality = GlobalData().imgQuality;
|
||||||
final String imageUrl =
|
final String imageUrl =
|
||||||
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp';
|
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp';
|
||||||
print(imageUrl);
|
|
||||||
int? memCacheWidth, memCacheHeight;
|
int? memCacheWidth, memCacheHeight;
|
||||||
|
|
||||||
if (width > height || (origAspectRatio != null && origAspectRatio! > 1)) {
|
if (width > height || (origAspectRatio != null && origAspectRatio! > 1)) {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ class DynamicsHttp {
|
|||||||
'data': DynamicsDataModel.fromJson(res.data['data']),
|
'data': DynamicsDataModel.fromJson(res.data['data']),
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
|
||||||
return {
|
return {
|
||||||
'status': false,
|
'status': false,
|
||||||
'data': [],
|
'data': [],
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class Request {
|
|||||||
|
|
||||||
String jsonData = json.encode({
|
String jsonData = json.encode({
|
||||||
'3064': 1,
|
'3064': 1,
|
||||||
'39c8': '${spmPrefix}.fp.risk',
|
'39c8': '$spmPrefix.fp.risk',
|
||||||
'3c43': {
|
'3c43': {
|
||||||
'adca': 'Linux',
|
'adca': 'Linux',
|
||||||
'bfe9': rand_png_end.substring(rand_png_end.length - 50),
|
'bfe9': rand_png_end.substring(rand_png_end.length - 50),
|
||||||
@@ -138,9 +138,9 @@ class Request {
|
|||||||
enableSystemProxy = setting.get(SettingBoxKey.enableSystemProxy,
|
enableSystemProxy = setting.get(SettingBoxKey.enableSystemProxy,
|
||||||
defaultValue: false) as bool;
|
defaultValue: false) as bool;
|
||||||
systemProxyHost =
|
systemProxyHost =
|
||||||
localCache.get(LocalCacheKey.systemProxyHost, defaultValue: '');
|
setting.get(SettingBoxKey.systemProxyHost, defaultValue: '');
|
||||||
systemProxyPort =
|
systemProxyPort =
|
||||||
localCache.get(LocalCacheKey.systemProxyPort, defaultValue: '');
|
setting.get(SettingBoxKey.systemProxyPort, defaultValue: '');
|
||||||
|
|
||||||
dio = Dio(options);
|
dio = Dio(options);
|
||||||
|
|
||||||
|
|||||||
@@ -89,8 +89,10 @@ class SearchHttp {
|
|||||||
try {
|
try {
|
||||||
switch (searchType) {
|
switch (searchType) {
|
||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
List<int> blackMidsList =
|
List<int> blackMidsList = setting
|
||||||
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
|
.get(SettingBoxKey.blackMidsList, defaultValue: [-1])
|
||||||
|
.map<int>((i) => i as int)
|
||||||
|
.toList();
|
||||||
for (var i in res.data['data']['result']) {
|
for (var i in res.data['data']['result']) {
|
||||||
// 屏蔽推广和拉黑用户
|
// 屏蔽推广和拉黑用户
|
||||||
i['available'] = !blackMidsList.contains(i['mid']);
|
i['available'] = !blackMidsList.contains(i['mid']);
|
||||||
|
|||||||
@@ -45,8 +45,10 @@ class VideoHttp {
|
|||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
List<RecVideoItemModel> list = [];
|
List<RecVideoItemModel> list = [];
|
||||||
List<int> blackMidsList =
|
List<int> blackMidsList = setting
|
||||||
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
|
.get(SettingBoxKey.blackMidsList, defaultValue: [-1])
|
||||||
|
.map<int>((e) => e as int)
|
||||||
|
.toList();
|
||||||
for (var i in res.data['data']['item']) {
|
for (var i in res.data['data']['item']) {
|
||||||
//过滤掉live与ad,以及拉黑用户
|
//过滤掉live与ad,以及拉黑用户
|
||||||
if (i['goto'] == 'av' &&
|
if (i['goto'] == 'av' &&
|
||||||
@@ -91,8 +93,10 @@ class VideoHttp {
|
|||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
List<RecVideoItemAppModel> list = [];
|
List<RecVideoItemAppModel> list = [];
|
||||||
List<int> blackMidsList =
|
List<int> blackMidsList = setting
|
||||||
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
|
.get(SettingBoxKey.blackMidsList, defaultValue: [-1])
|
||||||
|
.map<int>((e) => e as int)
|
||||||
|
.toList();
|
||||||
for (var i in res.data['data']['items']) {
|
for (var i in res.data['data']['items']) {
|
||||||
// 屏蔽推广和拉黑用户
|
// 屏蔽推广和拉黑用户
|
||||||
if (i['card_goto'] != 'ad_av' &&
|
if (i['card_goto'] != 'ad_av' &&
|
||||||
@@ -123,8 +127,10 @@ class VideoHttp {
|
|||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
List<HotVideoItemModel> list = [];
|
List<HotVideoItemModel> list = [];
|
||||||
List<int> blackMidsList =
|
List<int> blackMidsList = setting
|
||||||
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
|
.get(SettingBoxKey.blackMidsList, defaultValue: [-1])
|
||||||
|
.map<int>((e) => e as int)
|
||||||
|
.toList();
|
||||||
for (var i in res.data['data']['list']) {
|
for (var i in res.data['data']['list']) {
|
||||||
if (!blackMidsList.contains(i['owner']['mid'])) {
|
if (!blackMidsList.contains(i['owner']['mid'])) {
|
||||||
list.add(HotVideoItemModel.fromJson(i));
|
list.add(HotVideoItemModel.fromJson(i));
|
||||||
@@ -615,8 +621,10 @@ class VideoHttp {
|
|||||||
var res = await Request().get(rankApi);
|
var res = await Request().get(rankApi);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
List<HotVideoItemModel> list = [];
|
List<HotVideoItemModel> list = [];
|
||||||
List<int> blackMidsList =
|
List<int> blackMidsList = setting
|
||||||
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
|
.get(SettingBoxKey.blackMidsList, defaultValue: [-1])
|
||||||
|
.map<int>((e) => e as int)
|
||||||
|
.toList();
|
||||||
for (var i in res.data['data']['list']) {
|
for (var i in res.data['data']['list']) {
|
||||||
if (!blackMidsList.contains(i['owner']['mid'])) {
|
if (!blackMidsList.contains(i['owner']['mid'])) {
|
||||||
list.add(HotVideoItemModel.fromJson(i));
|
list.add(HotVideoItemModel.fromJson(i));
|
||||||
|
|||||||
@@ -30,10 +30,11 @@ void main() async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
MediaKit.ensureInitialized();
|
MediaKit.ensureInitialized();
|
||||||
await GStrorage.init();
|
await GStrorage.init();
|
||||||
if (GStrorage.setting.get(SettingBoxKey.autoClearCache, defaultValue: false)) {
|
if (GStrorage.setting.get(SettingBoxKey.autoClearCache, defaultValue: true)) {
|
||||||
await CacheManage.clearLibraryCache();
|
await CacheManage.clearLibraryCache();
|
||||||
}
|
}
|
||||||
if (GStrorage.setting.get(SettingBoxKey.horizontalScreen, defaultValue: false)) {
|
if (GStrorage.setting
|
||||||
|
.get(SettingBoxKey.horizontalScreen, defaultValue: false)) {
|
||||||
await SystemChrome.setPreferredOrientations(
|
await SystemChrome.setPreferredOrientations(
|
||||||
//支持竖屏与横屏
|
//支持竖屏与横屏
|
||||||
[
|
[
|
||||||
@@ -115,21 +116,18 @@ class MyApp extends StatelessWidget {
|
|||||||
|
|
||||||
// 强制设置高帧率
|
// 强制设置高帧率
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
try {
|
|
||||||
late List modes;
|
late List modes;
|
||||||
FlutterDisplayMode.supported.then((value) {
|
FlutterDisplayMode.supported.then((value) {
|
||||||
modes = value;
|
modes = value;
|
||||||
var storageDisplay = setting.get(SettingBoxKey.displayMode);
|
var storageDisplay = setting.get(SettingBoxKey.displayMode);
|
||||||
DisplayMode f = DisplayMode.auto;
|
DisplayMode f = DisplayMode.auto;
|
||||||
if (storageDisplay != null) {
|
if (storageDisplay != null) {
|
||||||
f = modes.firstWhere((e) => e.toString() == storageDisplay);
|
f = modes.firstWhere((e) => e.toString() == storageDisplay,
|
||||||
|
orElse: () => f);
|
||||||
}
|
}
|
||||||
DisplayMode preferred = modes.toList().firstWhere((el) => el == f);
|
DisplayMode preferred = modes.toList().firstWhere((el) => el == f);
|
||||||
FlutterDisplayMode.setPreferredMode(preferred);
|
FlutterDisplayMode.setPreferredMode(preferred);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
|
||||||
SmartDialog.showToast('设置帧率失败:$e', displayTime: const Duration(milliseconds: 500));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
|
|||||||
@@ -94,18 +94,18 @@ extension AudioQualityDesc on AudioQuality {
|
|||||||
|
|
||||||
enum VideoDecodeFormats {
|
enum VideoDecodeFormats {
|
||||||
DVH1,
|
DVH1,
|
||||||
AVC,
|
|
||||||
HEVC,
|
|
||||||
AV1,
|
AV1,
|
||||||
|
HEVC,
|
||||||
|
AVC,
|
||||||
}
|
}
|
||||||
|
|
||||||
extension VideoDecodeFormatsDesc on VideoDecodeFormats {
|
extension VideoDecodeFormatsDesc on VideoDecodeFormats {
|
||||||
static final List<String> _descList = ['DVH1', 'AVC', 'HEVC', 'AV1'];
|
static final List<String> _descList = ['DVH1', 'AV1', 'HEVC', 'AVC'];
|
||||||
get description => _descList[index];
|
get description => _descList[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
extension VideoDecodeFormatsCode on VideoDecodeFormats {
|
extension VideoDecodeFormatsCode on VideoDecodeFormats {
|
||||||
static final List<String> _codeList = ['dvh1', 'avc1', 'hev1', 'av01'];
|
static final List<String> _codeList = ['dvh1', 'av01', 'hev1', 'avc1'];
|
||||||
get code => _codeList[index];
|
get code => _codeList[index];
|
||||||
|
|
||||||
static VideoDecodeFormats? fromCode(String code) {
|
static VideoDecodeFormats? fromCode(String code) {
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
() => ListTile(
|
() => ListTile(
|
||||||
onTap: () => _aboutController.tapOnVersion(),
|
onTap: () => _aboutController.tapOnVersion(),
|
||||||
title: const Text('当前版本'),
|
title: const Text('当前版本'),
|
||||||
|
leading: const Icon(Icons.commit_outlined),
|
||||||
trailing: Text(_aboutController.currentVersion.value,
|
trailing: Text(_aboutController.currentVersion.value,
|
||||||
style: subTitleStyle),
|
style: subTitleStyle),
|
||||||
),
|
),
|
||||||
@@ -92,6 +93,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
() => ListTile(
|
() => ListTile(
|
||||||
onTap: () => _aboutController.onUpdate(),
|
onTap: () => _aboutController.onUpdate(),
|
||||||
title: const Text('最新版本'),
|
title: const Text('最新版本'),
|
||||||
|
leading: const Icon(Icons.flag_outlined),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
_aboutController.isLoading.value
|
_aboutController.isLoading.value
|
||||||
? '正在获取'
|
? '正在获取'
|
||||||
@@ -117,6 +119,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.githubUrl(),
|
onTap: () => _aboutController.githubUrl(),
|
||||||
|
leading: const Icon(Icons.star_outline_outlined),
|
||||||
title: const Text('Github开源仓库'),
|
title: const Text('Github开源仓库'),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'github.com/orz12/pilipala',
|
'github.com/orz12/pilipala',
|
||||||
@@ -125,6 +128,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.feedback(),
|
onTap: () => _aboutController.feedback(),
|
||||||
|
leading: const Icon(Icons.feedback_outlined),
|
||||||
title: const Text('问题反馈'),
|
title: const Text('问题反馈'),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
Icons.arrow_forward_ios,
|
Icons.arrow_forward_ios,
|
||||||
@@ -134,33 +138,37 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.qqGroup(),
|
onTap: () => _aboutController.qqGroup(),
|
||||||
title: const Text('QQ群:392176105'),
|
leading: const Icon(Icons.group_add_outlined),
|
||||||
trailing: Icon(
|
title: const Text('QQ群'),
|
||||||
Icons.arrow_forward_ios,
|
trailing: Text(
|
||||||
size: 16,
|
'392176105',
|
||||||
color: outline,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.tgChanel(),
|
onTap: () => _aboutController.tgChannel(),
|
||||||
|
leading: const Icon(Icons.group_add_outlined),
|
||||||
title: const Text('TG频道'),
|
title: const Text('TG频道'),
|
||||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.webSiteUrl(),
|
onTap: () => _aboutController.webSiteUrl(),
|
||||||
title: const Text('访问官网'),
|
leading: const Icon(Icons.language),
|
||||||
|
title: const Text('官网'),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'https://pilipalanet.mysxl.cn/pilipala-x',
|
'pilipalanet.mysxl.cn/pilipala-x',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.aPay(),
|
onTap: () => _aboutController.aPay(),
|
||||||
|
leading: const Icon(Icons.wallet_giftcard_outlined),
|
||||||
title: const Text('赞赏'),
|
title: const Text('赞赏'),
|
||||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.logs(),
|
onTap: () => _aboutController.logs(),
|
||||||
|
leading: const Icon(Icons.bug_report_outlined),
|
||||||
title: const Text('错误日志'),
|
title: const Text('错误日志'),
|
||||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||||
),
|
),
|
||||||
@@ -169,11 +177,78 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
await CacheManage().clearCacheAll();
|
await CacheManage().clearCacheAll();
|
||||||
getCacheSize();
|
getCacheSize();
|
||||||
},
|
},
|
||||||
|
leading: const Icon(Icons.delete_outline),
|
||||||
title: const Text('清除缓存'),
|
title: const Text('清除缓存'),
|
||||||
subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle),
|
subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('导入/导出设置'),
|
||||||
|
dense: false,
|
||||||
|
leading: const Icon(Icons.import_export_outlined),
|
||||||
|
onTap: () {
|
||||||
|
SmartDialog.show(
|
||||||
|
useSystem: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: const Text('导入/导出设置'),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text('导出设置至剪贴板'),
|
||||||
|
onTap: () async {
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
String data = await GStrorage.exportAllSettings();
|
||||||
|
Clipboard.setData(ClipboardData(text: data));
|
||||||
|
SmartDialog.showToast('已复制到剪贴板');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('从剪贴板导入设置'),
|
||||||
|
onTap: () async {
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
ClipboardData? data = await Clipboard.getData('text/plain');
|
||||||
|
if (data == null || data.text == null || data.text!.isEmpty) {
|
||||||
|
SmartDialog.showToast('剪贴板无数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SmartDialog.show(
|
||||||
|
useSystem: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('是否导入如下设置?'),
|
||||||
|
content: Text(data.text!),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
},
|
||||||
|
child: const Text('取消'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
try{
|
||||||
|
await GStrorage.importAllSettings(data.text!);
|
||||||
|
SmartDialog.showToast('导入成功');
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('导入失败:$e');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('重置所有设置'),
|
title: const Text('重置所有设置'),
|
||||||
|
leading: const Icon(Icons.settings_backup_restore_outlined),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SmartDialog.show(
|
SmartDialog.show(
|
||||||
useSystem: true,
|
useSystem: true,
|
||||||
@@ -191,6 +266,8 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
GStrorage.setting.clear();
|
GStrorage.setting.clear();
|
||||||
|
GStrorage.localCache.clear();
|
||||||
|
GStrorage.video.clear();
|
||||||
SmartDialog.showToast('重置成功');
|
SmartDialog.showToast('重置成功');
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
@@ -312,24 +389,24 @@ class AboutController extends GetxController {
|
|||||||
useSystem: true,
|
useSystem: true,
|
||||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return SimpleDialog(
|
||||||
title: const Text('问题反馈'),
|
title: const Text('问题反馈'),
|
||||||
actions: [
|
children: [
|
||||||
ElevatedButton(
|
ListTile(
|
||||||
onPressed: () => launchUrl(
|
title: const Text('GitHub Issue'),
|
||||||
|
onTap: () => launchUrl(
|
||||||
Uri.parse('https://github.com/orz12/pilipala/issues'),
|
Uri.parse('https://github.com/orz12/pilipala/issues'),
|
||||||
// 系统自带浏览器打开
|
// 系统自带浏览器打开
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
),
|
),
|
||||||
child: const Text('GitHub Issue'),
|
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ListTile(
|
||||||
onPressed: () => launchUrl(
|
title: const Text('腾讯兔小巢'),
|
||||||
|
onTap: () => launchUrl(
|
||||||
Uri.parse('https://support.qq.com/embed/phone/637735'),
|
Uri.parse('https://support.qq.com/embed/phone/637735'),
|
||||||
// 系统自带浏览器打开
|
// 系统自带浏览器打开
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
),
|
),
|
||||||
child: const Text('腾讯兔小巢'),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -355,7 +432,7 @@ class AboutController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tg频道
|
// tg频道
|
||||||
tgChanel() {
|
tgChannel() {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
const ClipboardData(text: 'https://t.me/+162zlPtZlT9hNWVl'),
|
const ClipboardData(text: 'https://t.me/+162zlPtZlT9hNWVl'),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPalaX/common/skeleton/video_card_h.dart';
|
|
||||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
|
||||||
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
|
||||||
import 'package:PiliPalaX/pages/history/widgets/item.dart';
|
import 'package:PiliPalaX/pages/history/widgets/item.dart';
|
||||||
|
|
||||||
import '../../common/constants.dart';
|
import '../../common/constants.dart';
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
void setTabConfig() async {
|
void setTabConfig() async {
|
||||||
defaultTabs = [...tabsConfig];
|
defaultTabs = [...tabsConfig];
|
||||||
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
||||||
defaultValue: ['live', 'rcmd', 'hot', 'bangumi']);
|
defaultValue: ['live', 'rcmd', 'hot', 'bangumi']).map<String>((i) => i.toString()).toList();
|
||||||
defaultTabs.retainWhere(
|
defaultTabs.retainWhere(
|
||||||
(item) => tabbarSort.contains((item['type'] as TabType).id));
|
(item) => tabbarSort.contains((item['type'] as TabType).id));
|
||||||
defaultTabs.sort((a, b) => tabbarSort
|
defaultTabs.sort((a, b) => tabbarSort
|
||||||
|
|||||||
@@ -184,7 +184,8 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
height: snapshot.data ? top + 52 : top,
|
height: snapshot.data ? top + 52 : top,
|
||||||
padding: EdgeInsets.fromLTRB(14, top + 6, 14, 0),
|
padding: EdgeInsets.fromLTRB(14, top + 6, 14, 0),
|
||||||
child: UserInfoWidget(
|
child: Obx(
|
||||||
|
() => UserInfoWidget(
|
||||||
top: top,
|
top: top,
|
||||||
ctr: ctr,
|
ctr: ctr,
|
||||||
userLogin: isUserLoggedIn,
|
userLogin: isUserLoggedIn,
|
||||||
@@ -192,6 +193,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
callback: () => callback!(),
|
callback: () => callback!(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -219,9 +221,9 @@ class UserInfoWidget extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
SearchBar(ctr: ctr),
|
SearchBar(ctr: ctr),
|
||||||
if (userLogin.value) ...[
|
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
ClipRect(
|
Obx(() => userLogin.value
|
||||||
|
? ClipRect(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: '消息',
|
tooltip: '消息',
|
||||||
onPressed: () => Get.toNamed('/whisper'),
|
onPressed: () => Get.toNamed('/whisper'),
|
||||||
@@ -230,7 +232,7 @@ class UserInfoWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
: const SizedBox.shrink()),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Semantics(
|
Semantics(
|
||||||
label: "我的",
|
label: "我的",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
|
||||||
import 'package:floating/floating.dart';
|
import 'package:floating/floating.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
int? _lastSelectTime; //上次点击时间
|
int? _lastSelectTime; //上次点击时间
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
late bool enableMYBar;
|
late bool enableMYBar;
|
||||||
late bool horizontalScreen;
|
late bool adaptiveNavBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -40,7 +40,8 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
_mainController.pageController =
|
_mainController.pageController =
|
||||||
PageController(initialPage: _mainController.selectedIndex);
|
PageController(initialPage: _mainController.selectedIndex);
|
||||||
enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true);
|
enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true);
|
||||||
horizontalScreen = setting.get(SettingBoxKey.horizontalScreen, defaultValue: false);
|
adaptiveNavBar =
|
||||||
|
setting.get(SettingBoxKey.adaptiveNavBar, defaultValue: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setIndex(int value) async {
|
void setIndex(int value) async {
|
||||||
@@ -112,7 +113,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
onPopInvoked: (bool didPop) async {
|
onPopInvoked: (bool didPop) async {
|
||||||
_mainController.onBackPressed(context);
|
_mainController.onBackPressed(context);
|
||||||
},
|
},
|
||||||
child: horizontalScreen
|
child: adaptiveNavBar
|
||||||
? AdaptiveScaffold(
|
? AdaptiveScaffold(
|
||||||
body: (_) => PageView(
|
body: (_) => PageView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
@@ -123,31 +124,32 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
},
|
},
|
||||||
children: _mainController.pages,
|
children: _mainController.pages,
|
||||||
),
|
),
|
||||||
destinations: _mainController.navigationBars.map((e) => NavigationDestination(
|
destinations: _mainController.navigationBars
|
||||||
|
.map((e) => NavigationDestination(
|
||||||
icon: Badge(
|
icon: Badge(
|
||||||
label: _mainController.dynamicBadgeType ==
|
label: _mainController.dynamicBadgeType ==
|
||||||
DynamicBadgeMode.number
|
DynamicBadgeMode.number
|
||||||
? Text(e['count'].toString())
|
? Text(e['count'].toString())
|
||||||
: null,
|
: null,
|
||||||
padding: const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
padding: const EdgeInsets.fromLTRB(2, 0, 2, 0),
|
||||||
isLabelVisible:
|
isLabelVisible: _mainController.dynamicBadgeType !=
|
||||||
_mainController.dynamicBadgeType !=
|
|
||||||
DynamicBadgeMode.hidden &&
|
DynamicBadgeMode.hidden &&
|
||||||
e['count'] > 0,
|
e['count'] > 0,
|
||||||
child: e['icon'],
|
child: e['icon'],
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).colorScheme.primary,
|
Theme.of(context).colorScheme.primary,
|
||||||
textColor: Theme.of(context)
|
textColor:
|
||||||
.colorScheme
|
Theme.of(context).colorScheme.onInverseSurface,
|
||||||
.onInverseSurface,
|
|
||||||
),
|
),
|
||||||
selectedIcon: e['selectIcon'],
|
selectedIcon: e['selectIcon'],
|
||||||
label: e['label'],
|
label: e['label'],
|
||||||
)).toList(),
|
))
|
||||||
|
.toList(),
|
||||||
onSelectedIndexChange: (value) => setIndex(value),
|
onSelectedIndexChange: (value) => setIndex(value),
|
||||||
selectedIndex: _mainController.selectedIndex,
|
selectedIndex: _mainController.selectedIndex,
|
||||||
useDrawer: false
|
extendedNavigationRailWidth: 180,
|
||||||
)
|
transitionDuration: const Duration(milliseconds: 500),
|
||||||
|
useDrawer: true)
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
body: PageView(
|
body: PageView(
|
||||||
@@ -181,7 +183,8 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
DynamicBadgeMode.number
|
DynamicBadgeMode.number
|
||||||
? Text(e['count'].toString())
|
? Text(e['count'].toString())
|
||||||
: null,
|
: null,
|
||||||
padding: const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
||||||
isLabelVisible:
|
isLabelVisible:
|
||||||
_mainController.dynamicBadgeType !=
|
_mainController.dynamicBadgeType !=
|
||||||
DynamicBadgeMode.hidden &&
|
DynamicBadgeMode.hidden &&
|
||||||
@@ -218,7 +221,8 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
DynamicBadgeMode.number
|
DynamicBadgeMode.number
|
||||||
? Text(e['count'].toString())
|
? Text(e['count'].toString())
|
||||||
: null,
|
: null,
|
||||||
padding: const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
||||||
isLabelVisible:
|
isLabelVisible:
|
||||||
_mainController.dynamicBadgeType !=
|
_mainController.dynamicBadgeType !=
|
||||||
DynamicBadgeMode.hidden &&
|
DynamicBadgeMode.hidden &&
|
||||||
|
|||||||
@@ -79,6 +79,16 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
tooltip: '设置',
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed('/setting');
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.settings_outlined,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
)
|
||||||
),
|
),
|
||||||
for (var i in mediaController.list) ...[
|
for (var i in mediaController.list) ...[
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:PiliPalaX/common/constants.dart';
|
import 'package:PiliPalaX/common/constants.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -28,8 +27,6 @@ class _RankPageState extends State<RankPage>
|
|||||||
TabController(vsync: this, length: _rankController.tabs.length);
|
TabController(vsync: this, length: _rankController.tabs.length);
|
||||||
_selectedTabIndex = _rankController.initialIndex.value;
|
_selectedTabIndex = _rankController.initialIndex.value;
|
||||||
_rankController.tabController.addListener(() {
|
_rankController.tabController.addListener(() {
|
||||||
print("_rankController.tabController.index");
|
|
||||||
print(_rankController.tabController.index);
|
|
||||||
if (!_rankController.tabController.indexIsChanging) {
|
if (!_rankController.tabController.indexIsChanging) {
|
||||||
// _rankController.onRefresh();
|
// _rankController.onRefresh();
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class ExtraSetting extends StatefulWidget {
|
|||||||
class _ExtraSettingState extends State<ExtraSetting> {
|
class _ExtraSettingState extends State<ExtraSetting> {
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
final SettingController settingController = Get.put(SettingController());
|
final SettingController settingController = Get.put(SettingController());
|
||||||
static Box localCache = GStrorage.localCache;
|
|
||||||
late dynamic defaultReplySort;
|
late dynamic defaultReplySort;
|
||||||
late dynamic defaultDynamicType;
|
late dynamic defaultDynamicType;
|
||||||
late dynamic enableSystemProxy;
|
late dynamic enableSystemProxy;
|
||||||
@@ -45,9 +44,9 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
enableSystemProxy =
|
enableSystemProxy =
|
||||||
setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false);
|
setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false);
|
||||||
defaultSystemProxyHost =
|
defaultSystemProxyHost =
|
||||||
localCache.get(LocalCacheKey.systemProxyHost, defaultValue: '');
|
setting.get(SettingBoxKey.systemProxyHost, defaultValue: '');
|
||||||
defaultSystemProxyPort =
|
defaultSystemProxyPort =
|
||||||
localCache.get(LocalCacheKey.systemProxyPort, defaultValue: '');
|
setting.get(SettingBoxKey.systemProxyPort, defaultValue: '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置代理
|
// 设置代理
|
||||||
@@ -111,8 +110,8 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
localCache.put(LocalCacheKey.systemProxyHost, systemProxyHost);
|
setting.put(SettingBoxKey.systemProxyHost, systemProxyHost);
|
||||||
localCache.put(LocalCacheKey.systemProxyPort, systemProxyPort);
|
setting.put(SettingBoxKey.systemProxyPort, systemProxyPort);
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
// Request.dio;
|
// Request.dio;
|
||||||
},
|
},
|
||||||
@@ -146,6 +145,7 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
() => ListTile(
|
() => ListTile(
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
onTap: () => settingController.onOpenFeedBack(),
|
onTap: () => settingController.onOpenFeedBack(),
|
||||||
|
leading: const Icon(Icons.vibration_outlined),
|
||||||
title: Text('震动反馈', style: titleStyle),
|
title: Text('震动反馈', style: titleStyle),
|
||||||
subtitle: Text('请确定手机设置中已开启震动反馈', style: subTitleStyle),
|
subtitle: Text('请确定手机设置中已开启震动反馈', style: subTitleStyle),
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
@@ -168,12 +168,14 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '大家都在搜',
|
title: '大家都在搜',
|
||||||
subTitle: '是否展示「大家都在搜」',
|
subTitle: '是否展示「大家都在搜」',
|
||||||
|
leading: Icon(Icons.data_thresholding_outlined),
|
||||||
setKey: SettingBoxKey.enableHotKey,
|
setKey: SettingBoxKey.enableHotKey,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
SetSwitchItem(
|
SetSwitchItem(
|
||||||
title: '搜索默认词',
|
title: '搜索默认词',
|
||||||
subTitle: '是否展示搜索框默认词',
|
subTitle: '是否展示搜索框默认词',
|
||||||
|
leading: const Icon(Icons.whatshot_outlined),
|
||||||
setKey: SettingBoxKey.enableSearchWord,
|
setKey: SettingBoxKey.enableSearchWord,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
callFn: (val) {
|
callFn: (val) {
|
||||||
@@ -183,30 +185,35 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '快速收藏',
|
title: '快速收藏',
|
||||||
subTitle: '点按收藏至默认,长按选择文件夹',
|
subTitle: '点按收藏至默认,长按选择文件夹',
|
||||||
|
leading: Icon(Icons.bookmark_add_outlined),
|
||||||
setKey: SettingBoxKey.enableQuickFav,
|
setKey: SettingBoxKey.enableQuickFav,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '评论区搜索关键词',
|
title: '评论区搜索关键词',
|
||||||
subTitle: '展示评论区搜索关键词',
|
subTitle: '展示评论区搜索关键词',
|
||||||
|
leading: Icon(Icons.search_outlined),
|
||||||
setKey: SettingBoxKey.enableWordRe,
|
setKey: SettingBoxKey.enableWordRe,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '启用ai总结',
|
title: '启用ai总结',
|
||||||
subTitle: '视频详情页开启ai总结',
|
subTitle: '视频详情页开启ai总结',
|
||||||
|
leading: Icon(Icons.engineering_outlined),
|
||||||
setKey: SettingBoxKey.enableAi,
|
setKey: SettingBoxKey.enableAi,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '消息页禁用“收到的赞”功能',
|
title: '消息页禁用“收到的赞”功能',
|
||||||
subTitle: '禁止打开入口,降低网络社交依赖',
|
subTitle: '禁止打开入口,降低网络社交依赖',
|
||||||
|
leading: Icon(Icons.beach_access_outlined),
|
||||||
setKey: SettingBoxKey.disableLikeMsg,
|
setKey: SettingBoxKey.disableLikeMsg,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('评论展示', style: titleStyle),
|
title: Text('评论展示', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.whatshot_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前优先展示「${ReplySortType.values[defaultReplySort].titles}」',
|
'当前优先展示「${ReplySortType.values[defaultReplySort].titles}」',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -233,6 +240,7 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('动态展示', style: titleStyle),
|
title: Text('动态展示', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.dynamic_feed_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前优先展示「${DynamicsType.values[defaultDynamicType].labels}」',
|
'当前优先展示「${DynamicsType.values[defaultDynamicType].labels}」',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -259,6 +267,7 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
onTap: () => twoFADialog(),
|
onTap: () => twoFADialog(),
|
||||||
|
leading: const Icon(Icons.airplane_ticket_outlined),
|
||||||
title: Text('设置代理', style: titleStyle),
|
title: Text('设置代理', style: titleStyle),
|
||||||
subtitle: Text('设置代理 host:port', style: subTitleStyle),
|
subtitle: Text('设置代理 host:port', style: subTitleStyle),
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
@@ -287,12 +296,14 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '自动清除缓存',
|
title: '自动清除缓存',
|
||||||
subTitle: '每次启动时清除缓存',
|
subTitle: '每次启动时清除缓存',
|
||||||
|
leading: Icon(Icons.auto_delete_outlined),
|
||||||
setKey: SettingBoxKey.autoClearCache,
|
setKey: SettingBoxKey.autoClearCache,
|
||||||
defaultVal: false,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '检查更新',
|
title: '检查更新',
|
||||||
subTitle: '每次启动时检查是否需要更新',
|
subTitle: '每次启动时检查是否需要更新',
|
||||||
|
leading: Icon(Icons.system_update_alt_outlined),
|
||||||
setKey: SettingBoxKey.autoUpdate,
|
setKey: SettingBoxKey.autoUpdate,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ class _HiddenSettingState extends State<HiddenSetting> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
|
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
|
||||||
TextStyle subTitleStyle = Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.copyWith(color: Theme.of(context).colorScheme.outline);
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
|
|||||||
@@ -65,30 +65,35 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '弹幕开关',
|
title: '弹幕开关',
|
||||||
subTitle: '是否展示弹幕',
|
subTitle: '是否展示弹幕',
|
||||||
|
leading: Icon(Icons.comment_outlined),
|
||||||
setKey: SettingBoxKey.enableShowDanmaku,
|
setKey: SettingBoxKey.enableShowDanmaku,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/playSpeedSet'),
|
onTap: () => Get.toNamed('/playSpeedSet'),
|
||||||
|
leading: const Icon(Icons.speed_outlined),
|
||||||
title: Text('倍速设置', style: titleStyle),
|
title: Text('倍速设置', style: titleStyle),
|
||||||
subtitle: Text('设置视频播放速度', style: subTitleStyle),
|
subtitle: Text('设置视频播放速度', style: subTitleStyle),
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '自动播放',
|
title: '自动播放',
|
||||||
subTitle: '进入详情页自动播放',
|
subTitle: '进入详情页自动播放',
|
||||||
|
leading: Icon(Icons.motion_photos_auto_outlined),
|
||||||
setKey: SettingBoxKey.autoPlayEnable,
|
setKey: SettingBoxKey.autoPlayEnable,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '双击快退/快进',
|
title: '双击快退/快进',
|
||||||
subTitle: '左侧双击快退,右侧双击快进',
|
subTitle: '左侧双击快退,右侧双击快进',
|
||||||
|
leading: Icon(Icons.touch_app_outlined),
|
||||||
setKey: SettingBoxKey.enableQuickDouble,
|
setKey: SettingBoxKey.enableQuickDouble,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('自动启用字幕', style: titleStyle),
|
title: Text('自动启用字幕', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.closed_caption_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前选择偏好:'
|
'当前选择偏好:'
|
||||||
'${SubtitlePreferenceCode.fromCode(defaultSubtitlePreference)!.description}',
|
'${SubtitlePreferenceCode.fromCode(defaultSubtitlePreference)!.description}',
|
||||||
@@ -116,30 +121,35 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '竖屏扩大展示',
|
title: '竖屏扩大展示',
|
||||||
subTitle: '小屏竖屏视频宽高比由16:9扩大至4:5(!暂不支持临时收起)',
|
subTitle: '小屏竖屏视频宽高比由16:9扩大至4:5(!暂不支持临时收起)',
|
||||||
|
leading: Icon(Icons.expand_outlined),
|
||||||
setKey: SettingBoxKey.enableVerticalExpand,
|
setKey: SettingBoxKey.enableVerticalExpand,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '自动全屏',
|
title: '自动全屏',
|
||||||
subTitle: '视频开始播放时进入全屏',
|
subTitle: '视频开始播放时进入全屏',
|
||||||
|
leading: Icon(Icons.fullscreen_outlined),
|
||||||
setKey: SettingBoxKey.enableAutoEnter,
|
setKey: SettingBoxKey.enableAutoEnter,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '自动退出全屏',
|
title: '自动退出全屏',
|
||||||
subTitle: '视频结束播放时退出全屏',
|
subTitle: '视频结束播放时退出全屏',
|
||||||
|
leading: Icon(Icons.fullscreen_exit_outlined),
|
||||||
setKey: SettingBoxKey.enableAutoExit,
|
setKey: SettingBoxKey.enableAutoExit,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '全向旋转',
|
title: '全向旋转',
|
||||||
subTitle: '非全屏时可受重力转为临时全屏,若系统锁定旋转仍异常触发请关闭,无异常可保持开启',
|
subTitle: '小屏可受重力转为临时全屏,若系统锁定旋转仍触发请关闭,关闭会影响横屏适配',
|
||||||
|
leading: Icon(Icons.screen_rotation_alt_outlined),
|
||||||
setKey: SettingBoxKey.allowRotateScreen,
|
setKey: SettingBoxKey.allowRotateScreen,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '后台播放',
|
title: '后台播放',
|
||||||
subTitle: '进入后台时继续播放',
|
subTitle: '进入后台时继续播放',
|
||||||
|
leading: Icon(Icons.motion_photos_pause_outlined),
|
||||||
setKey: SettingBoxKey.continuePlayInBackground,
|
setKey: SettingBoxKey.continuePlayInBackground,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
@@ -147,6 +157,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
SetSwitchItem(
|
SetSwitchItem(
|
||||||
title: '后台画中画',
|
title: '后台画中画',
|
||||||
subTitle: '进入后台时以小窗形式(PiP)播放',
|
subTitle: '进入后台时以小窗形式(PiP)播放',
|
||||||
|
leading: const Icon(Icons.picture_in_picture_outlined),
|
||||||
setKey: SettingBoxKey.autoPiP,
|
setKey: SettingBoxKey.autoPiP,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
callFn: (val) {
|
callFn: (val) {
|
||||||
@@ -160,26 +171,30 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '画中画不加载弹幕',
|
title: '画中画不加载弹幕',
|
||||||
subTitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验',
|
subTitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验',
|
||||||
|
leading: Icon(Icons.comments_disabled_outlined),
|
||||||
setKey: SettingBoxKey.pipNoDanmaku,
|
setKey: SettingBoxKey.pipNoDanmaku,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '全屏手势反向',
|
title: '全屏手势反向',
|
||||||
subTitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出',
|
subTitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出',
|
||||||
|
leading: Icon(Icons.swap_vert_outlined),
|
||||||
setKey: SettingBoxKey.fullScreenGestureReverse,
|
setKey: SettingBoxKey.fullScreenGestureReverse,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '观看人数',
|
title: '观看人数',
|
||||||
subTitle: '展示同时在看人数',
|
subTitle: '展示同时在看人数',
|
||||||
|
leading: Icon(Icons.people_outlined),
|
||||||
setKey: SettingBoxKey.enableOnlineTotal,
|
setKey: SettingBoxKey.enableOnlineTotal,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('默认全屏方式', style: titleStyle),
|
title: Text('默认全屏方向', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.open_with_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前全屏方式:${FullScreenModeCode.fromCode(defaultFullScreenMode)!.description}',
|
'当前全屏方向:${FullScreenModeCode.fromCode(defaultFullScreenMode)!.description}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@@ -187,7 +202,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return SelectDialog<int>(
|
return SelectDialog<int>(
|
||||||
title: '默认全屏方式',
|
title: '默认全屏方向',
|
||||||
value: defaultFullScreenMode,
|
value: defaultFullScreenMode,
|
||||||
values: FullScreenMode.values.map((e) {
|
values: FullScreenMode.values.map((e) {
|
||||||
return {'title': e.description, 'value': e.code};
|
return {'title': e.description, 'value': e.code};
|
||||||
@@ -204,6 +219,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('底部进度条展示', style: titleStyle),
|
title: Text('底部进度条展示', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.border_bottom_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前展示方式:${BtmProgresBehaviorCode.fromCode(defaultBtmProgressBehavior)!.description}',
|
'当前展示方式:${BtmProgresBehaviorCode.fromCode(defaultBtmProgressBehavior)!.description}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -230,6 +246,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '后台音频服务',
|
title: '后台音频服务',
|
||||||
subTitle: '避免画中画没有播放暂停功能',
|
subTitle: '避免画中画没有播放暂停功能',
|
||||||
|
leading: Icon(Icons.volume_up_outlined),
|
||||||
setKey: SettingBoxKey.enableBackgroundPlay,
|
setKey: SettingBoxKey.enableBackgroundPlay,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import 'package:PiliPalaX/utils/cookie.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@@ -6,6 +8,11 @@ import 'package:PiliPalaX/http/interceptor_anonymity.dart';
|
|||||||
import 'package:PiliPalaX/http/member.dart';
|
import 'package:PiliPalaX/http/member.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
|
||||||
|
import '../../http/user.dart';
|
||||||
|
import '../../models/user/info.dart';
|
||||||
|
import '../../utils/login.dart';
|
||||||
|
import '../home/controller.dart';
|
||||||
|
import '../media/controller.dart';
|
||||||
import '../mine/controller.dart';
|
import '../mine/controller.dart';
|
||||||
|
|
||||||
class PrivacySetting extends StatefulWidget {
|
class PrivacySetting extends StatefulWidget {
|
||||||
@@ -18,13 +25,16 @@ class PrivacySetting extends StatefulWidget {
|
|||||||
class _PrivacySettingState extends State<PrivacySetting> {
|
class _PrivacySettingState extends State<PrivacySetting> {
|
||||||
bool userLogin = false;
|
bool userLogin = false;
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
var userInfo;
|
UserInfoData? userInfo;
|
||||||
|
late bool hiddenSettingUnlocked;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
userLogin = userInfo != null;
|
userLogin = userInfo != null;
|
||||||
|
hiddenSettingUnlocked = GStrorage.setting
|
||||||
|
.get(SettingBoxKey.hiddenSettingUnlocked, defaultValue: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -56,6 +66,7 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
dense: false,
|
dense: false,
|
||||||
title: Text('黑名单管理', style: titleStyle),
|
title: Text('黑名单管理', style: titleStyle),
|
||||||
subtitle: Text('已拉黑用户', style: subTitleStyle),
|
subtitle: Text('已拉黑用户', style: subTitleStyle),
|
||||||
|
leading: const Icon(Icons.block),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -66,15 +77,33 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
},
|
},
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('刷新access_key', style: titleStyle),
|
title: Text('刷新access_key', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.perm_device_info_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'access_key是app端的用户凭证,用于推荐接口。刷新将使用cookie请求新的access_key,小概率导致其他设备下线。若发现app端推荐内容不是个性化内容,可尝试刷新',
|
'用于app端推荐接口的用户凭证。刷新有小概率导致其他设备下线。若app端未推荐个性化内容,可尝试刷新或清除本app数据后重新登录',
|
||||||
style: subTitleStyle),
|
style: subTitleStyle),
|
||||||
),
|
),
|
||||||
|
if (hiddenSettingUnlocked)
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
'导入/导出cookie',
|
||||||
|
style: titleStyle,
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'cookie代表您的登录状态,仅供高级用户使用',
|
||||||
|
style: subTitleStyle,
|
||||||
|
),
|
||||||
|
leading: const Icon(Icons.cookie_outlined),
|
||||||
|
dense: false,
|
||||||
|
onTap: () {
|
||||||
|
import_export_cookies(titleStyle, subTitleStyle);
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
MineController.onChangeAnonymity(context);
|
MineController.onChangeAnonymity(context);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
leading: const Icon(Icons.privacy_tip_outlined),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text(MineController.anonymity ? '退出无痕模式' : '进入无痕模式',
|
title: Text(MineController.anonymity ? '退出无痕模式' : '进入无痕模式',
|
||||||
style: titleStyle),
|
style: titleStyle),
|
||||||
@@ -104,6 +133,7 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
leading: const Icon(Icons.flag_outlined),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('了解无痕模式', style: titleStyle),
|
title: Text('了解无痕模式', style: titleStyle),
|
||||||
subtitle: Text('查看无痕模式作用的API列表', style: subTitleStyle),
|
subtitle: Text('查看无痕模式作用的API列表', style: subTitleStyle),
|
||||||
@@ -112,4 +142,146 @@ class _PrivacySettingState extends State<PrivacySetting> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void import_export_cookies(TextStyle titleStyle, TextStyle subTitleStyle) {
|
||||||
|
SmartDialog.show(
|
||||||
|
useSystem: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: const Text('导入/导出cookie', style: TextStyle(color: Colors.red)),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
'导出cookie至剪贴板',
|
||||||
|
style: titleStyle.copyWith(color: Colors.red),
|
||||||
|
),
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.warning_amber,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'泄露账号cookie等同于绕过账号密码与验证码直接登录,可导致隐私泄露、风控、毁号、盗号等各类问题。\n'
|
||||||
|
'你应妥善保管该cookie且仅供自己使用。你承诺,不会利用本服务进行任何违法或不当的活动。你承诺,对所进行的一切活动'
|
||||||
|
'(包括但不限于网上点击同意或提交各类规则协议或购买服务、分享资讯或图片等)负全部责任。\n'
|
||||||
|
'你承诺、理解、同意并确认,在你的账户遭到未获授权的使用,或者发生其他任何安全问题时,'
|
||||||
|
'作者不对上述情形产生的任何直接或间接的遗失或损害承担责任。',
|
||||||
|
style: subTitleStyle.copyWith(color: Colors.redAccent),
|
||||||
|
),
|
||||||
|
dense: false,
|
||||||
|
onTap: () async {
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
if (!userLogin) {
|
||||||
|
SmartDialog.showToast('请先登录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String cookie = await CookieTool.exportCookie();
|
||||||
|
await SmartDialog.show(
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('导出cookie(危险)',
|
||||||
|
style: TextStyle(color: Colors.red)),
|
||||||
|
content: Text(cookie),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(text: cookie));
|
||||||
|
},
|
||||||
|
child: const Text('复制(危险)',
|
||||||
|
style: TextStyle(color: Colors.red)),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
},
|
||||||
|
child: const Text('取消'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
'从剪贴板导入cookie',
|
||||||
|
style: titleStyle,
|
||||||
|
),
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.warning_amber,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'导入将覆盖当前登录状态,你应自行对利用服务从事的所有行为及结果承担责任,请慎用',
|
||||||
|
style: subTitleStyle,
|
||||||
|
),
|
||||||
|
dense: false,
|
||||||
|
onTap: () async {
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
ClipboardData? data = await Clipboard.getData('text/plain');
|
||||||
|
if (data == null || data.text == null || data.text == '') {
|
||||||
|
SmartDialog.showToast('未检测到剪贴板内容');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await SmartDialog.show(
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('导入剪贴板中的cookie'),
|
||||||
|
content: Text(data.text!),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
},
|
||||||
|
child: const Text('取消'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await SmartDialog.dismiss();
|
||||||
|
final String cookie = data.text!;
|
||||||
|
try {
|
||||||
|
await CookieTool.importCookie(cookie);
|
||||||
|
await SmartDialog.showToast('已导入');
|
||||||
|
await CookieTool.onSet();
|
||||||
|
final result = await UserHttp.userInfo();
|
||||||
|
if (result['status'] &&
|
||||||
|
result['data'].isLogin) {
|
||||||
|
SmartDialog.showToast('登录成功,当前采用「'
|
||||||
|
'${GStrorage.setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web')}'
|
||||||
|
'端」推荐');
|
||||||
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
|
await userInfoCache.put(
|
||||||
|
'userInfoCache', result['data']);
|
||||||
|
final HomeController homeCtr =
|
||||||
|
Get.find<HomeController>();
|
||||||
|
homeCtr.updateLoginStatus(true);
|
||||||
|
homeCtr.userFace.value = result['data'].face;
|
||||||
|
final MediaController mediaCtr =
|
||||||
|
Get.find<MediaController>();
|
||||||
|
mediaCtr.mid = result['data'].mid;
|
||||||
|
await LoginUtils.refreshLoginStatus(true);
|
||||||
|
Get.back();
|
||||||
|
} else {
|
||||||
|
// 获取用户信息失败
|
||||||
|
SmartDialog.showNotify(
|
||||||
|
msg:
|
||||||
|
'登录失败,请检查cookie是否正确,${result['message']}',
|
||||||
|
notifyType: NotifyType.warning);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('导入失败:$e');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('确认'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: Text(
|
title: Text(
|
||||||
'推荐设置',
|
'推荐流设置',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -66,6 +66,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('首页推荐类型', style: titleStyle),
|
title: Text('首页推荐类型', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.model_training_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前使用「$defaultRcmdType端」推荐¹',
|
'当前使用「$defaultRcmdType端」推荐¹',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -131,12 +132,14 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '推荐动态',
|
title: '推荐动态',
|
||||||
subTitle: '是否在推荐内容中展示动态(仅app端)',
|
subTitle: '是否在推荐内容中展示动态(仅app端)',
|
||||||
|
leading: Icon(Icons.motion_photos_on_outlined),
|
||||||
setKey: SettingBoxKey.enableRcmdDynamic,
|
setKey: SettingBoxKey.enableRcmdDynamic,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '首页推荐刷新',
|
title: '首页推荐刷新',
|
||||||
subTitle: '下拉刷新时保留上次内容',
|
subTitle: '下拉刷新时保留上次内容',
|
||||||
|
leading: Icon(Icons.refresh),
|
||||||
setKey: SettingBoxKey.enableSaveLastData,
|
setKey: SettingBoxKey.enableSaveLastData,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
@@ -144,6 +147,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
|
leading: const Icon(Icons.thumb_up_outlined),
|
||||||
title: Text('点赞率过滤', style: titleStyle),
|
title: Text('点赞率过滤', style: titleStyle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'过滤掉点赞数/播放量「小于$minLikeRatioForRecommend%」的推荐视频(仅web端)',
|
'过滤掉点赞数/播放量「小于$minLikeRatioForRecommend%」的推荐视频(仅web端)',
|
||||||
@@ -172,6 +176,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('视频时长过滤', style: titleStyle),
|
title: Text('视频时长过滤', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.timelapse_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'过滤掉时长「小于$minDurationForRcmd秒」的推荐视频',
|
'过滤掉时长「小于$minDurationForRcmd秒」的推荐视频',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -199,6 +204,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
SetSwitchItem(
|
SetSwitchItem(
|
||||||
title: '已关注Up豁免推荐过滤',
|
title: '已关注Up豁免推荐过滤',
|
||||||
subTitle: '推荐中已关注用户发布的内容不会被过滤',
|
subTitle: '推荐中已关注用户发布的内容不会被过滤',
|
||||||
|
leading: const Icon(Icons.favorite_border_outlined),
|
||||||
setKey: SettingBoxKey.exemptFilterForFollowed,
|
setKey: SettingBoxKey.exemptFilterForFollowed,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
callFn: (_) => {RecommendFilter.update},
|
callFn: (_) => {RecommendFilter.update},
|
||||||
@@ -234,6 +240,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
|
|||||||
SetSwitchItem(
|
SetSwitchItem(
|
||||||
title: '过滤器也应用于相关视频',
|
title: '过滤器也应用于相关视频',
|
||||||
subTitle: '视频详情页的相关视频也进行过滤²',
|
subTitle: '视频详情页的相关视频也进行过滤²',
|
||||||
|
leading: const Icon(Icons.explore_outlined),
|
||||||
setKey: SettingBoxKey.applyFilterToRelatedVideos,
|
setKey: SettingBoxKey.applyFilterToRelatedVideos,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
callFn: (_) => {RecommendFilter.update},
|
callFn: (_) => {RecommendFilter.update},
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
children: [
|
children: [
|
||||||
SetSwitchItem(
|
SetSwitchItem(
|
||||||
title: '横屏适配',
|
title: '横屏适配',
|
||||||
subTitle: '开启该项启用横屏布局与逻辑(测试)',
|
subTitle: '启用横屏布局与逻辑,适用于平板等设备',
|
||||||
|
leading: const Icon(Icons.phonelink_outlined),
|
||||||
setKey: SettingBoxKey.horizontalScreen,
|
setKey: SettingBoxKey.horizontalScreen,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
callFn: (value) {
|
callFn: (value) {
|
||||||
@@ -75,15 +76,24 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
SmartDialog.showToast('已关闭横屏适配');
|
SmartDialog.showToast('已关闭横屏适配');
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '自适应底栏/侧边栏',
|
||||||
|
subTitle: '横竖屏自动切换(其它底栏设置失效)',
|
||||||
|
leading: Icon(Icons.chrome_reader_mode_outlined),
|
||||||
|
setKey: SettingBoxKey.adaptiveNavBar,
|
||||||
|
defaultVal: false,
|
||||||
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: 'MD3样式底栏',
|
title: 'MD3样式底栏',
|
||||||
subTitle: '符合Material You设计规范的底栏,关闭可使底栏变窄',
|
subTitle: 'Material You设计规范底栏,关闭可变窄',
|
||||||
|
leading: Icon(Icons.design_services_outlined),
|
||||||
setKey: SettingBoxKey.enableMYBar,
|
setKey: SettingBoxKey.enableMYBar,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '首页顶栏收起',
|
title: '首页顶栏收起',
|
||||||
subTitle: '首页列表滑动时,收起顶栏',
|
subTitle: '首页列表滑动时,收起顶栏',
|
||||||
|
leading: Icon(Icons.vertical_align_top_outlined),
|
||||||
setKey: SettingBoxKey.hideSearchBar,
|
setKey: SettingBoxKey.hideSearchBar,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
needReboot: true,
|
needReboot: true,
|
||||||
@@ -91,6 +101,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '首页底栏收起',
|
title: '首页底栏收起',
|
||||||
subTitle: '首页列表滑动时,收起底栏',
|
subTitle: '首页列表滑动时,收起底栏',
|
||||||
|
leading: Icon(Icons.vertical_align_bottom_outlined),
|
||||||
setKey: SettingBoxKey.hideTabBar,
|
setKey: SettingBoxKey.hideTabBar,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
needReboot: true,
|
needReboot: true,
|
||||||
@@ -98,6 +109,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '首页背景渐变',
|
title: '首页背景渐变',
|
||||||
setKey: SettingBoxKey.enableGradientBg,
|
setKey: SettingBoxKey.enableGradientBg,
|
||||||
|
leading: Icon(Icons.gradient_outlined),
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
needReboot: true,
|
needReboot: true,
|
||||||
),
|
),
|
||||||
@@ -123,11 +135,12 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
leading: const Icon(Icons.calendar_view_week_outlined),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('最大列宽(dp)基准', style: titleStyle),
|
title: Text('列表宽度(dp)上限', style: titleStyle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前:${maxRowWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(context).size.width.toPrecision(2)}dp。'
|
'当前:${maxRowWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(context).size.width.toPrecision(2)}dp。'
|
||||||
'该值决定列表在不同屏宽下的列数,部分列表会根据系数折算宽度',
|
'宽度越小列数越多,横条、大卡会2倍折算',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -187,6 +200,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
},
|
},
|
||||||
title: Text('图片质量', style: titleStyle),
|
title: Text('图片质量', style: titleStyle),
|
||||||
subtitle: Text('选择合适的图片清晰度,上限100%', style: subTitleStyle),
|
subtitle: Text('选择合适的图片清晰度,上限100%', style: subTitleStyle),
|
||||||
|
leading: const Icon(Icons.image_outlined),
|
||||||
trailing: Padding(
|
trailing: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: Obx(
|
child: Obx(
|
||||||
@@ -218,8 +232,9 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
setting.put(SettingBoxKey.defaultToastOp, result);
|
setting.put(SettingBoxKey.defaultToastOp, result);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: Text('Toast不透明度', style: titleStyle),
|
leading: const Icon(Icons.opacity_outlined),
|
||||||
subtitle: Text('自定义Toast不透明度', style: subTitleStyle),
|
title: Text('气泡提示不透明度', style: titleStyle),
|
||||||
|
subtitle: Text('自定义气泡提示(Toast)不透明度', style: subTitleStyle),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@@ -242,6 +257,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
Get.forceAppUpdate();
|
Get.forceAppUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
leading: const Icon(Icons.flashlight_on_outlined),
|
||||||
title: Text('主题模式', style: titleStyle),
|
title: Text('主题模式', style: titleStyle),
|
||||||
subtitle: Obx(() => Text(
|
subtitle: Obx(() => Text(
|
||||||
'当前模式:${settingController.themeType.value.description}',
|
'当前模式:${settingController.themeType.value.description}',
|
||||||
@@ -251,6 +267,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => settingController.setDynamicBadgeMode(context),
|
onTap: () => settingController.setDynamicBadgeMode(context),
|
||||||
title: Text('动态未读标记', style: titleStyle),
|
title: Text('动态未读标记', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.motion_photos_on_outlined),
|
||||||
subtitle: Obx(() => Text(
|
subtitle: Obx(() => Text(
|
||||||
'当前标记样式:${settingController.dynamicBadgeType.value.description}',
|
'当前标记样式:${settingController.dynamicBadgeType.value.description}',
|
||||||
style: subTitleStyle)),
|
style: subTitleStyle)),
|
||||||
@@ -258,6 +275,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/colorSetting'),
|
onTap: () => Get.toNamed('/colorSetting'),
|
||||||
|
leading: const Icon(Icons.color_lens_outlined),
|
||||||
title: Text('应用主题', style: titleStyle),
|
title: Text('应用主题', style: titleStyle),
|
||||||
subtitle: Obx(() => Text(
|
subtitle: Obx(() => Text(
|
||||||
'当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}',
|
'当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}',
|
||||||
@@ -266,12 +284,14 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '默认展示评论区',
|
title: '默认展示评论区',
|
||||||
subTitle: '在视频详情页默认切换至评论区页',
|
subTitle: '在视频详情页默认切换至评论区页',
|
||||||
|
leading: Icon(Icons.mode_comment_outlined),
|
||||||
setKey: SettingBoxKey.defaultShowComment,
|
setKey: SettingBoxKey.defaultShowComment,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => settingController.seteDefaultHomePage(context),
|
onTap: () => settingController.seteDefaultHomePage(context),
|
||||||
|
leading: const Icon(Icons.home_outlined),
|
||||||
title: Text('默认启动页', style: titleStyle),
|
title: Text('默认启动页', style: titleStyle),
|
||||||
subtitle: Obx(() => Text(
|
subtitle: Obx(() => Text(
|
||||||
'当前启动页:${defaultNavigationBars.firstWhere((e) => e['id'] == settingController.defaultHomePage.value)['label']}',
|
'当前启动页:${defaultNavigationBars.firstWhere((e) => e['id'] == settingController.defaultHomePage.value)['label']}',
|
||||||
@@ -281,18 +301,21 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/fontSizeSetting'),
|
onTap: () => Get.toNamed('/fontSizeSetting'),
|
||||||
title: Text('字体大小', style: titleStyle),
|
title: Text('字体大小', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.format_size_outlined),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/tabbarSetting'),
|
onTap: () => Get.toNamed('/tabbarSetting'),
|
||||||
title: Text('首页标签页', style: titleStyle),
|
title: Text('首页标签页', style: titleStyle),
|
||||||
subtitle: Text('删除或调换首页标签页', style: subTitleStyle),
|
subtitle: Text('删除或调换首页标签页', style: subTitleStyle),
|
||||||
|
leading: const Icon(Icons.toc_outlined),
|
||||||
),
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/displayModeSetting'),
|
onTap: () => Get.toNamed('/displayModeSetting'),
|
||||||
title: Text('屏幕帧率', style: titleStyle),
|
title: Text('屏幕帧率', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.autofps_select_outlined),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
late dynamic defaultAudioQa;
|
late dynamic defaultAudioQa;
|
||||||
late dynamic defaultDecode;
|
late dynamic defaultDecode;
|
||||||
late dynamic secondDecode;
|
late dynamic secondDecode;
|
||||||
|
late dynamic hardwareDecoding;
|
||||||
|
late dynamic videoSync;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -33,6 +35,10 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
defaultValue: VideoDecodeFormats.values.last.code);
|
defaultValue: VideoDecodeFormats.values.last.code);
|
||||||
secondDecode = setting.get(SettingBoxKey.secondDecode,
|
secondDecode = setting.get(SettingBoxKey.secondDecode,
|
||||||
defaultValue: VideoDecodeFormats.values[1].code);
|
defaultValue: VideoDecodeFormats.values[1].code);
|
||||||
|
hardwareDecoding = setting.get(SettingBoxKey.hardwareDecoding,
|
||||||
|
defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto');
|
||||||
|
videoSync =
|
||||||
|
setting.get(SettingBoxKey.videoSync, defaultValue: 'display-resample');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -55,31 +61,36 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
children: [
|
children: [
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '开启硬解',
|
title: '开启硬解',
|
||||||
subTitle: '以较低功耗播放视频,若遇异常卡死,请尝试关闭',
|
subTitle: '以较低功耗播放视频,若异常卡死请关闭',
|
||||||
|
leading: Icon(Icons.flash_on_outlined),
|
||||||
setKey: SettingBoxKey.enableHA,
|
setKey: SettingBoxKey.enableHA,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '亮度记忆',
|
title: '亮度记忆',
|
||||||
subTitle: '返回时自动调整视频亮度',
|
subTitle: '返回时自动调整视频亮度',
|
||||||
|
leading: Icon(Icons.brightness_6_outlined),
|
||||||
setKey: SettingBoxKey.enableAutoBrightness,
|
setKey: SettingBoxKey.enableAutoBrightness,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '免登录1080P',
|
title: '免登录1080P',
|
||||||
subTitle: '免登录查看1080P视频',
|
subTitle: '免登录查看1080P视频',
|
||||||
|
leading: Icon(Icons.hd_outlined),
|
||||||
setKey: SettingBoxKey.p1080,
|
setKey: SettingBoxKey.p1080,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: 'CDN优化',
|
title: 'CDN优化',
|
||||||
subTitle: '使用优质CDN线路',
|
subTitle: '使用优质CDN线路',
|
||||||
|
leading: Icon(Icons.network_check_outlined),
|
||||||
setKey: SettingBoxKey.enableCDN,
|
setKey: SettingBoxKey.enableCDN,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('默认画质', style: titleStyle),
|
title: Text('默认画质', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.video_settings_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前画质:${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
|
'当前画质:${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -106,6 +117,7 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('默认音质', style: titleStyle),
|
title: Text('默认音质', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.audiotrack_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前音质:${AudioQualityCode.fromCode(defaultAudioQa)!.description!}',
|
'当前音质:${AudioQualityCode.fromCode(defaultAudioQa)!.description!}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -132,6 +144,7 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('首选解码格式', style: titleStyle),
|
title: Text('首选解码格式', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.movie_creation_outlined),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'首选解码格式:${VideoDecodeFormatsCode.fromCode(defaultDecode)!.description!},请根据设备支持情况与需求调整',
|
'首选解码格式:${VideoDecodeFormatsCode.fromCode(defaultDecode)!.description!},请根据设备支持情况与需求调整',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
@@ -162,6 +175,7 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
'非杜比视频次选:${VideoDecodeFormatsCode.fromCode(secondDecode)!.description!},仍无则选择首个提供的解码格式',
|
'非杜比视频次选:${VideoDecodeFormatsCode.fromCode(secondDecode)!.description!},仍无则选择首个提供的解码格式',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
|
leading: const Icon(Icons.swap_horizontal_circle_outlined),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
String? result = await showDialog(
|
String? result = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -184,16 +198,84 @@ class _VideoSettingState extends State<VideoSetting> {
|
|||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '优先使用 OpenSL ES 输出音频',
|
title: '优先使用 OpenSL ES 输出音频',
|
||||||
|
leading: Icon(Icons.speaker_outlined),
|
||||||
subTitle: '关闭则优先使用AudioTrack输出音频(此项即mpv的--ao)',
|
subTitle: '关闭则优先使用AudioTrack输出音频(此项即mpv的--ao)',
|
||||||
setKey: SettingBoxKey.useOpenSLES,
|
setKey: SettingBoxKey.useOpenSLES,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '扩大缓冲区',
|
title: '扩大缓冲区',
|
||||||
subTitle: '默认缓冲区为视频5MB/直播32MB,开启后为视频32MB/直播64MB,但会延长首次加载时间',
|
leading: Icon(Icons.storage_outlined),
|
||||||
|
subTitle: '默认缓冲区为视频5MB/直播32MB,开启后为32MB/64MB,加载时间变长',
|
||||||
setKey: SettingBoxKey.expandBuffer,
|
setKey: SettingBoxKey.expandBuffer,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
|
//video-sync
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
title: Text('视频同步', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.view_timeline_outlined),
|
||||||
|
subtitle: Text(
|
||||||
|
'当前:$videoSync(此项即mpv的--video-sync)',
|
||||||
|
style: subTitleStyle,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
String? result = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectDialog<String>(
|
||||||
|
title: '视频同步',
|
||||||
|
value: videoSync,
|
||||||
|
values: [
|
||||||
|
'audio',
|
||||||
|
'display-resample',
|
||||||
|
'display-resample-vdrop',
|
||||||
|
'display-resample-desync',
|
||||||
|
'display-tempo',
|
||||||
|
'display-vdrop',
|
||||||
|
'display-adrop',
|
||||||
|
'display-desync',
|
||||||
|
'desync'
|
||||||
|
].map((e) {
|
||||||
|
return {'title': e, 'value': e};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
setting.put(SettingBoxKey.videoSync, result);
|
||||||
|
videoSync = result;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
title: Text('硬解模式', style: titleStyle),
|
||||||
|
leading: const Icon(Icons.memory_outlined),
|
||||||
|
subtitle: Text(
|
||||||
|
'当前:$hardwareDecoding(此项即mpv的--hwdec)',
|
||||||
|
style: subTitleStyle,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
String? result = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectDialog<String>(
|
||||||
|
title: '硬解模式',
|
||||||
|
value: hardwareDecoding,
|
||||||
|
values: ['auto', 'auto-copy', 'auto-safe', 'no', 'yes']
|
||||||
|
.map((e) {
|
||||||
|
return {'title': e, 'value': e};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
setting.put(SettingBoxKey.hardwareDecoding, result);
|
||||||
|
hardwareDecoding = result;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class SettingPage extends StatelessWidget {
|
|||||||
() => Visibility(
|
() => Visibility(
|
||||||
visible: settingController.hiddenSettingUnlocked.value,
|
visible: settingController.hiddenSettingUnlocked.value,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.developer_mode_outlined),
|
leading: const Icon(Icons.developer_board_outlined),
|
||||||
onTap: () => Get.toNamed('/hiddenSetting'),
|
onTap: () => Get.toNamed('/hiddenSetting'),
|
||||||
dense: false,
|
dense: false,
|
||||||
title: const Text('开发人员选项'),
|
title: const Text('开发人员选项'),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
final bool? defaultVal;
|
final bool? defaultVal;
|
||||||
final Function? callFn;
|
final Function? callFn;
|
||||||
final bool? needReboot;
|
final bool? needReboot;
|
||||||
|
final Widget? leading;
|
||||||
|
|
||||||
const SetSwitchItem({
|
const SetSwitchItem({
|
||||||
this.title,
|
this.title,
|
||||||
@@ -19,6 +20,7 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
this.defaultVal,
|
this.defaultVal,
|
||||||
this.callFn,
|
this.callFn,
|
||||||
this.needReboot,
|
this.needReboot,
|
||||||
|
this.leading,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -66,6 +68,7 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
|||||||
subtitle: widget.subTitle != null
|
subtitle: widget.subTitle != null
|
||||||
? Text(widget.subTitle!, style: subTitleStyle)
|
? Text(widget.subTitle!, style: subTitleStyle)
|
||||||
: null,
|
: null,
|
||||||
|
leading: widget.leading,
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大
|
alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大
|
||||||
scale: 0.8,
|
scale: 0.8,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class VideoDetailController extends GetxController
|
|||||||
RxBool isShowCover = true.obs;
|
RxBool isShowCover = true.obs;
|
||||||
// 硬解
|
// 硬解
|
||||||
RxBool enableHA = true.obs;
|
RxBool enableHA = true.obs;
|
||||||
|
RxString hwdec = 'auto-safe'.obs;
|
||||||
|
|
||||||
/// 本地存储
|
/// 本地存储
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
@@ -117,7 +118,8 @@ class VideoDetailController extends GetxController
|
|||||||
autoPlay.value =
|
autoPlay.value =
|
||||||
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
|
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
|
||||||
enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true);
|
enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true);
|
||||||
|
hwdec.value = setting.get(SettingBoxKey.hardwareDecoding,
|
||||||
|
defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto');
|
||||||
if (userInfo == null ||
|
if (userInfo == null ||
|
||||||
localCache.get(LocalCacheKey.historyPause) == true) {
|
localCache.get(LocalCacheKey.historyPause) == true) {
|
||||||
enableHeart = false;
|
enableHeart = false;
|
||||||
@@ -270,6 +272,7 @@ class VideoDetailController extends GetxController
|
|||||||
),
|
),
|
||||||
// 硬解
|
// 硬解
|
||||||
enableHA: enableHA.value,
|
enableHA: enableHA.value,
|
||||||
|
hwdec: hwdec.value,
|
||||||
seekTo: seekToTime ?? defaultST,
|
seekTo: seekToTime ?? defaultST,
|
||||||
duration: duration ?? data.timeLength == null
|
duration: duration ?? data.timeLength == null
|
||||||
? null
|
? null
|
||||||
@@ -300,18 +303,33 @@ class VideoDetailController extends GetxController
|
|||||||
'该视频为专属视频,仅提供试看',
|
'该视频为专属视频,仅提供试看',
|
||||||
displayTime: const Duration(seconds: 3),
|
displayTime: const Duration(seconds: 3),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (data.dash == null && data.durl != null) {
|
||||||
videoUrl = data.durl!.first.url!;
|
videoUrl = data.durl!.first.url!;
|
||||||
audioUrl = '';
|
audioUrl = '';
|
||||||
defaultST = Duration.zero;
|
defaultST = Duration.zero;
|
||||||
firstVideo = VideoItem();
|
// 实际为FLV/MP4格式,但已被淘汰,这里仅做兜底处理
|
||||||
|
firstVideo = VideoItem(
|
||||||
|
id: data.quality!,
|
||||||
|
baseUrl: videoUrl,
|
||||||
|
codecs: 'avc1',
|
||||||
|
quality: VideoQualityCode.fromCode(data.quality!)!
|
||||||
|
);
|
||||||
|
currentDecodeFormats = VideoDecodeFormatsCode.fromString('avc1')!;
|
||||||
|
currentVideoQa = VideoQualityCode.fromCode(data.quality!)!;
|
||||||
if (autoPlay.value) {
|
if (autoPlay.value) {
|
||||||
await playerInit();
|
await playerInit();
|
||||||
isShowCover.value = false;
|
isShowCover.value = false;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
if (data.dash == null) {
|
||||||
|
SmartDialog.showToast('视频资源不存在');
|
||||||
|
isShowCover.value = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
final List<VideoItem> allVideosList = data.dash!.video!;
|
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||||
try {
|
print("allVideosList:${allVideosList}");
|
||||||
// 当前可播放的最高质量视频
|
// 当前可播放的最高质量视频
|
||||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||||
// 预设的画质为null,则当前可用的最高质量
|
// 预设的画质为null,则当前可用的最高质量
|
||||||
@@ -319,9 +337,8 @@ class VideoDetailController extends GetxController
|
|||||||
int resVideoQa = currentHighVideoQa;
|
int resVideoQa = currentHighVideoQa;
|
||||||
if (cacheVideoQa! <= currentHighVideoQa) {
|
if (cacheVideoQa! <= currentHighVideoQa) {
|
||||||
// 如果预设的画质低于当前最高
|
// 如果预设的画质低于当前最高
|
||||||
final List<int> numbers = data.acceptQuality!
|
final List<int> numbers =
|
||||||
.where((e) => e <= currentHighVideoQa)
|
data.acceptQuality!.where((e) => e <= currentHighVideoQa).toList();
|
||||||
.toList();
|
|
||||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
||||||
}
|
}
|
||||||
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
||||||
@@ -333,13 +350,14 @@ class VideoDetailController extends GetxController
|
|||||||
/// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式
|
/// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式
|
||||||
final List<FormatItem> supportFormats = data.supportFormats!;
|
final List<FormatItem> supportFormats = data.supportFormats!;
|
||||||
// 根据画质选编码格式
|
// 根据画质选编码格式
|
||||||
final List supportDecodeFormats =
|
final List supportDecodeFormats = supportFormats
|
||||||
supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!;
|
.firstWhere((e) => e.quality == resVideoQa,
|
||||||
|
orElse: () => supportFormats.first)
|
||||||
|
.codecs!;
|
||||||
// 默认从设置中取AV1
|
// 默认从设置中取AV1
|
||||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
||||||
VideoDecodeFormats secondDecodeFormats =
|
VideoDecodeFormats secondDecodeFormats =
|
||||||
VideoDecodeFormatsCode.fromString(cacheSecondDecode)!;
|
VideoDecodeFormatsCode.fromString(cacheSecondDecode)!;
|
||||||
try {
|
|
||||||
// 当前视频没有对应格式返回第一个
|
// 当前视频没有对应格式返回第一个
|
||||||
int flag = 0;
|
int flag = 0;
|
||||||
for (var i in supportDecodeFormats) {
|
for (var i in supportDecodeFormats) {
|
||||||
@@ -356,30 +374,21 @@ class VideoDetailController extends GetxController
|
|||||||
currentDecodeFormats =
|
currentDecodeFormats =
|
||||||
VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
|
VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast('DecodeFormats error: $err');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 取出符合当前解码格式的videoItem
|
/// 取出符合当前解码格式的videoItem
|
||||||
try {
|
|
||||||
firstVideo = videosList.firstWhere(
|
firstVideo = videosList.firstWhere(
|
||||||
(e) => e.codecs!.startsWith(currentDecodeFormats.code));
|
(e) => e.codecs!.startsWith(currentDecodeFormats.code),
|
||||||
} catch (_) {
|
orElse: () => videosList.first);
|
||||||
firstVideo = videosList.first;
|
|
||||||
}
|
|
||||||
videoUrl = enableCDN
|
videoUrl = enableCDN
|
||||||
? VideoUtils.getCdnUrl(firstVideo)
|
? VideoUtils.getCdnUrl(firstVideo)
|
||||||
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
|
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast('firstVideo error: $err');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
||||||
late AudioItem? firstAudio;
|
late AudioItem? firstAudio;
|
||||||
final List<AudioItem> audiosList = data.dash!.audio!;
|
final List<AudioItem> audiosList = data.dash!.audio!;
|
||||||
|
|
||||||
try {
|
if (data.dash!.dolby?.audio != null && data.dash!.dolby!.audio!.isNotEmpty) {
|
||||||
if (data.dash!.dolby?.audio?.isNotEmpty == true) {
|
|
||||||
// 杜比
|
// 杜比
|
||||||
audiosList.insert(0, data.dash!.dolby!.audio!.first);
|
audiosList.insert(0, data.dash!.dolby!.audio!.first);
|
||||||
}
|
}
|
||||||
@@ -396,14 +405,11 @@ class VideoDetailController extends GetxController
|
|||||||
numbers.any((e) => e > cacheAudioQa)) {
|
numbers.any((e) => e > cacheAudioQa)) {
|
||||||
closestNumber = 30280;
|
closestNumber = 30280;
|
||||||
}
|
}
|
||||||
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber);
|
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber,
|
||||||
|
orElse: () => audiosList.first);
|
||||||
} else {
|
} else {
|
||||||
firstAudio = AudioItem();
|
firstAudio = AudioItem();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
firstAudio = audiosList.isNotEmpty ? audiosList.first : AudioItem();
|
|
||||||
SmartDialog.showToast('firstAudio error: $err');
|
|
||||||
}
|
|
||||||
|
|
||||||
audioUrl = enableCDN
|
audioUrl = enableCDN
|
||||||
? VideoUtils.getCdnUrl(firstAudio)
|
? VideoUtils.getCdnUrl(firstAudio)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import 'package:PiliPalaX/utils/feed_back.dart';
|
|||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
|
|
||||||
import '../../../../utils/id_utils.dart';
|
|
||||||
import 'widgets/action_item.dart';
|
import 'widgets/action_item.dart';
|
||||||
import 'widgets/action_row_item.dart';
|
import 'widgets/action_row_item.dart';
|
||||||
import 'widgets/fav_panel.dart';
|
import 'widgets/fav_panel.dart';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:PiliPalaX/common/constants.dart';
|
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
|
|
||||||
class ActionItem extends StatelessWidget {
|
class ActionItem extends StatelessWidget {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPalaX/models/video_detail_res.dart';
|
import 'package:PiliPalaX/models/video_detail_res.dart';
|
||||||
import 'package:PiliPalaX/pages/video/detail/index.dart';
|
import 'package:PiliPalaX/pages/video/detail/index.dart';
|
||||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
|
||||||
|
|
||||||
class SeasonPanel extends StatefulWidget {
|
class SeasonPanel extends StatefulWidget {
|
||||||
const SeasonPanel({
|
const SeasonPanel({
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
@override
|
@override
|
||||||
void didChangeMetrics() {
|
void didChangeMetrics() {
|
||||||
super.didChangeMetrics();
|
super.didChangeMetrics();
|
||||||
final String routePath = Get.currentRoute;
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
RxBool isFullScreen = false.obs;
|
RxBool isFullScreen = false.obs;
|
||||||
late StreamSubscription<bool> fullScreenStatusListener;
|
late StreamSubscription<bool> fullScreenStatusListener;
|
||||||
late final MethodChannel onUserLeaveHintListener;
|
late final MethodChannel onUserLeaveHintListener;
|
||||||
late AnimationController _animationController;
|
|
||||||
late Animation<double> _animation;
|
|
||||||
StreamSubscription<Duration>? _bufferedListener;
|
StreamSubscription<Duration>? _bufferedListener;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -292,29 +290,33 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => {});
|
setState(() => {});
|
||||||
}
|
}
|
||||||
|
super.didPopNext();
|
||||||
videoDetailController.isFirstTime = false;
|
videoDetailController.isFirstTime = false;
|
||||||
final bool autoplay = autoPlayEnable;
|
final bool autoplay = autoPlayEnable;
|
||||||
videoDetailController.playerInit(autoplay: autoplay);
|
await videoDetailController.playerInit(autoplay: autoplay);
|
||||||
|
|
||||||
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
||||||
videoDetailController.autoPlay.value =
|
videoDetailController.autoPlay.value =
|
||||||
!videoDetailController.isShowCover.value;
|
!videoDetailController.isShowCover.value;
|
||||||
videoIntroController.isPaused = false;
|
videoIntroController.isPaused = false;
|
||||||
if (autoplay) {
|
// if (autoplay) {
|
||||||
// await Future.delayed(const Duration(milliseconds: 300));
|
// // await Future.delayed(const Duration(milliseconds: 300));
|
||||||
if (plPlayerController?.buffered.value == Duration.zero) {
|
// print(plPlayerController);
|
||||||
_bufferedListener = plPlayerController?.buffered.listen((p0) {
|
// if (plPlayerController?.buffered.value == Duration.zero) {
|
||||||
if (p0 > Duration.zero) {
|
// _bufferedListener = plPlayerController?.buffered.listen((p0) {
|
||||||
_bufferedListener!.cancel();
|
// print("p0");
|
||||||
plPlayerController?.seekTo(videoDetailController.defaultST);
|
// print(p0);
|
||||||
plPlayerController?.play();
|
// if (p0 > Duration.zero) {
|
||||||
}
|
// _bufferedListener!.cancel();
|
||||||
});
|
// plPlayerController?.seekTo(videoDetailController.defaultST);
|
||||||
} else {
|
// plPlayerController?.play();
|
||||||
plPlayerController?.seekTo(videoDetailController.defaultST);
|
// }
|
||||||
plPlayerController?.play();
|
// });
|
||||||
}
|
// } else {
|
||||||
}
|
// plPlayerController?.seekTo(videoDetailController.defaultST);
|
||||||
|
// plPlayerController?.play();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
Future.delayed(const Duration(milliseconds: 600), () {
|
Future.delayed(const Duration(milliseconds: 600), () {
|
||||||
AutoOrientation.fullAutoMode();
|
AutoOrientation.fullAutoMode();
|
||||||
});
|
});
|
||||||
@@ -322,7 +324,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
if (plPlayerController != null) {
|
if (plPlayerController != null) {
|
||||||
listenFullScreenStatus();
|
listenFullScreenStatus();
|
||||||
}
|
}
|
||||||
super.didPopNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -409,6 +410,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
child: AppBar(
|
child: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
// systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
// statusBarColor: Colors.transparent,
|
||||||
|
// statusBarIconBrightness: Brightness.light),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
@@ -417,10 +421,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
() {
|
() {
|
||||||
double videoheight = context.width * 9 / 16;
|
double videoheight = context.width * 9 / 16;
|
||||||
final double videowidth = context.width;
|
final double videowidth = context.width;
|
||||||
print(videoDetailController.tabCtr.index);
|
// print(videoDetailController.tabCtr.index);
|
||||||
if (enableVerticalExpand &&
|
if (enableVerticalExpand &&
|
||||||
plPlayerController?.direction.value == 'vertical' &&
|
plPlayerController?.direction.value == 'vertical') {
|
||||||
videoDetailController.tabCtr.index != 1) {
|
|
||||||
videoheight = context.width * 5 / 4;
|
videoheight = context.width * 5 / 4;
|
||||||
}
|
}
|
||||||
if (MediaQuery.of(context).orientation ==
|
if (MediaQuery.of(context).orientation ==
|
||||||
@@ -812,6 +815,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
child: AppBar(
|
child: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
// systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
// statusBarColor: Colors.transparent,
|
||||||
|
// statusBarIconBrightness: Brightness.dark),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: childWhenDisabledLandscapeInner)
|
body: childWhenDisabledLandscapeInner)
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ import 'package:PiliPalaX/utils/storage.dart';
|
|||||||
import 'package:PiliPalaX/http/danmaku.dart';
|
import 'package:PiliPalaX/http/danmaku.dart';
|
||||||
import 'package:PiliPalaX/services/shutdown_timer_service.dart';
|
import 'package:PiliPalaX/services/shutdown_timer_service.dart';
|
||||||
import '../../../../models/video_detail_res.dart';
|
import '../../../../models/video_detail_res.dart';
|
||||||
import '../../../../services/service_locator.dart';
|
|
||||||
import '../introduction/index.dart';
|
import '../introduction/index.dart';
|
||||||
import 'package:marquee/marquee.dart';
|
import 'package:marquee/marquee.dart';
|
||||||
import '../../../danmaku/controller.dart';
|
|
||||||
|
|
||||||
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||||
const HeaderControl({
|
const HeaderControl({
|
||||||
@@ -440,6 +438,10 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
|
|
||||||
/// 选择画质
|
/// 选择画质
|
||||||
void showSetVideoQa() {
|
void showSetVideoQa() {
|
||||||
|
if (videoInfo.dash == null) {
|
||||||
|
SmartDialog.showToast('当前视频不支持选择画质');
|
||||||
|
return;
|
||||||
|
}
|
||||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||||
final VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
final VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
||||||
|
|
||||||
@@ -634,9 +636,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
||||||
// 当前视频可用的解码格式
|
// 当前视频可用的解码格式
|
||||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||||
final List list = videoFormat
|
final List? list = videoFormat
|
||||||
.firstWhere((FormatItem e) => e.quality == firstVideo.quality!.code)
|
.firstWhere((FormatItem e) => e.quality == firstVideo.quality!.code)
|
||||||
.codecs!;
|
.codecs;
|
||||||
|
if (list == null) {
|
||||||
|
SmartDialog.showToast('当前视频不支持选择解码格式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class WebviewController extends GetxController {
|
|||||||
content = '${content + url}; \n';
|
content = '${content + url}; \n';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await SetCookie.onSet();
|
await CookieTool.onSet();
|
||||||
final result = await UserHttp.userInfo();
|
final result = await UserHttp.userInfo();
|
||||||
if (result['status'] && result['data'].isLogin) {
|
if (result['status'] && result['data'].isLogin) {
|
||||||
SmartDialog.showToast('登录成功,当前采用「'
|
SmartDialog.showToast('登录成功,当前采用「'
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class PlPlayerController {
|
|||||||
static List<Map<String, dynamic>> videoFitType = [
|
static List<Map<String, dynamic>> videoFitType = [
|
||||||
{'attr': BoxFit.contain, 'desc': '自动', 'toast': '缩放至播放器尺寸,保留黑边'},
|
{'attr': BoxFit.contain, 'desc': '自动', 'toast': '缩放至播放器尺寸,保留黑边'},
|
||||||
{'attr': BoxFit.cover, 'desc': '裁剪', 'toast': '缩放至填满播放器,裁剪超出部分'},
|
{'attr': BoxFit.cover, 'desc': '裁剪', 'toast': '缩放至填满播放器,裁剪超出部分'},
|
||||||
{'attr': BoxFit.fill, 'desc': '拉伸', 'toast': '拉伸至播放器尺寸,将产生变形'},
|
{'attr': BoxFit.fill, 'desc': '拉伸', 'toast': '拉伸至播放器尺寸,将产生变形(竖屏改为自动)'},
|
||||||
{'attr': BoxFit.none, 'desc': '原始', 'toast': '不缩放,以视频原始尺寸显示'},
|
{'attr': BoxFit.none, 'desc': '原始', 'toast': '不缩放,以视频原始尺寸显示'},
|
||||||
{'attr': BoxFit.fitHeight, 'desc': '等高', 'toast': '缩放至撑满播放器高度'},
|
{'attr': BoxFit.fitHeight, 'desc': '等高', 'toast': '缩放至撑满播放器高度'},
|
||||||
{'attr': BoxFit.fitWidth, 'desc': '等宽', 'toast': '缩放至撑满播放器宽度'},
|
{'attr': BoxFit.fitWidth, 'desc': '等宽', 'toast': '缩放至撑满播放器宽度'},
|
||||||
@@ -285,21 +285,21 @@ class PlPlayerController {
|
|||||||
isOpenDanmu.value =
|
isOpenDanmu.value =
|
||||||
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
||||||
danmakuWeight.value =
|
danmakuWeight.value =
|
||||||
localCache.get(LocalCacheKey.danmakuWeight, defaultValue: 0);
|
setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0);
|
||||||
blockTypes =
|
blockTypes =
|
||||||
localCache.get(LocalCacheKey.danmakuBlockType, defaultValue: []);
|
setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []);
|
||||||
showArea = localCache.get(LocalCacheKey.danmakuShowArea, defaultValue: 0.5);
|
showArea = setting.get(SettingBoxKey.danmakuShowArea, defaultValue: 0.5);
|
||||||
// 不透明度
|
// 不透明度
|
||||||
opacityVal =
|
opacityVal =
|
||||||
localCache.get(LocalCacheKey.danmakuOpacity, defaultValue: 1.0);
|
setting.get(SettingBoxKey.danmakuOpacity, defaultValue: 1.0);
|
||||||
// 字体大小
|
// 字体大小
|
||||||
fontSizeVal =
|
fontSizeVal =
|
||||||
localCache.get(LocalCacheKey.danmakuFontScale, defaultValue: 1.0);
|
setting.get(SettingBoxKey.danmakuFontScale, defaultValue: 1.0);
|
||||||
// 弹幕时间
|
// 弹幕时间
|
||||||
danmakuDurationVal =
|
danmakuDurationVal =
|
||||||
localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0);
|
setting.get(SettingBoxKey.danmakuDuration, defaultValue: 4.0);
|
||||||
// 描边粗细
|
// 描边粗细
|
||||||
strokeWidth = localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5);
|
strokeWidth = setting.get(SettingBoxKey.strokeWidth, defaultValue: 1.5);
|
||||||
playRepeat = PlayRepeat.values.toList().firstWhere(
|
playRepeat = PlayRepeat.values.toList().firstWhere(
|
||||||
(e) =>
|
(e) =>
|
||||||
e.value ==
|
e.value ==
|
||||||
@@ -356,6 +356,7 @@ class PlPlayerController {
|
|||||||
double speed = 1.0,
|
double speed = 1.0,
|
||||||
// 硬件加速
|
// 硬件加速
|
||||||
bool enableHA = true,
|
bool enableHA = true,
|
||||||
|
String? hwdec,
|
||||||
double? width,
|
double? width,
|
||||||
double? height,
|
double? height,
|
||||||
Duration? duration,
|
Duration? duration,
|
||||||
@@ -390,7 +391,7 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
// 配置Player 音轨、字幕等等
|
// 配置Player 音轨、字幕等等
|
||||||
_videoPlayerController = await _createVideoController(
|
_videoPlayerController = await _createVideoController(
|
||||||
dataSource, _looping, enableHA, width, height);
|
dataSource, _looping, enableHA, hwdec, width, height);
|
||||||
// 获取视频时长 00:00
|
// 获取视频时长 00:00
|
||||||
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
||||||
updateDurationSecond();
|
updateDurationSecond();
|
||||||
@@ -401,7 +402,7 @@ class PlPlayerController {
|
|||||||
if (!_listenersInitialized) {
|
if (!_listenersInitialized) {
|
||||||
startListeners();
|
startListeners();
|
||||||
}
|
}
|
||||||
await _initializePlayer(seekTo: seekTo, duration: _duration.value);
|
await _initializePlayer(seekTo: seekTo);
|
||||||
if (videoType.value != 'live' && _cid != 0) {
|
if (videoType.value != 'live' && _cid != 0) {
|
||||||
refreshSubtitles().then((value) {
|
refreshSubtitles().then((value) {
|
||||||
if (_vttSubtitles.isNotEmpty) {
|
if (_vttSubtitles.isNotEmpty) {
|
||||||
@@ -432,6 +433,7 @@ class PlPlayerController {
|
|||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
PlaylistMode looping,
|
PlaylistMode looping,
|
||||||
bool enableHA,
|
bool enableHA,
|
||||||
|
String? hwdec,
|
||||||
double? width,
|
double? width,
|
||||||
double? height,
|
double? height,
|
||||||
) async {
|
) async {
|
||||||
@@ -468,6 +470,9 @@ class PlPlayerController {
|
|||||||
: "audiotrack,opensles";
|
: "audiotrack,opensles";
|
||||||
await pp.setProperty("ao", ao);
|
await pp.setProperty("ao", ao);
|
||||||
}
|
}
|
||||||
|
// video-sync=display-resample
|
||||||
|
await pp.setProperty("video-sync",
|
||||||
|
setting.get(SettingBoxKey.videoSync, defaultValue: 'display-resample'));
|
||||||
// // vo=gpu-next & gpu-context=android & gpu-api=opengl
|
// // vo=gpu-next & gpu-context=android & gpu-api=opengl
|
||||||
// await pp.setProperty("vo", "gpu-next");
|
// await pp.setProperty("vo", "gpu-next");
|
||||||
// await pp.setProperty("gpu-context", "android");
|
// await pp.setProperty("gpu-context", "android");
|
||||||
@@ -510,6 +515,7 @@ class PlPlayerController {
|
|||||||
configuration: VideoControllerConfiguration(
|
configuration: VideoControllerConfiguration(
|
||||||
enableHardwareAcceleration: enableHA,
|
enableHardwareAcceleration: enableHA,
|
||||||
androidAttachSurfaceAfterVideoParameters: false,
|
androidAttachSurfaceAfterVideoParameters: false,
|
||||||
|
hwdec: hwdec,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -519,15 +525,16 @@ class PlPlayerController {
|
|||||||
final assetUrl = dataSource.videoSource!.startsWith("asset://")
|
final assetUrl = dataSource.videoSource!.startsWith("asset://")
|
||||||
? dataSource.videoSource!
|
? dataSource.videoSource!
|
||||||
: "asset://${dataSource.videoSource!}";
|
: "asset://${dataSource.videoSource!}";
|
||||||
player.open(
|
await player.open(
|
||||||
Media(assetUrl, httpHeaders: dataSource.httpHeaders),
|
Media(assetUrl, httpHeaders: dataSource.httpHeaders),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
player.open(
|
await player.open(
|
||||||
Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders),
|
Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// 音轨
|
// 音轨
|
||||||
// player.setAudioTrack(
|
// player.setAudioTrack(
|
||||||
// AudioTrack.uri(dataSource.audioSource!),
|
// AudioTrack.uri(dataSource.audioSource!),
|
||||||
@@ -764,6 +771,12 @@ class PlPlayerController {
|
|||||||
if (repeat) {
|
if (repeat) {
|
||||||
await seekTo(Duration.zero);
|
await seekTo(Duration.zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 临时fix _duration.value丢失
|
||||||
|
if (duration != null) {
|
||||||
|
_duration.value = duration;
|
||||||
|
updateDurationSecond();
|
||||||
|
}
|
||||||
await _videoPlayerController?.play();
|
await _videoPlayerController?.play();
|
||||||
|
|
||||||
await getCurrentVolume();
|
await getCurrentVolume();
|
||||||
@@ -772,11 +785,6 @@ class PlPlayerController {
|
|||||||
playerStatus.status.value = PlayerStatus.playing;
|
playerStatus.status.value = PlayerStatus.playing;
|
||||||
// screenManager.setOverlays(false);
|
// screenManager.setOverlays(false);
|
||||||
|
|
||||||
/// 临时fix _duration.value丢失
|
|
||||||
if (duration != null) {
|
|
||||||
_duration.value = duration;
|
|
||||||
updateDurationSecond();
|
|
||||||
}
|
|
||||||
audioSessionHandler.setActive(true);
|
audioSessionHandler.setActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,7 +965,7 @@ class PlPlayerController {
|
|||||||
Future<void> getVideoFit() async {
|
Future<void> getVideoFit() async {
|
||||||
int fitValue = videoStorage.get(VideoBoxKey.cacheVideoFit, defaultValue: 0);
|
int fitValue = videoStorage.get(VideoBoxKey.cacheVideoFit, defaultValue: 0);
|
||||||
var attr = videoFitType[fitValue]['attr'];
|
var attr = videoFitType[fitValue]['attr'];
|
||||||
// 由于none与scaleDown涉及视频原始尺寸,需要等待视频加载后再设置,否则尺寸会变为0,出现错误
|
// 由于none与scaleDown涉及视频原始尺寸,需要等待视频加载后再设置,否则尺寸会变为0,出现错误;
|
||||||
if (attr == BoxFit.none || attr == BoxFit.scaleDown) {
|
if (attr == BoxFit.none || attr == BoxFit.scaleDown) {
|
||||||
if (buffered.value == Duration.zero) {
|
if (buffered.value == Duration.zero) {
|
||||||
attr = BoxFit.contain;
|
attr = BoxFit.contain;
|
||||||
@@ -973,6 +981,9 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// fill不应该在竖屏视频生效
|
||||||
|
} else if (attr == BoxFit.fill && direction.value == 'vertical') {
|
||||||
|
attr = BoxFit.contain;
|
||||||
}
|
}
|
||||||
_videoFit.value = attr;
|
_videoFit.value = attr;
|
||||||
_videoFitDesc.value = videoFitType[fitValue]['desc'];
|
_videoFitDesc.value = videoFitType[fitValue]['desc'];
|
||||||
@@ -1132,13 +1143,13 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void putDanmakuSettings() {
|
void putDanmakuSettings() {
|
||||||
localCache.put(LocalCacheKey.danmakuWeight, danmakuWeight.value);
|
setting.put(SettingBoxKey.danmakuWeight, danmakuWeight.value);
|
||||||
localCache.put(LocalCacheKey.danmakuBlockType, blockTypes);
|
setting.put(SettingBoxKey.danmakuBlockType, blockTypes);
|
||||||
localCache.put(LocalCacheKey.danmakuShowArea, showArea);
|
setting.put(SettingBoxKey.danmakuShowArea, showArea);
|
||||||
localCache.put(LocalCacheKey.danmakuOpacity, opacityVal);
|
setting.put(SettingBoxKey.danmakuOpacity, opacityVal);
|
||||||
localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal);
|
setting.put(SettingBoxKey.danmakuFontScale, fontSizeVal);
|
||||||
localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal);
|
setting.put(SettingBoxKey.danmakuDuration, danmakuDurationVal);
|
||||||
localCache.put(LocalCacheKey.strokeWidth, strokeWidth);
|
setting.put(SettingBoxKey.strokeWidth, strokeWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dispose({String type = 'single'}) async {
|
Future<void> dispose({String type = 'single'}) async {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import 'package:PiliPalaX/plugin/pl_player/models/fullscreen_mode.dart';
|
|||||||
import 'package:PiliPalaX/plugin/pl_player/utils.dart';
|
import 'package:PiliPalaX/plugin/pl_player/utils.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/methods/video_state.dart';
|
|
||||||
import 'package:screen_brightness/screen_brightness.dart';
|
import 'package:screen_brightness/screen_brightness.dart';
|
||||||
|
|
||||||
import '../../common/widgets/audio_video_progress_bar.dart';
|
import '../../common/widgets/audio_video_progress_bar.dart';
|
||||||
@@ -496,7 +495,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Obx(
|
Obx(
|
||||||
() => Video(
|
() => Video(
|
||||||
key: ValueKey('${_.videoFit.value}${_.continuePlayInBackground.value}'),
|
key: ValueKey('${_.videoFit.value}'),
|
||||||
controller: videoController,
|
controller: videoController,
|
||||||
controls: NoVideoControls,
|
controls: NoVideoControls,
|
||||||
pauseUponEnteringBackgroundMode: !_.continuePlayInBackground.value,
|
pauseUponEnteringBackgroundMode: !_.continuePlayInBackground.value,
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:nil/nil.dart';
|
import 'package:nil/nil.dart';
|
||||||
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
||||||
import 'package:PiliPalaX/plugin/pl_player/widgets/play_pause_btn.dart';
|
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
|
|
||||||
import '../../../common/widgets/audio_video_progress_bar.dart';
|
import '../../../common/widgets/audio_video_progress_bar.dart';
|
||||||
import '../../../utils/utils.dart';
|
|
||||||
|
|
||||||
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final PlPlayerController? controller;
|
final PlPlayerController? controller;
|
||||||
@@ -27,10 +25,6 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color colorTheme = Theme.of(context).colorScheme.primary;
|
Color colorTheme = Theme.of(context).colorScheme.primary;
|
||||||
final _ = controller!;
|
final _ = controller!;
|
||||||
const textStyle = TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 11,
|
|
||||||
);
|
|
||||||
//阅读器限制
|
//阅读器限制
|
||||||
Timer? _accessibilityDebounce;
|
Timer? _accessibilityDebounce;
|
||||||
double _lastAnnouncedValue = -1;
|
double _lastAnnouncedValue = -1;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:appscheme/appscheme.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import '../http/dynamics.dart';
|
|
||||||
import '../http/search.dart';
|
import '../http/search.dart';
|
||||||
import '../models/common/search_type.dart';
|
import '../models/common/search_type.dart';
|
||||||
import 'id_utils.dart';
|
import 'id_utils.dart';
|
||||||
|
|||||||
@@ -1,8 +1,35 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:PiliPalaX/http/constants.dart';
|
import 'package:PiliPalaX/http/constants.dart';
|
||||||
import 'package:PiliPalaX/http/init.dart';
|
import 'package:PiliPalaX/http/init.dart';
|
||||||
import 'package:webview_cookie_manager/webview_cookie_manager.dart';
|
import 'package:webview_cookie_manager/webview_cookie_manager.dart';
|
||||||
|
|
||||||
class SetCookie {
|
class CookieTool {
|
||||||
|
static exportCookie() async {
|
||||||
|
Map<String, String> allCookies = {};
|
||||||
|
List<String> Urls = [HttpString.baseUrl, HttpString.apiBaseUrl, HttpString.tUrl];
|
||||||
|
for (var url in Urls) {
|
||||||
|
allCookies[url] = await WebviewCookieManager().getCookies(url)
|
||||||
|
.then((cookies) => cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '));
|
||||||
|
}
|
||||||
|
return jsonEncode(allCookies);
|
||||||
|
}
|
||||||
|
static importCookie(String cookie) async {
|
||||||
|
var allCookies = jsonDecode(cookie);
|
||||||
|
for (var url in allCookies.keys) {
|
||||||
|
List<String> cookiesStringList = allCookies[url]!.split('; ');
|
||||||
|
List<Cookie> cookies = [];
|
||||||
|
for (var c in cookiesStringList) {
|
||||||
|
List<String> kv = c.split('=');
|
||||||
|
cookies.add(Cookie(kv[0], kv[1]));
|
||||||
|
}
|
||||||
|
await Request.cookieManager.cookieJar.saveFromResponse(Uri.parse(url), cookies);
|
||||||
|
if (url == HttpString.baseUrl) {
|
||||||
|
Request.dio.options.headers['cookie'] = allCookies[url];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
static onSet() async {
|
static onSet() async {
|
||||||
var cookies = await WebviewCookieManager().getCookies(HttpString.baseUrl);
|
var cookies = await WebviewCookieManager().getCookies(HttpString.baseUrl);
|
||||||
await Request.cookieManager.cookieJar
|
await Request.cookieManager.cookieJar
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
@@ -47,6 +48,21 @@ class GStrorage {
|
|||||||
setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); // 设置全局变量
|
setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); // 设置全局变量
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<String> exportAllSettings() async {
|
||||||
|
return jsonEncode({
|
||||||
|
setting.name: setting.toMap(),
|
||||||
|
video.name: video.toMap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> importAllSettings(String data) async {
|
||||||
|
final Map<String, dynamic> map = jsonDecode(data);
|
||||||
|
await setting.clear();
|
||||||
|
await video.clear();
|
||||||
|
await setting.putAll(map[setting.name]);
|
||||||
|
await video.putAll(map[video.name]);
|
||||||
|
}
|
||||||
|
|
||||||
static void regAdapter() {
|
static void regAdapter() {
|
||||||
Hive.registerAdapter(OwnerAdapter());
|
Hive.registerAdapter(OwnerAdapter());
|
||||||
Hive.registerAdapter(UserInfoDataAdapter());
|
Hive.registerAdapter(UserInfoDataAdapter());
|
||||||
@@ -89,6 +105,8 @@ class SettingBoxKey {
|
|||||||
enableHA = 'enableHA',
|
enableHA = 'enableHA',
|
||||||
useOpenSLES = 'useOpenSLES',
|
useOpenSLES = 'useOpenSLES',
|
||||||
expandBuffer = 'expandBuffer',
|
expandBuffer = 'expandBuffer',
|
||||||
|
hardwareDecoding = 'hardwareDecoding',
|
||||||
|
videoSync = 'videoSync',
|
||||||
enableVerticalExpand = 'enableVerticalExpand',
|
enableVerticalExpand = 'enableVerticalExpand',
|
||||||
enableOnlineTotal = 'enableOnlineTotal',
|
enableOnlineTotal = 'enableOnlineTotal',
|
||||||
enableAutoBrightness = 'enableAutoBrightness',
|
enableAutoBrightness = 'enableAutoBrightness',
|
||||||
@@ -136,7 +154,20 @@ class SettingBoxKey {
|
|||||||
enableSystemProxy = 'enableSystemProxy',
|
enableSystemProxy = 'enableSystemProxy',
|
||||||
enableAi = 'enableAi',
|
enableAi = 'enableAi',
|
||||||
disableLikeMsg = 'disableLikeMsg',
|
disableLikeMsg = 'disableLikeMsg',
|
||||||
defaultHomePage = 'defaultHomePage';
|
defaultHomePage = 'defaultHomePage',
|
||||||
|
|
||||||
|
// 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细
|
||||||
|
danmakuWeight = 'danmakuWeight',
|
||||||
|
danmakuBlockType = 'danmakuBlockType',
|
||||||
|
danmakuShowArea = 'danmakuShowArea',
|
||||||
|
danmakuOpacity = 'danmakuOpacity',
|
||||||
|
danmakuFontScale = 'danmakuFontScale',
|
||||||
|
danmakuDuration = 'danmakuDuration',
|
||||||
|
strokeWidth = 'strokeWidth',
|
||||||
|
|
||||||
|
// 代理host port
|
||||||
|
systemProxyHost = 'systemProxyHost',
|
||||||
|
systemProxyPort = 'systemProxyPort';
|
||||||
|
|
||||||
/// 外观
|
/// 外观
|
||||||
static const String themeMode = 'themeMode',
|
static const String themeMode = 'themeMode',
|
||||||
@@ -146,6 +177,7 @@ class SettingBoxKey {
|
|||||||
enableSingleRow = 'enableSingleRow', // 首页单列
|
enableSingleRow = 'enableSingleRow', // 首页单列
|
||||||
displayMode = 'displayMode',
|
displayMode = 'displayMode',
|
||||||
maxRowWidth = 'maxRowWidth', // 首页列最大宽度(dp)
|
maxRowWidth = 'maxRowWidth', // 首页列最大宽度(dp)
|
||||||
|
adaptiveNavBar = 'adaptiveNavBar',
|
||||||
enableMYBar = 'enableMYBar',
|
enableMYBar = 'enableMYBar',
|
||||||
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
||||||
hideTabBar = 'hideTabBar', // 收起底栏
|
hideTabBar = 'hideTabBar', // 收起底栏
|
||||||
@@ -164,20 +196,7 @@ class LocalCacheKey {
|
|||||||
|
|
||||||
//
|
//
|
||||||
wbiKeys = 'wbiKeys',
|
wbiKeys = 'wbiKeys',
|
||||||
timeStamp = 'timeStamp',
|
timeStamp = 'timeStamp';
|
||||||
|
|
||||||
// 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细
|
|
||||||
danmakuWeight = 'danmakuWeight',
|
|
||||||
danmakuBlockType = 'danmakuBlockType',
|
|
||||||
danmakuShowArea = 'danmakuShowArea',
|
|
||||||
danmakuOpacity = 'danmakuOpacity',
|
|
||||||
danmakuFontScale = 'danmakuFontScale',
|
|
||||||
danmakuDuration = 'danmakuDuration',
|
|
||||||
strokeWidth = 'strokeWidth',
|
|
||||||
|
|
||||||
// 代理host port
|
|
||||||
systemProxyHost = 'systemProxyHost',
|
|
||||||
systemProxyPort = 'systemProxyPort';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoBoxKey {
|
class VideoBoxKey {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:PiliPalaX/utils/storage.dart';
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
@@ -193,7 +192,6 @@ class Utils {
|
|||||||
toInt: true,
|
toInt: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
print('distance: $distance');
|
|
||||||
if (distance <= 60) {
|
if (distance <= 60) {
|
||||||
return '刚刚';
|
return '刚刚';
|
||||||
} else if (distance <= 3600) {
|
} else if (distance <= 3600) {
|
||||||
|
|||||||
Reference in New Issue
Block a user