133 lines
3.6 KiB
Dart
133 lines
3.6 KiB
Dart
// lib/database/song_database.dart
|
|
import 'package:path/path.dart';
|
|
import 'package:sqflite/sqflite.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
class SongDatabase {
|
|
static final SongDatabase _instance = SongDatabase._internal();
|
|
factory SongDatabase() => _instance;
|
|
SongDatabase._internal();
|
|
|
|
static Database? _database;
|
|
|
|
Future<Database> get database async {
|
|
if (_database != null) return _database!;
|
|
_database = await _initDatabase();
|
|
return _database!;
|
|
}
|
|
|
|
Future<Database> _initDatabase() async {
|
|
final dir = await getApplicationDocumentsDirectory();
|
|
final path = join(dir.path, 'qingting_songs.db');
|
|
return await openDatabase(
|
|
path,
|
|
version: 1,
|
|
onCreate: _onCreate,
|
|
);
|
|
}
|
|
|
|
Future<void> _onCreate(Database db, int version) async {
|
|
await db.execute('''
|
|
CREATE TABLE songs (
|
|
song_key TEXT PRIMARY KEY,
|
|
remote_path TEXT,
|
|
file_size INTEGER,
|
|
modified_time INTEGER,
|
|
etag TEXT,
|
|
title TEXT,
|
|
artist TEXT,
|
|
album TEXT,
|
|
genre TEXT,
|
|
artwork_path TEXT,
|
|
confidence REAL DEFAULT 0.0,
|
|
validation_count INTEGER DEFAULT 0,
|
|
first_scan INTEGER,
|
|
last_scan INTEGER,
|
|
metadata_status TEXT DEFAULT 'pending'
|
|
)
|
|
''');
|
|
|
|
await db.execute('''
|
|
CREATE TABLE metadata_cache (
|
|
song_key TEXT PRIMARY KEY,
|
|
cache_path TEXT,
|
|
cache_created_at INTEGER,
|
|
cache_size INTEGER,
|
|
FOREIGN KEY (song_key) REFERENCES songs(song_key) ON DELETE CASCADE
|
|
)
|
|
''');
|
|
|
|
await db.execute('CREATE INDEX idx_songs_artist ON songs(artist)');
|
|
await db.execute('CREATE INDEX idx_songs_title ON songs(title)');
|
|
}
|
|
|
|
// ---- 查询 ----
|
|
Future<Map<String, dynamic>?> getSong(String songKey) async {
|
|
final db = await database;
|
|
final result =
|
|
await db.query('songs', where: 'song_key = ?', whereArgs: [songKey]);
|
|
return result.isNotEmpty ? result.first : null;
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>> getSongsByArtist(String artist,
|
|
{int limit = 20}) async {
|
|
if (artist.isEmpty) return [];
|
|
final db = await database;
|
|
return await db.query(
|
|
'songs',
|
|
where: 'artist = ?',
|
|
whereArgs: [artist],
|
|
limit: limit,
|
|
);
|
|
}
|
|
|
|
// ---- 写入 ----
|
|
Future<void> insertSong(Map<String, dynamic> song) async {
|
|
final db = await database;
|
|
await db.insert('songs', song,
|
|
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
}
|
|
|
|
Future<void> updateSong(String songKey, Map<String, dynamic> updates) async {
|
|
final db = await database;
|
|
await db.update(
|
|
'songs',
|
|
updates,
|
|
where: 'song_key = ?',
|
|
whereArgs: [songKey],
|
|
);
|
|
}
|
|
|
|
// ---- 删除 ----
|
|
Future<void> deleteSong(String songKey) async {
|
|
final db = await database;
|
|
await db.delete('songs', where: 'song_key = ?', whereArgs: [songKey]);
|
|
}
|
|
|
|
Future<void> deleteCache(String songKey) async {
|
|
final db = await database;
|
|
await db
|
|
.delete('metadata_cache', where: 'song_key = ?', whereArgs: [songKey]);
|
|
}
|
|
|
|
// ---- 缓存 ----
|
|
Future<void> insertCache(Map<String, dynamic> cache) async {
|
|
final db = await database;
|
|
await db.insert('metadata_cache', cache,
|
|
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
}
|
|
|
|
Future<Map<String, dynamic>?> getCache(String songKey) async {
|
|
final db = await database;
|
|
final result = await db
|
|
.query('metadata_cache', where: 'song_key = ?', whereArgs: [songKey]);
|
|
return result.isNotEmpty ? result.first : null;
|
|
}
|
|
|
|
Future<void> close() async {
|
|
final db = await database;
|
|
await db.close();
|
|
_database = null;
|
|
}
|
|
}
|