Files
minstrel/flutter_client/lib/api/endpoints/playlists.dart
T
bvandeusen 2499449c0b feat(flutter/player): playNext + enqueue + PlaylistsApi.appendTracks
Three small data-layer additions for the upcoming track-actions menu:

- PlaylistsApi.appendTracks(playlistId, trackIds) wraps
  POST /api/playlists/{id}/tracks for the "Add to playlist" action.
- audio_handler gains playNext (insertAudioSource at currentIndex+1)
  and enqueue (addAudioSource) — both also push the audio_service
  queue notifier so the queue-screen UI stays in sync.
- The AudioSource construction was extracted into a private
  _buildAudioSource helper so setQueueFromTracks / playNext / enqueue
  share one source-building path.
- PlayerActions exposes playNext / enqueue for menu use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:40:17 -04:00

57 lines
1.9 KiB
Dart

import 'package:dio/dio.dart';
import '../../models/playlist.dart';
/// Wire shape for GET /api/playlists. Server splits owned vs. public so
/// the UI can present them as different sections; we preserve that split
/// to give the integrations page room to grow.
class PlaylistsList {
const PlaylistsList({required this.owned, required this.public});
final List<Playlist> owned;
final List<Playlist> public;
factory PlaylistsList.empty() =>
const PlaylistsList(owned: [], public: []);
/// Concatenated view for callers that don't care about the split.
List<Playlist> get all => [...owned, ...public];
}
class PlaylistsApi {
PlaylistsApi(this._dio);
final Dio _dio;
/// GET /api/playlists?kind=user|system|all. Server returns
/// `{"owned": [...], "public": [...]}`. Owned is the caller's own
/// playlists, filtered by the kind param (default "user"). Public
/// is other users' shared playlists; not filtered by kind.
Future<PlaylistsList> list({String kind = 'user'}) async {
final r = await _dio.get<Map<String, dynamic>>(
'/api/playlists',
queryParameters: {'kind': kind},
);
final body = r.data ?? const <String, dynamic>{};
List<Playlist> parse(String key) {
final raw = (body[key] as List?) ?? const [];
return raw
.map((e) => Playlist.fromJson((e as Map).cast<String, dynamic>()))
.toList(growable: false);
}
return PlaylistsList(owned: parse('owned'), public: parse('public'));
}
Future<PlaylistDetail> get(String id) async {
final r = await _dio.get<Map<String, dynamic>>('/api/playlists/$id');
return PlaylistDetail.fromJson(r.data ?? const {});
}
/// POST /api/playlists/{id}/tracks. Owner only; server returns the
/// playlist detail with the new rows.
Future<void> appendTracks(String playlistId, List<String> trackIds) async {
await _dio.post<void>(
'/api/playlists/$playlistId/tracks',
data: {'track_ids': trackIds},
);
}
}