135 lines
3.4 KiB
Dart
135 lines
3.4 KiB
Dart
|
|
import 'package:flutter/foundation.dart';
|
||
|
|
|
||
|
|
import '../../../core/contracts/library.dart';
|
||
|
|
import '../session/playback_target.dart';
|
||
|
|
import 'playlist.dart';
|
||
|
|
|
||
|
|
|
||
|
|
@immutable
|
||
|
|
class CrossServerPlaylistEntry {
|
||
|
|
const CrossServerPlaylistEntry({
|
||
|
|
required this.serverId,
|
||
|
|
required this.serverName,
|
||
|
|
required this.serverUrl,
|
||
|
|
required this.token,
|
||
|
|
required this.userId,
|
||
|
|
required this.itemId,
|
||
|
|
this.label = '',
|
||
|
|
this.preloadedItem,
|
||
|
|
});
|
||
|
|
|
||
|
|
final String serverId;
|
||
|
|
final String serverName;
|
||
|
|
final String serverUrl;
|
||
|
|
final String token;
|
||
|
|
final String userId;
|
||
|
|
final String itemId;
|
||
|
|
final String label;
|
||
|
|
final EmbyRawItem? preloadedItem;
|
||
|
|
|
||
|
|
PlaybackServerIdentity toIdentity() => PlaybackServerIdentity(
|
||
|
|
serverId: serverId,
|
||
|
|
baseUrl: serverUrl,
|
||
|
|
token: token,
|
||
|
|
userId: userId,
|
||
|
|
serverName: serverName,
|
||
|
|
);
|
||
|
|
|
||
|
|
@override
|
||
|
|
bool operator ==(Object other) =>
|
||
|
|
other is CrossServerPlaylistEntry &&
|
||
|
|
other.serverId == serverId &&
|
||
|
|
other.serverName == serverName &&
|
||
|
|
other.serverUrl == serverUrl &&
|
||
|
|
other.token == token &&
|
||
|
|
other.userId == userId &&
|
||
|
|
other.itemId == itemId &&
|
||
|
|
other.label == label &&
|
||
|
|
other.preloadedItem == preloadedItem;
|
||
|
|
|
||
|
|
@override
|
||
|
|
int get hashCode => Object.hash(
|
||
|
|
serverId,
|
||
|
|
serverName,
|
||
|
|
serverUrl,
|
||
|
|
token,
|
||
|
|
userId,
|
||
|
|
itemId,
|
||
|
|
label,
|
||
|
|
preloadedItem,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class CrossServerPlaylist implements Playlist {
|
||
|
|
CrossServerPlaylist({
|
||
|
|
required List<CrossServerPlaylistEntry> sequence,
|
||
|
|
required int initialIndex,
|
||
|
|
bool initialPlayFromStart = false,
|
||
|
|
}) : assert(sequence.isNotEmpty, 'cross-server sequence cannot be empty'),
|
||
|
|
assert(initialIndex >= 0 && initialIndex < sequence.length),
|
||
|
|
_entries = List.unmodifiable(sequence),
|
||
|
|
_currentIndex = ValueNotifier<int>(initialIndex),
|
||
|
|
_current = ValueNotifier<PlaylistItem>(
|
||
|
|
_toItem(sequence[initialIndex], playFromStart: initialPlayFromStart),
|
||
|
|
);
|
||
|
|
|
||
|
|
final List<CrossServerPlaylistEntry> _entries;
|
||
|
|
final ValueNotifier<PlaylistItem> _current;
|
||
|
|
final ValueNotifier<int> _currentIndex;
|
||
|
|
|
||
|
|
bool _disposed = false;
|
||
|
|
|
||
|
|
@override
|
||
|
|
ValueListenable<PlaylistItem> get current => _current;
|
||
|
|
@override
|
||
|
|
ValueListenable<int> get currentIndex => _currentIndex;
|
||
|
|
@override
|
||
|
|
int get length => _entries.length;
|
||
|
|
@override
|
||
|
|
bool get hasNext => _currentIndex.value < _entries.length - 1;
|
||
|
|
@override
|
||
|
|
bool get hasPrev => _currentIndex.value > 0;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<void> next() => goTo(_currentIndex.value + 1);
|
||
|
|
@override
|
||
|
|
Future<void> prev() => goTo(_currentIndex.value - 1);
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<void> goTo(int index) async {
|
||
|
|
if (_disposed) return;
|
||
|
|
if (index < 0 || index >= _entries.length) return;
|
||
|
|
if (index == _currentIndex.value) return;
|
||
|
|
final target = _entries[index];
|
||
|
|
_currentIndex.value = index;
|
||
|
|
_current.value = _toItem(target);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<void> goToItemId(String itemId) async {
|
||
|
|
final idx = _entries.indexWhere((e) => e.itemId == itemId);
|
||
|
|
if (idx < 0) return;
|
||
|
|
await goTo(idx);
|
||
|
|
}
|
||
|
|
|
||
|
|
static PlaylistItem _toItem(
|
||
|
|
CrossServerPlaylistEntry e, {
|
||
|
|
bool playFromStart = false,
|
||
|
|
}) => PlaylistItem(
|
||
|
|
itemId: e.itemId,
|
||
|
|
serverId: e.serverId,
|
||
|
|
serverIdentity: e.toIdentity(),
|
||
|
|
displayLabel: e.label,
|
||
|
|
playFromStart: playFromStart,
|
||
|
|
preloadedItem: e.preloadedItem,
|
||
|
|
);
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
_disposed = true;
|
||
|
|
_current.dispose();
|
||
|
|
_currentIndex.dispose();
|
||
|
|
}
|
||
|
|
}
|