Skip to content

Commit 2be2ff3

Browse files
AnonymousAnonymous
authored andcommitted
fix: path fix
1 parent bc340d8 commit 2be2ff3

3 files changed

Lines changed: 102 additions & 47 deletions

File tree

lib/src/providers/lyric_provider.dart

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ class LyricController extends StateNotifier<LyricState> {
273273
// 确保数据库已初始化
274274
await SubtitleLibraryService.ensureInitialized();
275275

276+
// 获取字幕库根目录,用于将相对路径拼接为绝对路径
277+
final libraryDir =
278+
await SubtitleLibraryService.getSubtitleLibraryDirectory();
279+
final libraryRoot = libraryDir.path;
280+
276281
// 优先级1: 通过 workId 查询数据库
277282
if (workId != null) {
278283
final records =
@@ -285,13 +290,14 @@ class LyricController extends StateNotifier<LyricState> {
285290
final (isMatch, score) = SubtitleLibraryService.checkMatch(
286291
record.fileName, trackTitle);
287292
if (isMatch && score > bestScore) {
293+
final absolutePath = record.absolutePath(libraryRoot);
288294
// 验证文件是否仍存在(DB 可能过期)
289-
if (!await File(record.filePath).exists()) continue;
295+
if (!await File(absolutePath).exists()) continue;
290296
bestScore = score;
291-
bestMatchPath = record.filePath;
297+
bestMatchPath = absolutePath;
292298
if (score == 1.0) {
293299
print('[Lyric] 在数据库中找到完美匹配 (workId=$workId): ${record.fileName}');
294-
return record.filePath;
300+
return absolutePath;
295301
}
296302
}
297303
}
@@ -314,12 +320,13 @@ class LyricController extends StateNotifier<LyricState> {
314320
final (isMatch, score) = SubtitleLibraryService.checkMatch(
315321
record.fileName, trackTitle);
316322
if (isMatch && score > bestScore) {
317-
if (!await File(record.filePath).exists()) continue;
323+
final absolutePath = record.absolutePath(libraryRoot);
324+
if (!await File(absolutePath).exists()) continue;
318325
bestScore = score;
319-
bestMatchPath = record.filePath;
326+
bestMatchPath = absolutePath;
320327
if (score == 1.0) {
321328
print('[Lyric] 在"已保存"中找到完美匹配: ${record.fileName}');
322-
return record.filePath;
329+
return absolutePath;
323330
}
324331
}
325332
}

lib/src/services/subtitle_database.dart

Lines changed: 78 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import 'package:path/path.dart' as p;
44
import 'package:path_provider/path_provider.dart';
55

66
/// 字幕文件数据库记录
7+
///
8+
/// 数据库只存储相对路径(相对于字幕库根目录),绝对路径在运行时动态拼接。
9+
/// 这样即使 iOS 重装后沙盒 UUID 变化,或其他平台迁移下载目录,数据库无需更新。
710
class SubtitleFileRecord {
811
final String fileName;
9-
final String filePath;
1012
final String relativePath;
1113
final String category;
1214
final int? workId;
@@ -16,7 +18,6 @@ class SubtitleFileRecord {
1618

1719
SubtitleFileRecord({
1820
required this.fileName,
19-
required this.filePath,
2021
required this.relativePath,
2122
required this.category,
2223
this.workId,
@@ -25,9 +26,13 @@ class SubtitleFileRecord {
2526
this.normalizedName,
2627
});
2728

29+
/// 根据字幕库根目录拼接出绝对路径
30+
String absolutePath(String libraryRootPath) {
31+
return p.joinAll([libraryRootPath, ...relativePath.split('/')]);
32+
}
33+
2834
Map<String, dynamic> toMap() => {
2935
'file_name': fileName,
30-
'file_path': filePath,
3136
'relative_path': relativePath,
3237
'category': category,
3338
'work_id': workId,
@@ -39,7 +44,6 @@ class SubtitleFileRecord {
3944
factory SubtitleFileRecord.fromMap(Map<String, dynamic> map) {
4045
return SubtitleFileRecord(
4146
fileName: map['file_name'] as String,
42-
filePath: map['file_path'] as String,
4347
relativePath: map['relative_path'] as String,
4448
category: map['category'] as String,
4549
workId: map['work_id'] as int?,
@@ -76,8 +80,9 @@ class SubtitleDatabase {
7680

7781
return await openDatabase(
7882
path,
79-
version: 1,
83+
version: 2,
8084
onCreate: _createDB,
85+
onUpgrade: _upgradeDB,
8186
);
8287
}
8388

@@ -86,8 +91,7 @@ class SubtitleDatabase {
8691
CREATE TABLE subtitle_files (
8792
id INTEGER PRIMARY KEY AUTOINCREMENT,
8893
file_name TEXT NOT NULL,
89-
file_path TEXT NOT NULL UNIQUE,
90-
relative_path TEXT NOT NULL,
94+
relative_path TEXT NOT NULL UNIQUE,
9195
category TEXT NOT NULL,
9296
work_id INTEGER,
9397
file_size INTEGER DEFAULT 0,
@@ -101,8 +105,6 @@ class SubtitleDatabase {
101105
'CREATE INDEX idx_files_work_id ON subtitle_files(work_id)');
102106
await db.execute(
103107
'CREATE INDEX idx_files_category ON subtitle_files(category)');
104-
await db.execute(
105-
'CREATE INDEX idx_files_file_path ON subtitle_files(file_path)');
106108

107109
await db.execute('''
108110
CREATE TABLE library_meta (
@@ -112,6 +114,41 @@ class SubtitleDatabase {
112114
''');
113115
}
114116

117+
Future<void> _upgradeDB(Database db, int oldVersion, int newVersion) async {
118+
if (oldVersion < 2) {
119+
// v2: 移除 file_path 列,改用 relative_path 作为唯一键
120+
// SQLite 不支持直接 DROP COLUMN,需要重建表
121+
await db.execute(
122+
'ALTER TABLE subtitle_files RENAME TO subtitle_files_old');
123+
await db.execute('''
124+
CREATE TABLE subtitle_files (
125+
id INTEGER PRIMARY KEY AUTOINCREMENT,
126+
file_name TEXT NOT NULL,
127+
relative_path TEXT NOT NULL UNIQUE,
128+
category TEXT NOT NULL,
129+
work_id INTEGER,
130+
file_size INTEGER DEFAULT 0,
131+
modified_at TEXT,
132+
normalized_name TEXT,
133+
created_at TEXT DEFAULT (datetime('now'))
134+
)
135+
''');
136+
await db.execute('''
137+
INSERT OR REPLACE INTO subtitle_files
138+
(file_name, relative_path, category, work_id, file_size,
139+
modified_at, normalized_name, created_at)
140+
SELECT file_name, relative_path, category, work_id, file_size,
141+
modified_at, normalized_name, created_at
142+
FROM subtitle_files_old
143+
''');
144+
await db.execute('DROP TABLE subtitle_files_old');
145+
await db.execute(
146+
'CREATE INDEX idx_files_work_id ON subtitle_files(work_id)');
147+
await db.execute(
148+
'CREATE INDEX idx_files_category ON subtitle_files(category)');
149+
}
150+
}
151+
115152
// ==================== CRUD ====================
116153

117154
/// 插入单条记录
@@ -133,75 +170,70 @@ class SubtitleDatabase {
133170
await batch.commit(noResult: true);
134171
}
135172

136-
/// 按文件路径删除
137-
Future<int> deleteByPath(String filePath) async {
173+
/// 按相对路径删除
174+
Future<int> deleteByRelativePath(String relativePath) async {
138175
final db = await database;
139176
return await db.delete('subtitle_files',
140-
where: 'file_path = ?', whereArgs: [filePath]);
177+
where: 'relative_path = ?', whereArgs: [relativePath]);
141178
}
142179

143-
/// 按路径前缀删除(用于删除目录下所有文件)
144-
Future<int> deleteByPathPrefix(String directoryPath) async {
180+
/// 按相对路径前缀删除(用于删除目录下所有文件)
181+
Future<int> deleteByRelativePathPrefix(String relativePrefix) async {
145182
final db = await database;
146183
// 确保路径以 / 结尾,避免误删同前缀的其他目录
147184
final prefix =
148-
directoryPath.endsWith('/') ? directoryPath : '$directoryPath/';
185+
relativePrefix.endsWith('/') ? relativePrefix : '$relativePrefix/';
149186
return await db.delete('subtitle_files',
150-
where: 'file_path LIKE ?', whereArgs: ['$prefix%']);
187+
where: 'relative_path LIKE ?', whereArgs: ['$prefix%']);
151188
}
152189

153190
/// 更新单个文件的路径(重命名)
154-
Future<void> updateFilePath(
155-
String oldPath,
156-
String newFilePath,
191+
Future<void> updateRelativePath(
192+
String oldRelativePath,
157193
String newRelativePath,
158194
String newFileName,
159195
) async {
160196
final db = await database;
197+
final newCategory = _extractCategoryFromRelativePath(newRelativePath);
198+
final newWorkId = _extractWorkIdFromRelativePath(newRelativePath);
161199
await db.update(
162200
'subtitle_files',
163201
{
164-
'file_path': newFilePath,
165202
'relative_path': newRelativePath,
166203
'file_name': newFileName,
204+
'category': newCategory,
205+
'work_id': newWorkId,
167206
},
168-
where: 'file_path = ?',
169-
whereArgs: [oldPath],
207+
where: 'relative_path = ?',
208+
whereArgs: [oldRelativePath],
170209
);
171210
}
172211

173212
/// 批量更新目录路径(目录重命名/移动)
174-
Future<void> updateDirectoryPaths(
175-
String oldDirPath,
176-
String newDirPath,
177-
String libraryRootPath,
213+
Future<void> updateDirectoryRelativePaths(
214+
String oldRelativePrefix,
215+
String newRelativePrefix,
178216
) async {
179217
final db = await database;
180218
final oldPrefix =
181-
oldDirPath.endsWith('/') ? oldDirPath : '$oldDirPath/';
219+
oldRelativePrefix.endsWith('/') ? oldRelativePrefix : '$oldRelativePrefix/';
182220
final files = await db.query('subtitle_files',
183-
where: 'file_path LIKE ?', whereArgs: ['$oldPrefix%']);
221+
where: 'relative_path LIKE ?', whereArgs: ['$oldPrefix%']);
184222

185223
if (files.isEmpty) return;
186224

187225
final batch = db.batch();
188226
for (final file in files) {
189-
final oldFilePath = file['file_path'] as String;
190-
final newFilePath = oldFilePath.replaceFirst(oldDirPath, newDirPath);
191-
var newRelativePath =
192-
newFilePath.substring(libraryRootPath.length + 1);
193-
// 统一用 / 存储相对路径
194-
if (Platform.isWindows) {
195-
newRelativePath = newRelativePath.replaceAll('\\', '/');
196-
}
227+
final oldRelativePath = file['relative_path'] as String;
228+
final newRelativePath =
229+
oldRelativePath.replaceFirst(oldRelativePrefix, newRelativePrefix);
197230
final parts = newRelativePath.split('/');
198231
final newCategory = parts.isNotEmpty ? parts.first : '';
199232
final newWorkId = _extractWorkIdFromRelativePath(newRelativePath);
200233

201234
batch.update(
202235
'subtitle_files',
203236
{
204-
'file_path': newFilePath,
205237
'relative_path': newRelativePath,
206238
'category': newCategory,
207239
'work_id': newWorkId,
@@ -332,6 +364,15 @@ class SubtitleDatabase {
332364

333365
static final _workIdRegex = RegExp(r'[RrBbVv][Jj]0*(\d+)');
334366

367+
/// 从相对路径提取分类(第一级目录)
368+
static String _extractCategoryFromRelativePath(String relativePath) {
369+
final firstSlash = relativePath.indexOf('/');
370+
if (firstSlash > 0) {
371+
return relativePath.substring(0, firstSlash);
372+
}
373+
return '';
374+
}
375+
335376
/// 从相对路径提取 workId
336377
static int? _extractWorkIdFromRelativePath(String relativePath) {
337378
// 相对路径格式: "已解析/RJ1003058/track.vtt"

lib/src/services/subtitle_library_service.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,6 @@ class SubtitleLibraryService {
344344

345345
records.add(SubtitleFileRecord(
346346
fileName: fileName,
347-
filePath: entity.path,
348347
relativePath: relativePath,
349348
category: category,
350349
workId: workId,
@@ -373,9 +372,18 @@ class SubtitleLibraryService {
373372
/// 同步单个目录到数据库(删除旧记录 + 重新扫描)
374373
static Future<void> _syncDirectoryToDatabase(String directoryPath) async {
375374
final libraryDir = await getSubtitleLibraryDirectory();
375+
final libraryRoot = libraryDir.path;
376+
377+
// 将绝对路径转换为相对路径前缀
378+
final relativePrefix = directoryPath.length > libraryRoot.length
379+
? _toRelativePath(directoryPath.substring(libraryRoot.length + 1))
380+
: '';
376381

377382
// 删除该目录下的旧记录
378-
await SubtitleDatabase.instance.deleteByPathPrefix(directoryPath);
383+
if (relativePrefix.isNotEmpty) {
384+
await SubtitleDatabase.instance
385+
.deleteByRelativePathPrefix(relativePrefix);
386+
}
379387

380388
// 如果目录仍存在,重新扫描并插入
381389
final dir = Directory(directoryPath);
@@ -413,7 +421,6 @@ class SubtitleLibraryService {
413421

414422
records.add(SubtitleFileRecord(
415423
fileName: fileName,
416-
filePath: filePath,
417424
relativePath: relativePath,
418425
category: category,
419426
workId: workId,
@@ -506,7 +513,7 @@ class SubtitleLibraryService {
506513
'_meta': {
507514
'type': 'text',
508515
'title': fileName,
509-
'path': record['file_path'] as String,
516+
'path': '$currentFullPath${Platform.pathSeparator}$fileName',
510517
'size': record['file_size'] as int?,
511518
'modified': record['modified_at'] as String?,
512519
},

0 commit comments

Comments
 (0)