播放列表持久化设计、播放列表点击显示当前播放歌曲的列表内位置
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user