播放列表持久化设计、播放列表点击显示当前播放歌曲的列表内位置

This commit is contained in:
2026-08-28 22:16:55 +08:00
parent f91846be9a
commit fa396ce681
4 changed files with 424 additions and 54 deletions
+70 -24
View File
@@ -1,8 +1,10 @@
// lib/database/song_database.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import '../services/audio_service.dart'; // 用于 PlayMode 枚举,但为了解耦我们传字符串
class SongDatabase {
static final SongDatabase _instance = SongDatabase._internal();
@@ -11,7 +13,6 @@ class SongDatabase {
static Database? _database;
// ⭐ 开启/关闭日志(开发阶段开启)
static bool enableLog = true;
Future<Database> get database async {
@@ -26,19 +27,14 @@ class SongDatabase {
_log('📂 数据库路径: $path');
return await openDatabase(
path,
version: 3,
version: 4, // 升级版本号
onCreate: _onCreate,
onUpgrade: _onUpgrade,
);
}
// ════════════════════════════════════════════════════════════
// 日志工具
// ════════════════════════════════════════════════════════════
void _log(String message) {
if (enableLog) {
debugPrint('📦 [DB] $message');
}
if (enableLog) debugPrint('📦 [DB] $message');
}
void _logQuery(String table, String operation, {Map<String, dynamic>? args}) {
@@ -48,9 +44,6 @@ class SongDatabase {
}
}
// ════════════════════════════════════════════════════════════
// 辅助方法
// ════════════════════════════════════════════════════════════
Future<bool> _tableExists(Database db, String tableName) async {
final result = await db.query(
'sqlite_master',
@@ -66,9 +59,6 @@ class SongDatabase {
return result.any((col) => col['name'] == columnName);
}
// ════════════════════════════════════════════════════════════
// 创建表
// ════════════════════════════════════════════════════════════
Future<void> _onCreate(Database db, int version) async {
_log('🆕 创建数据库 (version $version)');
await _createSongsTable(db);
@@ -76,6 +66,7 @@ class SongDatabase {
await _createPlaylistsTable(db);
await _createPlaylistSongsTable(db);
await _createFavoritesTable(db);
await _createPlaybackStateTable(db);
await _createIndexes(db);
_log('✅ 数据库创建完成');
}
@@ -155,6 +146,21 @@ class SongDatabase {
_log('📋 表创建: favorites');
}
Future<void> _createPlaybackStateTable(Database db) async {
await db.execute('''
CREATE TABLE playback_state (
id INTEGER PRIMARY KEY,
queue_json TEXT,
current_index INTEGER,
play_mode TEXT DEFAULT 'sequential',
current_playlist_id TEXT,
position_ms INTEGER DEFAULT 0,
updated_at INTEGER
)
''');
_log('📋 表创建: playback_state');
}
Future<void> _createIndexes(Database db) async {
await db.execute(
'CREATE INDEX IF NOT EXISTS idx_songs_artist ON songs(artist)');
@@ -170,7 +176,7 @@ class SongDatabase {
}
// ════════════════════════════════════════════════════════════
// 升级逻辑
// 升级逻辑(版本 3 → 4
// ════════════════════════════════════════════════════════════
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
_log('⬆️ 数据库升级: $oldVersion$newVersion');
@@ -180,7 +186,6 @@ class SongDatabase {
await db.execute('ALTER TABLE songs ADD COLUMN content_hash TEXT');
_log('🔧 添加列: songs.content_hash');
}
final playlistsExists = await _tableExists(db, 'playlists');
if (!playlistsExists) {
await _createPlaylistsTable(db);
@@ -195,13 +200,11 @@ class SongDatabase {
}
await _createIndexes(db);
}
if (oldVersion < 3) {
if (!await _columnExists(db, 'songs', 'content_hash')) {
await db.execute('ALTER TABLE songs ADD COLUMN content_hash TEXT');
_log('🔧 添加列: songs.content_hash');
}
final playlistsExists = await _tableExists(db, 'playlists');
if (!playlistsExists) {
await _createPlaylistsTable(db);
@@ -216,12 +219,18 @@ class SongDatabase {
}
await _createIndexes(db);
}
// ⭐ 升级到版本 4:添加 playback_state 表
if (oldVersion < 4) {
final exists = await _tableExists(db, 'playback_state');
if (!exists) {
await _createPlaybackStateTable(db);
}
}
_log('✅ 数据库升级完成');
}
// ════════════════════════════════════════════════════════════
// 查询方法(带日志
// 查询方法(原有
// ════════════════════════════════════════════════════════════
Future<Map<String, dynamic>?> getSong(String songKey) async {
_logQuery('songs', 'get', args: {'song_key': songKey});
@@ -261,7 +270,7 @@ class SongDatabase {
}
// ════════════════════════════════════════════════════════════
// 播放列表 CRUD带日志
// 播放列表 CRUD原有
// ════════════════════════════════════════════════════════════
Future<List<Map<String, dynamic>>> getAllPlaylists() async {
_logQuery('playlists', 'getAll');
@@ -298,7 +307,7 @@ class SongDatabase {
}
// ════════════════════════════════════════════════════════════
// 播放列表歌曲(带日志
// 播放列表歌曲(原有
// ════════════════════════════════════════════════════════════
Future<List<Map<String, dynamic>>> getPlaylistSongs(String playlistId) async {
_logQuery('playlist_songs', 'get', args: {'playlist_id': playlistId});
@@ -359,7 +368,7 @@ class SongDatabase {
}
// ════════════════════════════════════════════════════════════
// 收藏(带日志
// 收藏(原有
// ════════════════════════════════════════════════════════════
Future<List<Map<String, dynamic>>> getFavorites() async {
_logQuery('favorites', 'getAll');
@@ -403,7 +412,7 @@ class SongDatabase {
}
// ════════════════════════════════════════════════════════════
// 原有方法(带日志
// 原有方法(歌曲 CRUD
// ════════════════════════════════════════════════════════════
Future<void> insertSong(Map<String, dynamic> song) async {
_logQuery('songs', 'insert', args: {'title': song['title']});
@@ -449,6 +458,43 @@ class SongDatabase {
return result.isNotEmpty ? result.first : null;
}
// ════════════════════════════════════════════════════════════
// ⭐ 新增:播放状态持久化
// ════════════════════════════════════════════════════════════
/// 保存播放状态
Future<void> savePlaybackState({
required List<Map<String, dynamic>> queueJson, // 预先序列化的列表
required int currentIndex,
required String playMode,
String? currentPlaylistId,
required int positionMs,
}) async {
final db = await database;
await db.insert(
'playback_state',
{
'id': 1,
'queue_json': jsonEncode(queueJson),
'current_index': currentIndex,
'play_mode': playMode,
'current_playlist_id': currentPlaylistId,
'position_ms': positionMs,
'updated_at': DateTime.now().millisecondsSinceEpoch,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
_log('💾 播放状态已保存');
}
/// 获取播放状态
Future<Map<String, dynamic>?> getPlaybackState() async {
final db = await database;
final result =
await db.query('playback_state', where: 'id = ?', whereArgs: [1]);
return result.isNotEmpty ? result.first : null;
}
Future<void> close() async {
_log('🔒 关闭数据库连接');
final db = await database;