From b505be1da8002719053bac83c2de4711e1d49574 Mon Sep 17 00:00:00 2001 From: ngfchl Date: Sat, 18 Jul 2026 20:57:55 +0800 Subject: [PATCH] sequence media sync matching and rename --- lib/pages/media_library_page.dart | 7 ++- lib/providers/media_library_provider.dart | 64 +++++++++++++++++------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/lib/pages/media_library_page.dart b/lib/pages/media_library_page.dart index ed4dec9..103409d 100644 --- a/lib/pages/media_library_page.dart +++ b/lib/pages/media_library_page.dart @@ -120,10 +120,15 @@ class _MediaLibraryPageState extends ConsumerState { if (!mounted) return; ShadSonner.maybeOf(context)?.show( next.errorMessage == null - ? ShadToast(title: const Text('媒体库'), description: Text(message)) + ? ShadToast( + title: const Text('媒体库'), + description: Text(message), + showCloseIconOnlyWhenHovered: false, + ) : ShadToast.destructive( title: const Text('媒体库操作失败'), description: Text(message), + showCloseIconOnlyWhenHovered: false, ), ); }); diff --git a/lib/providers/media_library_provider.dart b/lib/providers/media_library_provider.dart index 2e2bdef..7ed917f 100644 --- a/lib/providers/media_library_provider.dart +++ b/lib/providers/media_library_provider.dart @@ -572,21 +572,47 @@ class MediaLibraryNotifier extends StateNotifier { if (originals.isEmpty) return; final apiKey = StorageManager.get(StorageKeys.tmdbApiKey) ?? ''; state = state.copyWith(statusMessage: '正在同步云盘文件信息…', clearError: true); - final updates = []; - final replacements = {}; + final synced = <_SyncedMediaItem>[]; final failures = []; - var recognizedCount = 0; for (final original in originals) { try { final latestFile = await _resolveCurrentCloudFile(original.file); if (latestFile == null) { throw StateError('云盘中未找到该文件'); } - final fallback = MediaLibraryItem.fromFile( - original.libraryID, - latestFile, - directoryName: _parentDirectoryName(latestFile.cloudPath), + synced.add( + _SyncedMediaItem( + original: original, + fallback: MediaLibraryItem.fromFile( + original.libraryID, + latestFile, + directoryName: _parentDirectoryName(latestFile.cloudPath), + ), + ), ); + } catch (error) { + failures.add('${original.file.name}: $error'); + } + } + if (synced.isEmpty) { + state = state.copyWith( + errorMessage: failures.isEmpty + ? '未能同步云盘文件信息' + : '同步失败:${failures.first}', + statusMessage: '未能同步云盘文件信息', + ); + return; + } + + state = state.copyWith(statusMessage: '正在自动识别并规范命名…'); + final updates = []; + final replacements = {}; + var recognizedCount = 0; + var renamedCount = 0; + for (final entry in synced) { + final original = entry.original; + try { + final fallback = entry.fallback; var updated = apiKey.trim().isEmpty ? fallback : await _recognizeMediaItem( @@ -602,11 +628,13 @@ class MediaLibraryNotifier extends StateNotifier { // scraped item just because its refreshed file metadata was available. if (updated.tmdbID == null && original.tmdbID != null) { updated = original.copyWith( - file: latestFile, + file: fallback.file, updatedAt: DateTime.now(), ); } + final beforeName = updated.file.name; updated = await _renameMatchedMediaFile(updated); + if (updated.file.name != beforeName) renamedCount++; updates.add(updated); replacements['${original.libraryID}:${original.id}'] = updated; } catch (error) { @@ -626,16 +654,13 @@ class MediaLibraryNotifier extends StateNotifier { item, ) .toList(), - statusMessage: failures.isEmpty - ? '已同步 ${updates.length} 个资源,自动识别 $recognizedCount 个' - : '已同步 ${updates.length} 个资源,识别 $recognizedCount 个,${failures.length} 个同步失败', + statusMessage: apiKey.trim().isEmpty + ? '已同步 ${updates.length} 个资源,未配置 TMDB,未执行自动识别' + : failures.isEmpty + ? '已同步 ${updates.length} 个资源,自动识别 $recognizedCount 个,规范命名 $renamedCount 个' + : '已同步 ${updates.length} 个资源,识别 $recognizedCount 个,规范命名 $renamedCount 个,${failures.length} 个失败', errorMessage: failures.isEmpty ? null : failures.first, ); - } else if (failures.isNotEmpty) { - state = state.copyWith( - errorMessage: '同步失败:${failures.first}', - statusMessage: '未能同步云盘文件信息', - ); } } @@ -1733,6 +1758,13 @@ class _ScanFolder { const _ScanFolder(this.id, this.path); } +class _SyncedMediaItem { + final MediaLibraryItem original; + final MediaLibraryItem fallback; + + const _SyncedMediaItem({required this.original, required this.fallback}); +} + final mediaLibraryProvider = StateNotifierProvider( (ref) => MediaLibraryNotifier(),