mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-31 16:18:22 +08:00
feat: webdav
Closes #432 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -7,12 +7,14 @@ import 'package:PiliPlus/pages/setting/privacy_setting.dart';
|
|||||||
import 'package:PiliPlus/pages/setting/recommend_setting.dart';
|
import 'package:PiliPlus/pages/setting/recommend_setting.dart';
|
||||||
import 'package:PiliPlus/pages/setting/style_setting.dart';
|
import 'package:PiliPlus/pages/setting/style_setting.dart';
|
||||||
import 'package:PiliPlus/pages/setting/video_setting.dart';
|
import 'package:PiliPlus/pages/setting/video_setting.dart';
|
||||||
|
import 'package:PiliPlus/pages/webdav/view.dart';
|
||||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.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 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
|
||||||
import 'widgets/multi_select_dialog.dart';
|
import 'widgets/multi_select_dialog.dart';
|
||||||
|
|
||||||
@@ -84,6 +86,11 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
subtitle: '震动、搜索、收藏、ai、评论、动态、代理、更新检查等',
|
subtitle: '震动、搜索、收藏、ai、评论、动态、代理、更新检查等',
|
||||||
icon: Icons.extension_outlined,
|
icon: Icons.extension_outlined,
|
||||||
),
|
),
|
||||||
|
_SettingsModel(
|
||||||
|
name: 'webdavSetting',
|
||||||
|
title: 'WebDAV 设置',
|
||||||
|
icon: MdiIcons.databaseCogOutline,
|
||||||
|
),
|
||||||
_SettingsModel(
|
_SettingsModel(
|
||||||
name: 'about',
|
name: 'about',
|
||||||
title: '关于',
|
title: '关于',
|
||||||
@@ -104,6 +111,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
'playSetting' => '播放器设置',
|
'playSetting' => '播放器设置',
|
||||||
'styleSetting' => '外观设置',
|
'styleSetting' => '外观设置',
|
||||||
'extraSetting' => '其它设置',
|
'extraSetting' => '其它设置',
|
||||||
|
'webdavSetting' => 'WebDAV 设置',
|
||||||
'about' => '关于',
|
'about' => '关于',
|
||||||
_ => '设置',
|
_ => '设置',
|
||||||
}),
|
}),
|
||||||
@@ -127,6 +135,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
'playSetting' => PlaySetting(showAppBar: false),
|
'playSetting' => PlaySetting(showAppBar: false),
|
||||||
'styleSetting' => StyleSetting(showAppBar: false),
|
'styleSetting' => StyleSetting(showAppBar: false),
|
||||||
'extraSetting' => ExtraSetting(showAppBar: false),
|
'extraSetting' => ExtraSetting(showAppBar: false),
|
||||||
|
'webdavSetting' => WebDavSettingPage(showAppBar: false),
|
||||||
'about' => AboutPage(showAppBar: false),
|
'about' => AboutPage(showAppBar: false),
|
||||||
_ => const SizedBox.shrink(),
|
_ => const SizedBox.shrink(),
|
||||||
},
|
},
|
||||||
|
|||||||
134
lib/pages/webdav/view.dart
Normal file
134
lib/pages/webdav/view.dart
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import 'package:PiliPlus/pages/webdav/webdav.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
|
||||||
|
class WebDavSettingPage extends StatefulWidget {
|
||||||
|
const WebDavSettingPage({
|
||||||
|
super.key,
|
||||||
|
this.showAppBar,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool? showAppBar;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WebDavSettingPage> createState() => _WebDavSettingPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebDavSettingPageState extends State<WebDavSettingPage> {
|
||||||
|
final _uriCtr = TextEditingController(text: GStorage.webdavUri);
|
||||||
|
final _usernameCtr = TextEditingController(text: GStorage.webdavUsername);
|
||||||
|
final _passwordCtr = TextEditingController(text: GStorage.webdavPassword);
|
||||||
|
final _directoryCtr = TextEditingController(text: GStorage.webdavDirectory);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_uriCtr.dispose();
|
||||||
|
_usernameCtr.dispose();
|
||||||
|
_passwordCtr.dispose();
|
||||||
|
_directoryCtr.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
EdgeInsets padding = MediaQuery.paddingOf(context);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: widget.showAppBar == false
|
||||||
|
? null
|
||||||
|
: AppBar(title: const Text('WebDAV 设置')),
|
||||||
|
body: ListView(
|
||||||
|
padding: padding.copyWith(
|
||||||
|
top: 20,
|
||||||
|
left: padding.left + 20,
|
||||||
|
right: padding.right + 20,
|
||||||
|
bottom: padding.bottom + 90,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _uriCtr,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '地址',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
TextField(
|
||||||
|
controller: _usernameCtr,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '用户',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
TextField(
|
||||||
|
controller: _passwordCtr,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '密码',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
TextField(
|
||||||
|
controller: _directoryCtr,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '路径',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: FilledButton.tonal(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: WebDav().backup,
|
||||||
|
child: const Text('备份设置'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Expanded(
|
||||||
|
child: FilledButton.tonal(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: WebDav().restore,
|
||||||
|
child: const Text('恢复设置'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
child: const Icon(Icons.save),
|
||||||
|
onPressed: () async {
|
||||||
|
if (_uriCtr.text.isEmpty) {
|
||||||
|
SmartDialog.showToast('地址不能为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await GStorage.setting.put(SettingBoxKey.webdavUri, _uriCtr.text);
|
||||||
|
await GStorage.setting
|
||||||
|
.put(SettingBoxKey.webdavUsername, _usernameCtr.text);
|
||||||
|
await GStorage.setting
|
||||||
|
.put(SettingBoxKey.webdavPassword, _passwordCtr.text);
|
||||||
|
await GStorage.setting
|
||||||
|
.put(SettingBoxKey.webdavDirectory, _directoryCtr.text);
|
||||||
|
try {
|
||||||
|
final res = await WebDav().init();
|
||||||
|
SmartDialog.showToast('配置${res ? '成功' : '失败'}');
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('配置失败: ${e.toString()}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
lib/pages/webdav/webdav.dart
Normal file
88
lib/pages/webdav/webdav.dart
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:webdav_client/webdav_client.dart' as webdav;
|
||||||
|
|
||||||
|
class WebDav {
|
||||||
|
late String _webDavUri;
|
||||||
|
late String _webDavUsername;
|
||||||
|
late String _webDavPassword;
|
||||||
|
late String _webdavDirectory;
|
||||||
|
|
||||||
|
webdav.Client? _client;
|
||||||
|
|
||||||
|
WebDav._internal();
|
||||||
|
static final WebDav _instance = WebDav._internal();
|
||||||
|
factory WebDav() => _instance;
|
||||||
|
|
||||||
|
Future<bool> init() async {
|
||||||
|
_webDavUri = GStorage.webdavUri;
|
||||||
|
_webDavUsername = GStorage.webdavUsername;
|
||||||
|
_webDavPassword = GStorage.webdavPassword;
|
||||||
|
_webdavDirectory = GStorage.webdavDirectory;
|
||||||
|
if (_webdavDirectory.endsWith('/').not) {
|
||||||
|
_webdavDirectory += '/';
|
||||||
|
}
|
||||||
|
_webdavDirectory += 'PiliPlus';
|
||||||
|
|
||||||
|
try {
|
||||||
|
_client = null;
|
||||||
|
final client = webdav.newClient(
|
||||||
|
_webDavUri,
|
||||||
|
user: _webDavUsername,
|
||||||
|
password: _webDavPassword,
|
||||||
|
)
|
||||||
|
..setHeaders({'accept-charset': 'utf-8'})
|
||||||
|
..setConnectTimeout(4000)
|
||||||
|
..setReceiveTimeout(4000)
|
||||||
|
..setSendTimeout(4000);
|
||||||
|
|
||||||
|
await client.mkdirAll(_webdavDirectory);
|
||||||
|
|
||||||
|
_client = client;
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future backup() async {
|
||||||
|
if (_client == null) {
|
||||||
|
if (await init() == false) {
|
||||||
|
SmartDialog.showToast('备份失败,请检查配置');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final path = '$_webdavDirectory/piliplus_settings.json';
|
||||||
|
final file = File(path);
|
||||||
|
if (await file.exists()) {
|
||||||
|
await file.delete();
|
||||||
|
}
|
||||||
|
String data = await GStorage.exportAllSettings();
|
||||||
|
await _client!.write(path, utf8.encode(data));
|
||||||
|
SmartDialog.showToast('备份成功');
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('备份失败: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future restore() async {
|
||||||
|
if (_client == null) {
|
||||||
|
if (await init() == false) {
|
||||||
|
SmartDialog.showToast('恢复失败,请检查配置');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final path = '$_webdavDirectory/piliplus_settings.json';
|
||||||
|
final data = await _client!.read(path);
|
||||||
|
await GStorage.importAllSettings(utf8.decode(data));
|
||||||
|
SmartDialog.showToast('恢复成功');
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('恢复失败: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import 'package:PiliPlus/pages/setting/sponsor_block_page.dart';
|
|||||||
import 'package:PiliPlus/pages/setting/view.dart';
|
import 'package:PiliPlus/pages/setting/view.dart';
|
||||||
import 'package:PiliPlus/pages/video/detail/introduction/widgets/create_fav_page.dart';
|
import 'package:PiliPlus/pages/video/detail/introduction/widgets/create_fav_page.dart';
|
||||||
import 'package:PiliPlus/pages/video/detail/view_v.dart';
|
import 'package:PiliPlus/pages/video/detail/view_v.dart';
|
||||||
|
import 'package:PiliPlus/pages/webdav/view.dart';
|
||||||
import 'package:PiliPlus/pages/webview/webview_page.dart';
|
import 'package:PiliPlus/pages/webview/webview_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -175,6 +176,8 @@ class Routes {
|
|||||||
name: '/navbarSetting', page: () => const NavigationBarSetPage()),
|
name: '/navbarSetting', page: () => const NavigationBarSetPage()),
|
||||||
CustomGetPage(
|
CustomGetPage(
|
||||||
name: '/settingsSearch', page: () => const SettingsSearchPage()),
|
name: '/settingsSearch', page: () => const SettingsSearchPage()),
|
||||||
|
CustomGetPage(
|
||||||
|
name: '/webdavSetting', page: () => const WebDavSettingPage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -451,6 +451,18 @@ class GStorage {
|
|||||||
static bool get navSearchStreamDebounce => GStorage.setting
|
static bool get navSearchStreamDebounce => GStorage.setting
|
||||||
.get(SettingBoxKey.navSearchStreamDebounce, defaultValue: false);
|
.get(SettingBoxKey.navSearchStreamDebounce, defaultValue: false);
|
||||||
|
|
||||||
|
static String get webdavUri =>
|
||||||
|
GStorage.setting.get(SettingBoxKey.webdavUri, defaultValue: '');
|
||||||
|
|
||||||
|
static String get webdavUsername =>
|
||||||
|
GStorage.setting.get(SettingBoxKey.webdavUsername, defaultValue: '');
|
||||||
|
|
||||||
|
static String get webdavPassword =>
|
||||||
|
GStorage.setting.get(SettingBoxKey.webdavPassword, defaultValue: '');
|
||||||
|
|
||||||
|
static String get webdavDirectory =>
|
||||||
|
GStorage.setting.get(SettingBoxKey.webdavDirectory, defaultValue: '/');
|
||||||
|
|
||||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||||
|
|
||||||
@@ -737,6 +749,12 @@ class SettingBoxKey {
|
|||||||
recordSearchHistory = 'recordSearchHistory',
|
recordSearchHistory = 'recordSearchHistory',
|
||||||
navSearchStreamDebounce = 'navSearchStreamDebounce',
|
navSearchStreamDebounce = 'navSearchStreamDebounce',
|
||||||
|
|
||||||
|
// WebDAV
|
||||||
|
webdavUri = 'webdavUri',
|
||||||
|
webdavUsername = 'webdavUsername',
|
||||||
|
webdavPassword = 'webdavPassword',
|
||||||
|
webdavDirectory = 'webdavDirectory',
|
||||||
|
|
||||||
// Sponsor Block
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
blockSettings = 'blockSettings',
|
blockSettings = 'blockSettings',
|
||||||
|
|||||||
@@ -2022,6 +2022,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
webdav_client:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: webdav_client
|
||||||
|
sha256: "682fffc50b61dc0e8f46717171db03bf9caaa17347be41c0c91e297553bf86b2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
webview_cookie_manager:
|
webview_cookie_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ dependencies:
|
|||||||
fl_chart: ^0.69.2
|
fl_chart: ^0.69.2
|
||||||
synchronized: ^3.3.0
|
synchronized: ^3.3.0
|
||||||
document_file_save_plus: ^2.0.0
|
document_file_save_plus: ^2.0.0
|
||||||
|
webdav_client: ^1.2.2
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
screen_brightness: ^2.0.1
|
screen_brightness: ^2.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user