mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-03-11 20:06:16 +00:00
Revamped transcoding subtitles
This commit is contained in:
@@ -4,7 +4,8 @@ import {
|
||||
BaseItemDto,
|
||||
MediaSourceInfo,
|
||||
} from "@jellyfin/sdk/lib/generated-client";
|
||||
import { Settings } from "../atoms/settings";
|
||||
import { Settings, useSettings } from "../atoms/settings";
|
||||
import { StreamRanker, SubtitleStreamRanker } from "../streamRanker";
|
||||
|
||||
interface PlaySettings {
|
||||
item: BaseItemDto;
|
||||
@@ -14,9 +15,13 @@ interface PlaySettings {
|
||||
subtitleIndex?: number | undefined;
|
||||
}
|
||||
|
||||
// Used getting default values for the next player.
|
||||
export function getDefaultPlaySettings(
|
||||
item: BaseItemDto,
|
||||
settings: Settings
|
||||
settings: Settings,
|
||||
previousIndex?: number,
|
||||
previousItem?: BaseItemDto,
|
||||
previousSource?: MediaSourceInfo
|
||||
): PlaySettings {
|
||||
if (item.Type === "Program") {
|
||||
return {
|
||||
@@ -41,7 +46,22 @@ export function getDefaultPlaySettings(
|
||||
(x) => x.Type === "Audio"
|
||||
)?.Index;
|
||||
|
||||
// TODO: Need to most common next subtitle index as an option.
|
||||
// We prefer the previous track over the default track.
|
||||
let trackOptions = {};
|
||||
const mediaStreams = mediaSource?.MediaStreams ?? [];
|
||||
if (settings?.rememberSubtitleSelections) {
|
||||
if (previousIndex !== undefined && previousSource) {
|
||||
const subtitleRanker = new SubtitleStreamRanker();
|
||||
const ranker = new StreamRanker(subtitleRanker);
|
||||
ranker.rankStream(
|
||||
previousIndex,
|
||||
previousSource,
|
||||
mediaStreams,
|
||||
trackOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const finalSubtitleIndex = mediaSource?.DefaultAudioStreamIndex;
|
||||
|
||||
// 4. Get default bitrate
|
||||
|
||||
@@ -1,248 +1,35 @@
|
||||
interface StreamRankerStrategy {
|
||||
rankStream(
|
||||
prevIndex: number,
|
||||
prevSource: any,
|
||||
mediaStreams: any[],
|
||||
trackOptions: any,
|
||||
isSecondarySubtitle: boolean
|
||||
): void;
|
||||
}
|
||||
import {
|
||||
MediaSourceInfo,
|
||||
MediaStream,
|
||||
} from "@jellyfin/sdk/lib/generated-client";
|
||||
|
||||
class SubtitleStreamRanker implements StreamRankerStrategy {
|
||||
rankStream(
|
||||
abstract class StreamRankerStrategy {
|
||||
abstract streamType: string;
|
||||
|
||||
abstract rankStream(
|
||||
prevIndex: number,
|
||||
prevSource: any,
|
||||
mediaStreams: any[],
|
||||
trackOptions: any,
|
||||
isSecondarySubtitle: boolean
|
||||
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`);
|
||||
if (isSecondarySubtitle) {
|
||||
trackOptions.DefaultSecondarySubtitleStreamIndex = -1;
|
||||
} else {
|
||||
trackOptions.DefaultSubtitleStreamIndex = -1;
|
||||
}
|
||||
trackOptions[`Default${this.streamType}StreamIndex`] = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prevSource.MediaStreams || !mediaStreams) {
|
||||
console.debug(`AutoSet Subtitle - No MediaStreams`);
|
||||
console.debug(`AutoSet ${this.streamType} - 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];
|
||||
@@ -308,3 +95,52 @@ abstract class StreamRanker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
|
||||
Reference in New Issue
Block a user