97 lines
3.4 KiB
Dart
97 lines
3.4 KiB
Dart
import '../media/media_item.dart';
|
|
import '../media/media_server_client.dart';
|
|
import '../media/media_source_info.dart';
|
|
import '../media/media_version.dart';
|
|
import '../models/transcode_quality_preset.dart';
|
|
import 'playback_context.dart';
|
|
import 'playback_initialization_types.dart';
|
|
import 'playback_subtitle_resolver.dart';
|
|
|
|
/// Immutable snapshot of everything that describes the item currently loaded
|
|
/// in the player: the resolver output plus the effective (post-fallback,
|
|
/// post-clamping) source selections.
|
|
///
|
|
/// Built once per resolve and swapped atomically on the screen — failure
|
|
/// before the swap means the previous session (and the state derived from
|
|
/// it) stays untouched, which replaces the old field-by-field
|
|
/// snapshot/rollback bookkeeping.
|
|
class PlaybackSession {
|
|
final PlaybackContext context;
|
|
|
|
/// Effective quality preset — the requested preset, downgraded to
|
|
/// original when the backend reported a transcode fallback.
|
|
final TranscodeQualityPreset qualityPreset;
|
|
|
|
/// Effective media version id, refined from the resolver's clamped
|
|
/// version index when the version list provides one.
|
|
final String? mediaSourceId;
|
|
final PlaybackSubtitleSelection subtitleSelection;
|
|
|
|
const PlaybackSession({
|
|
required this.context,
|
|
required this.qualityPreset,
|
|
this.mediaSourceId,
|
|
this.subtitleSelection = const PlaybackSubtitleSelection.off(),
|
|
});
|
|
|
|
/// Derives the effective selections from a resolved [context]:
|
|
/// quality falls back to original when the backend rejected the requested
|
|
/// preset, and the media source id follows the clamped version index.
|
|
factory PlaybackSession.fromContext(
|
|
PlaybackContext context, {
|
|
required TranscodeQualityPreset requestedQualityPreset,
|
|
String? requestedMediaSourceId,
|
|
PlaybackSubtitleSelection subtitleSelection = const PlaybackSubtitleSelection.off(),
|
|
}) {
|
|
final result = context.result;
|
|
final fellBackToOriginal = result.fallbackReason != null && !requestedQualityPreset.isOriginal;
|
|
return PlaybackSession(
|
|
context: context,
|
|
qualityPreset: fellBackToOriginal ? TranscodeQualityPreset.original : requestedQualityPreset,
|
|
mediaSourceId:
|
|
result.selectedMediaSourceId ??
|
|
mediaSourceIdForIndex(result.availableVersions, result.selectedMediaIndex) ??
|
|
requestedMediaSourceId,
|
|
subtitleSelection: subtitleSelection,
|
|
);
|
|
}
|
|
|
|
PlaybackSession withSubtitleSelection(PlaybackSubtitleSelection selection) {
|
|
return PlaybackSession(
|
|
context: context,
|
|
qualityPreset: qualityPreset,
|
|
mediaSourceId: mediaSourceId,
|
|
subtitleSelection: selection,
|
|
);
|
|
}
|
|
|
|
static String? mediaSourceIdForIndex(List<MediaVersion> versions, int index) {
|
|
if (index < 0 || index >= versions.length) return null;
|
|
return versions[index].id;
|
|
}
|
|
|
|
PlaybackInitializationResult get result => context.result;
|
|
|
|
MediaItem get metadata => context.metadata;
|
|
|
|
MediaServerClient? get reportingClient => context.reportingClient;
|
|
|
|
bool get isTranscoding => result.isTranscoding;
|
|
|
|
bool get isOffline => result.isOffline;
|
|
|
|
String? get playSessionId => result.playSessionId;
|
|
|
|
String? get playMethod => result.playMethod;
|
|
|
|
int? get audioStreamId => result.activeAudioStreamId;
|
|
|
|
int get mediaIndex => result.selectedMediaIndex;
|
|
|
|
List<MediaVersion> get availableVersions => result.availableVersions;
|
|
|
|
MediaSourceInfo? get mediaInfo => result.mediaInfo;
|
|
|
|
Map<String, String>? get streamHeaders => context.streamHeaders;
|
|
}
|