还在尝试跑通播放状态

This commit is contained in:
2026-08-17 23:00:30 +08:00
parent a6056c7f1a
commit 725116760a
7 changed files with 480 additions and 114 deletions
+84 -4
View File
@@ -1,4 +1,7 @@
// lib/services/audio_service.dart
import 'dart:async'; // ⬅️ 添加这个导入
import 'package:flutter/material.dart';
import 'playback_service.dart';
class Song {
final String id;
@@ -17,24 +20,101 @@ class Song {
class AudioService extends ChangeNotifier {
Song? _currentSong;
bool _isPlaying = false;
Duration _position = Duration.zero;
Duration _duration = Duration.zero;
bool _listening = false;
final List<StreamSubscription> _subscriptions = [];
Song? get currentSong => _currentSong;
bool get isPlaying => _isPlaying;
Duration get position => _position;
Duration get duration => _duration;
void playSong(Song song) {
// ✅ 完整的播放入口
Future<void> playSong(Song song) async {
_currentSong = song;
_isPlaying = true;
_position = Duration.zero;
_duration = Duration.zero;
_startListening();
notifyListeners();
if (song.url == null || song.url!.isEmpty) {
return;
}
// 播放由 PlaybackService 执行,状态由 stream 更新
await PlaybackService().play(song.url!);
}
// ✅ 只调用播放器方法,不手动修改 _isPlaying
void togglePlay() {
_isPlaying = !_isPlaying;
notifyListeners();
if (_currentSong == null) return;
if (_isPlaying) {
PlaybackService().pause();
} else {
PlaybackService().resume();
}
// _isPlaying 由 player.stream.playing 更新
}
void stopPlay() {
_currentSong = null;
_isPlaying = false;
_position = Duration.zero;
_duration = Duration.zero;
_stopListening();
notifyListeners();
}
void seekTo(Duration position) {
PlaybackService().seek(position);
_position = position;
notifyListeners();
}
// ✅ 直接监听 media_kit 的三个独立 Stream
void _startListening() {
if (_listening) return;
_listening = true;
final player = PlaybackService().player;
_subscriptions.add(
player.stream.playing.listen((playing) {
if (_isPlaying != playing) {
_isPlaying = playing;
notifyListeners();
}
}),
);
_subscriptions.add(
player.stream.position.listen((position) {
if (_position != position) {
_position = position;
notifyListeners();
}
}),
);
_subscriptions.add(
player.stream.duration.listen((duration) {
if (_duration != duration) {
_duration = duration;
notifyListeners();
}
}),
);
}
void _stopListening() {
_listening = false;
for (final subscription in _subscriptions) {
subscription.cancel();
}
_subscriptions.clear();
}
}
+21 -30
View File
@@ -1,3 +1,4 @@
// lib/services/playback_service.dart
import 'package:media_kit/media_kit.dart';
class PlaybackService {
@@ -8,34 +9,43 @@ class PlaybackService {
late final Player _player;
bool _initialized = false;
// 获取 Player 实例(供 AudioService 直接监听)
Player get player {
if (!_initialized) init();
return _player;
}
void init() {
if (_initialized) return;
_player = Player();
_initialized = true;
}
Future<void> play(String url) async {
Future<void> play(String url, {Map<String, String>? headers}) async {
if (!_initialized) init();
await _player.open(Media(url));
print('🎵 播放 URL: $url');
print('📋 认证头: ${headers?.keys}');
final media = headers != null && headers.isNotEmpty
? Media(url, httpHeaders: headers)
: Media(url);
await _player.open(media);
await _player.play();
}
void pause() {
if (_initialized) {
_player.pause();
}
if (_initialized) _player.pause();
}
void resume() {
if (_initialized) {
_player.play();
}
if (_initialized) _player.play();
}
void stop() {
if (_initialized) {
_player.stop();
}
if (_initialized) _player.stop();
}
void seek(Duration position) {
if (_initialized) _player.seek(position);
}
void dispose() {
@@ -44,23 +54,4 @@ class PlaybackService {
_initialized = false;
}
}
// ✅ 直接返回 PlayerStream(它就是播放器的状态流)
PlayerStream get stateStream {
if (!_initialized) init();
return _player.stream;
}
// 获取当前播放状态
PlayerState get currentState {
if (!_initialized) {
return PlayerState(
playing: false,
position: Duration.zero,
duration: Duration.zero,
buffering: false,
);
}
return _player.state;
}
}
+34
View File
@@ -26,10 +26,22 @@ class WebDAVService {
Future<void> saveCredentials(
String baseUrl, String username, String password) async {
final prefs = await SharedPreferences.getInstance();
print('💾 [saveCredentials] 开始保存凭据...');
print('💾 [saveCredentials] baseUrl: $baseUrl');
print('💾 [saveCredentials] username: $username');
print('💾 [saveCredentials] password 长度: ${password.length}');
await prefs.setString(_keyBaseUrl, baseUrl);
await prefs.setString(_keyUsername, username);
await prefs.setString(_keyPassword, password);
// 立即读取验证
final verifyUsername = prefs.getString(_keyUsername);
final verifyPassword = prefs.getString(_keyPassword);
print(
'💾 [saveCredentials] 验证读取 - username: $verifyUsername, password 存在: ${verifyPassword != null}');
_baseUrl = baseUrl;
_username = username;
_dio = Dio(BaseOptions(
@@ -74,6 +86,28 @@ class WebDAVService {
_username = null;
}
// ✅ 新增:获取认证头(用于 media_kit 播放)
Future<Map<String, String>> getAuthHeaders() async {
final prefs = await SharedPreferences.getInstance();
final username = prefs.getString(_keyUsername);
final password = prefs.getString(_keyPassword);
print('🔑 [getAuthHeaders] 读取到用户名: $username');
print('🔑 [getAuthHeaders] 密码存在: ${password != null}');
print('🔑 [getAuthHeaders] 密码长度: ${password?.length ?? 0}');
if (username != null && password != null) {
final credentials = '$username:$password';
final encoded = base64Encode(utf8.encode(credentials));
print('🔑 [getAuthHeaders] 生成的 Authorization 头: Basic $encoded');
return {
'Authorization': 'Basic $encoded',
};
}
print('⚠️ [getAuthHeaders] 用户名或密码为 null,返回空 Map');
return {};
}
// 获取音乐文件列表(PROPFIND
Future<List<WebDAVItem>> listDirectory({String path = '/'}) async {
if (_dio == null) throw Exception('WebDAV 未连接');