spotube/lib/provider/proxy_playlist/proxy_playlist.dart
Kingkor Roy Tirtho 68374efd3e
feat: LAN connect a.k.a control remote Spotube playback and local output device selection (#1355)
* feat: add connect server support

* feat: add ability discover and connect to same network Spotube(s) and sync queue

* feat(connect): add player controls, shuffle, loop, progress bar and queue support

* feat: make control page adaptive

* feat: add volume control support

* cd: upgrade macos runner version

* chore: upgrade inappwebview version to 6

* feat: customized devices button

* feat: add user icon next to devices button

* feat: add play in remote device support

* feat: show alert when new client connects

* fix: ignore the device itself from broadcast list

* fix: volume control not working

* feat: add ability to select current device's output speaker
2024-04-04 22:22:00 +06:00

101 lines
2.9 KiB
Dart

import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/extensions/track.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart';
class ProxyPlaylist {
final Set<Track> tracks;
final Set<String> collections;
final int? active;
ProxyPlaylist(this.tracks, [this.active, this.collections = const {}]);
factory ProxyPlaylist.fromJson(
Map<String, dynamic> json,
Ref ref,
) {
return ProxyPlaylist(
List.castFrom<dynamic, Map<String, dynamic>>(
json['tracks'] ?? <Map<String, dynamic>>[],
).map((t) => _makeAppropriateTrack(t, ref)).toSet(),
json['active'] as int?,
json['collections'] == null
? {}
: (json['collections'] as List).toSet().cast<String>(),
);
}
factory ProxyPlaylist.fromJsonRaw(Map<String, dynamic> json) => ProxyPlaylist(
json['tracks'] == null
? <Track>{}
: (json['tracks'] as List).map((t) => Track.fromJson(t)).toSet(),
json['active'] as int?,
json['collections'] == null
? {}
: (json['collections'] as List).toSet().cast<String>(),
);
Track? get activeTrack =>
active == null || active == -1 ? null : tracks.elementAtOrNull(active!);
bool get isFetching =>
activeTrack != null &&
activeTrack is! SourcedTrack &&
activeTrack is! LocalTrack;
bool containsCollection(String collection) {
return collections.contains(collection);
}
bool containsTrack(TrackSimple track) {
return tracks.firstWhereOrNull((element) => element.id == track.id) != null;
}
bool containsTracks(Iterable<TrackSimple> tracks) {
if (tracks.isEmpty) return false;
return tracks.every(containsTrack);
}
static Track _makeAppropriateTrack(Map<String, dynamic> track, Ref ref) {
if (track.containsKey("ytUri")) {
return SourcedTrack.fromJson(track, ref: ref);
} else if (track.containsKey("path")) {
return LocalTrack.fromJson(track);
} else {
return Track.fromJson(track);
}
}
/// To make sure proper instance method is used for JSON serialization
/// Otherwise default super.toJson() is used
static Map<String, dynamic> _makeAppropriateTrackJson(Track track) {
return switch (track.runtimeType) {
LocalTrack() => track.toJson(),
SourcedTrack() => track.toJson(),
_ => track.toJson(),
};
}
Map<String, dynamic> toJson() {
return {
'tracks': tracks.map(_makeAppropriateTrackJson).toList(),
'active': active,
'collections': collections.toList(),
};
}
ProxyPlaylist copyWith({
Set<Track>? tracks,
int? active,
Set<String>? collections,
}) {
return ProxyPlaylist(
tracks ?? this.tracks,
active ?? this.active,
collections ?? this.collections,
);
}
}