还在尝试跑通播放状态
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 未连接');
|
||||
|
||||
Reference in New Issue
Block a user