feat: edit media library definitions

This commit is contained in:
ngfchl
2026-07-18 13:07:54 +08:00
parent 86a4824b32
commit 9755e522fc
2 changed files with 83 additions and 18 deletions
+69 -18
View File
@@ -257,6 +257,8 @@ class _MediaLibraryPageState extends ConsumerState<MediaLibraryPage> {
onTap: () => ref
.read(mediaLibraryProvider.notifier)
.selectLibrary(library.id),
onEdit: () =>
_showEditLibraryDialog(context, ref, library),
onDelete: () => ref
.read(mediaLibraryProvider.notifier)
.deleteLibrary(library.id),
@@ -537,6 +539,22 @@ class _MediaLibraryPageState extends ConsumerState<MediaLibraryPage> {
);
}
static void _showEditLibraryDialog(
BuildContext context,
WidgetRef ref,
MediaLibraryDefinition library,
) {
showShadDialog(
context: context,
builder: (_) => _CreateMediaLibraryDialog(
initialRootID: library.rootID,
initialPath: library.rootPath,
initialName: library.name,
editingLibrary: library,
),
);
}
void _saveApiKey() {
final key = _apiKeyController.text.trim();
StorageManager.set(StorageKeys.tmdbApiKey, key);
@@ -591,11 +609,13 @@ class _CreateMediaLibraryDialog extends ConsumerStatefulWidget {
final String? initialRootID;
final String initialPath;
final String initialName;
final MediaLibraryDefinition? editingLibrary;
const _CreateMediaLibraryDialog({
required this.initialRootID,
required this.initialPath,
required this.initialName,
this.editingLibrary,
});
@override
@@ -616,17 +636,26 @@ class _CreateMediaLibraryDialogState
final _browserPath = <CloudFile>[];
var _folders = <CloudFile>[];
bool get _isEditing => widget.editingLibrary != null;
@override
void initState() {
super.initState();
_sources = [
MediaLibrarySource(
id: 'initial-source',
rootID: widget.initialRootID,
path: widget.initialPath,
),
];
_sources =
widget.editingLibrary?.sources ??
[
MediaLibrarySource(
id: 'initial-source',
rootID: widget.initialRootID,
path: widget.initialPath,
),
];
_nameController = TextEditingController(text: widget.initialName);
if (_isEditing) {
_kind = widget.editingLibrary!.kind;
_recursive = widget.editingLibrary!.recursive;
_minSizeController.text = widget.editingLibrary!.minimumSizeMB.toString();
}
}
@override
@@ -730,26 +759,38 @@ class _CreateMediaLibraryDialogState
});
}
void _create(BuildContext context) {
ref
.read(mediaLibraryProvider.notifier)
.createLibrary(
Future<void> _save(BuildContext context) async {
final notifier = ref.read(mediaLibraryProvider.notifier);
if (_isEditing) {
await notifier.updateLibrary(
widget.editingLibrary!.copyWith(
name: _nameController.text,
rootID: _sources.isEmpty ? null : _sources.first.rootID,
rootPath: _sources.isEmpty ? '未配置目录' : _sources.first.path,
sources: _sources,
kind: _kind,
recursive: _recursive,
minimumSizeMB: int.tryParse(_minSizeController.text.trim()) ?? 50,
);
Navigator.of(context).pop();
updatedAt: DateTime.now(),
),
);
} else {
await notifier.createLibrary(
name: _nameController.text,
rootID: _sources.isEmpty ? null : _sources.first.rootID,
rootPath: _sources.isEmpty ? '未配置目录' : _sources.first.path,
sources: _sources,
kind: _kind,
recursive: _recursive,
minimumSizeMB: int.tryParse(_minSizeController.text.trim()) ?? 50,
);
}
if (context.mounted) Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
return ShadDialog(
title: Text(_isBrowsing ? '选择云盘文件夹' : '创建媒体库'),
title: Text(_isBrowsing ? '选择云盘文件夹' : (_isEditing ? '管理媒体库' : '创建媒体库')),
description: Text(
_isBrowsing ? '进入目标目录后,选择该目录作为媒体库来源。' : '媒体库会从指定目录扫描视频文件。',
),
@@ -771,9 +812,9 @@ class _CreateMediaLibraryDialogState
child: const Text('取消'),
),
ShadButton(
onPressed: _sources.isEmpty ? null : () => _create(context),
onPressed: _sources.isEmpty ? null : () => _save(context),
leading: const Icon(Icons.add_rounded, size: 16),
child: const Text('创建媒体库'),
child: Text(_isEditing ? '保存并扫描' : '创建媒体库'),
),
],
child: _isBrowsing ? _folderBrowser(cs) : _form(cs),
@@ -1039,12 +1080,14 @@ class _LibraryRow extends StatelessWidget {
final MediaLibraryDefinition library;
final bool selected;
final VoidCallback onTap;
final VoidCallback onEdit;
final VoidCallback onDelete;
const _LibraryRow({
required this.library,
required this.selected,
required this.onTap,
required this.onEdit,
required this.onDelete,
});
@@ -1104,6 +1147,14 @@ class _LibraryRow extends StatelessWidget {
],
),
),
ShadTooltip(
builder: (_) => const Text('管理媒体库'),
child: ShadButton.ghost(
size: ShadButtonSize.sm,
onPressed: onEdit,
child: const Icon(Icons.edit_outlined, size: 15),
),
),
ShadTooltip(
builder: (_) => const Text('删除媒体库'),
child: ShadButton.destructive(
+14
View File
@@ -179,6 +179,20 @@ class MediaLibraryNotifier extends StateNotifier<MediaLibraryState> {
);
}
Future<void> updateLibrary(MediaLibraryDefinition library) async {
if (library.name.trim().isEmpty || library.sources.isEmpty) return;
final libraries = state.libraries
.map((item) => item.id == library.id ? library : item)
.toList();
await _saveLibraries(libraries);
state = state.copyWith(
libraries: libraries,
selectedLibraryID: library.id,
items: _loadItems(library.id),
statusMessage: '媒体库「${library.name}」已更新',
);
}
Future<void> scanSelectedLibrary() async {
final library = state.selectedLibrary;
if (library == null || _api == null || state.isScanning) return;