import { MediaSourceInfo, MediaStream, } from "@jellyfin/sdk/lib/generated-client"; abstract class StreamRankerStrategy { abstract streamType: string; abstract rankStream( prevIndex: number, prevSource: MediaSourceInfo, mediaStreams: MediaStream[], trackOptions: any ): void; protected rank( prevIndex: number, prevSource: MediaSourceInfo, mediaStreams: MediaStream[], trackOptions: any ): void { if (prevIndex == -1) { console.debug(`AutoSet Subtitle - No Stream Set`); trackOptions[`Default${this.streamType}StreamIndex`] = -1; return; } if (!prevSource.MediaStreams || !mediaStreams) { console.debug(`AutoSet ${this.streamType} - No MediaStreams`); return; } let bestStreamIndex = null; let bestStreamScore = 0; const prevStream = prevSource.MediaStreams[prevIndex]; if (!prevStream) { console.debug(`AutoSet ${this.streamType} - No prevStream`); return; } console.debug( `AutoSet ${this.streamType} - Previous was ${prevStream.Index} - ${prevStream.DisplayTitle}` ); let prevRelIndex = 0; for (const stream of prevSource.MediaStreams) { if (stream.Type != this.streamType) continue; if (stream.Index == prevIndex) break; prevRelIndex += 1; } let newRelIndex = 0; for (const stream of mediaStreams) { if (stream.Type != this.streamType) continue; let score = 0; if (prevStream.Codec == stream.Codec) score += 1; if (prevRelIndex == newRelIndex) score += 1; if ( prevStream.DisplayTitle && prevStream.DisplayTitle == stream.DisplayTitle ) score += 2; if ( prevStream.Language && prevStream.Language != "und" && prevStream.Language == stream.Language ) score += 2; console.debug( `AutoSet ${this.streamType} - Score ${score} for ${stream.Index} - ${stream.DisplayTitle}` ); if (score > bestStreamScore && score >= 3) { bestStreamScore = score; bestStreamIndex = stream.Index; } newRelIndex += 1; } if (bestStreamIndex != null) { console.debug( `AutoSet ${this.streamType} - Using ${bestStreamIndex} score ${bestStreamScore}.` ); trackOptions[`Default${this.streamType}StreamIndex`] = bestStreamIndex; } else { console.debug( `AutoSet ${this.streamType} - Threshold not met. Using default.` ); } } } class SubtitleStreamRanker extends StreamRankerStrategy { streamType = "Subtitle"; rankStream( prevIndex: number, prevSource: MediaSourceInfo, mediaStreams: MediaStream[], trackOptions: any ): void { super.rank(prevIndex, prevSource, mediaStreams, trackOptions); } } class AudioStreamRanker extends StreamRankerStrategy { streamType = "Audio"; rankStream( prevIndex: number, prevSource: MediaSourceInfo, mediaStreams: MediaStream[], trackOptions: any ): void { super.rank(prevIndex, prevSource, mediaStreams, trackOptions); } } class StreamRanker { private strategy: StreamRankerStrategy; constructor(strategy: StreamRankerStrategy) { this.strategy = strategy; } setStrategy(strategy: StreamRankerStrategy) { this.strategy = strategy; } rankStream( prevIndex: number, prevSource: MediaSourceInfo, mediaStreams: MediaStream[], trackOptions: any ) { this.strategy.rankStream(prevIndex, prevSource, mediaStreams, trackOptions); } } export { StreamRanker, SubtitleStreamRanker, AudioStreamRanker };