mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
311 lines
7.5 KiB
TypeScript
311 lines
7.5 KiB
TypeScript
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.`
|
|
);
|
|
}
|
|
}
|
|
}
|