diff --git a/utils/streamRanker.ts b/utils/streamRanker.ts new file mode 100644 index 00000000..e3a3746f --- /dev/null +++ b/utils/streamRanker.ts @@ -0,0 +1,310 @@ +interface StreamRankerStrategy { + rankStream( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any, + isSecondarySubtitle: boolean + ): void; +} + +class SubtitleStreamRanker implements StreamRankerStrategy { + rankStream( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any, + isSecondarySubtitle: boolean + ): void { + if (prevIndex == -1) { + console.debug(`AutoSet Subtitle - No Stream Set`); + if (isSecondarySubtitle) { + trackOptions.DefaultSecondarySubtitleStreamIndex = -1; + } else { + trackOptions.DefaultSubtitleStreamIndex = -1; + } + return; + } + + if (!prevSource.MediaStreams || !mediaStreams) { + console.debug(`AutoSet Subtitle - No MediaStreams`); + return; + } + + this.rank( + prevIndex, + prevSource, + mediaStreams, + trackOptions, + isSecondarySubtitle, + "Subtitle" + ); + } + + private rank( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any, + isSecondarySubtitle: boolean, + streamType: string + ): void { + let bestStreamIndex = null; + let bestStreamScore = 0; + const prevStream = prevSource.MediaStreams[prevIndex]; + + if (!prevStream) { + console.debug(`AutoSet ${streamType} - No prevStream`); + return; + } + + console.debug( + `AutoSet ${streamType} - Previous was ${prevStream.Index} - ${prevStream.DisplayTitle}` + ); + + let prevRelIndex = 0; + for (const stream of prevSource.MediaStreams) { + if (stream.Type != streamType) continue; + + if (stream.Index == prevIndex) break; + + prevRelIndex += 1; + } + + let newRelIndex = 0; + for (const stream of mediaStreams) { + if (stream.Type != 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 ${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 ${streamType} - Using ${bestStreamIndex} score ${bestStreamScore}.` + ); + trackOptions.DefaultSubtitleStreamIndex = bestStreamIndex; + } else { + console.debug( + `AutoSet ${streamType} - Threshold not met. Using default.` + ); + } + } +} + +class AudioStreamRanker implements StreamRankerStrategy { + rankStream( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any + ): void { + if (prevIndex == -1) { + console.debug(`AutoSet Audio - No Stream Set`); + return; + } + + if (!prevSource.MediaStreams || !mediaStreams) { + console.debug(`AutoSet Audio - No MediaStreams`); + return; + } + + this.rank(prevIndex, prevSource, mediaStreams, trackOptions, "Audio"); + } + + private rank( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any, + streamType: string + ): void { + let bestStreamIndex = null; + let bestStreamScore = 0; + const prevStream = prevSource.MediaStreams[prevIndex]; + + if (!prevStream) { + console.debug(`AutoSet ${streamType} - No prevStream`); + return; + } + + console.debug( + `AutoSet ${streamType} - Previous was ${prevStream.Index} - ${prevStream.DisplayTitle}` + ); + + let prevRelIndex = 0; + for (const stream of prevSource.MediaStreams) { + if (stream.Type != streamType) continue; + + if (stream.Index == prevIndex) break; + + prevRelIndex += 1; + } + + let newRelIndex = 0; + for (const stream of mediaStreams) { + if (stream.Type != 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 ${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 ${streamType} - Using ${bestStreamIndex} score ${bestStreamScore}.` + ); + trackOptions.DefaultAudioStreamIndex = bestStreamIndex; + } else { + console.debug( + `AutoSet ${streamType} - Threshold not met. Using default.` + ); + } + } +} + +abstract class StreamRanker { + private strategy: StreamRankerStrategy; + abstract streamType: string; + + constructor(strategy: StreamRankerStrategy) { + this.strategy = strategy; + } + + setStrategy(strategy: StreamRankerStrategy) { + this.strategy = strategy; + } + + rankStream( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any, + streamType: string, + isSecondarySubtitle: boolean + ) { + this.strategy.rankStream( + prevIndex, + prevSource, + mediaStreams, + trackOptions, + isSecondarySubtitle + ); + } + + private rank( + prevIndex: number, + prevSource: any, + mediaStreams: any[], + trackOptions: any + ): void { + 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.` + ); + } + } +}