Skip to content

Commit

Permalink
Smart font streaming for external ASS subtitles
Browse files Browse the repository at this point in the history
Also refactors the function renderSSaAss to be more parallel and readable and nice...
  • Loading branch information
p0358 committed Oct 25, 2024
1 parent 207318c commit 389702b
Showing 1 changed file with 64 additions and 31 deletions.
95 changes: 64 additions & 31 deletions src/plugins/htmlVideoPlayer/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,24 @@ function getTextTrackUrl(track, item, format) {
return url;
}

function getSubtitlesFontsUrl(track, item) {
if (itemHelper.isLocalItem(item) && track.Path)
return false;

Check failure on line 169 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

Expected { after 'if' condition

if (!track.IsExternal || track.IsExternalUrl)
return false;

Check failure on line 172 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

Expected { after 'if' condition

const apiClient = ServerConnections.getApiClient(serverId);

Check failure on line 174 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

'serverId' is not defined

const replacementRegex = /(.*Videos\/[0-9a-f\-]+\/[0-9a-f]+\/Subtitles\/[0-9]+(?:\/[0-9]+)\/)(Stream\.[^\?]*)(.*)/;

Check failure on line 176 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

Unnecessary escape character: \-

Check failure on line 176 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

Unnecessary escape character: \?
if (!replacementRegex.test(textStream.DeliveryUrl))

Check failure on line 177 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

'textStream' is not defined
return false;

Check failure on line 178 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

Expected { after 'if' condition

return apiClient.getUrl(textStream.DeliveryUrl.replace(replacementRegex, '$1Fonts$3'), {

Check failure on line 180 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

'textStream' is not defined
api_key: apiClient.accessToken()
});
}

function getDefaultProfile() {
return profileBuilder({});
}
Expand Down Expand Up @@ -1258,28 +1276,47 @@ export class HtmlVideoPlayer {
* @private
*/
renderSsaAss(videoElement, track, item) {
const supportedFonts = ['application/vnd.ms-opentype', 'application/x-truetype-font', 'font/otf', 'font/ttf', 'font/woff', 'font/woff2'];
const availableFonts = [];
const workerUrl = `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker.js`;
const legacyWorkerUrl = `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker-legacy.js`;
const supportedFontMimeTypes = ['application/vnd.ms-opentype', 'application/x-truetype-font', 'font/otf', 'font/ttf', 'font/woff', 'font/woff2'];
const fontsToPreload = [];
const attachments = this._currentPlayOptions.mediaSource.MediaAttachments || [];
const apiClient = ServerConnections.getApiClient(item);
attachments.forEach(i => {
// we only require font files and ignore embedded media attachments like covers as there are cases where ffmpeg fails to extract those
if (supportedFonts.includes(i.MimeType)) {
if (supportedFontMimeTypes.includes(i.MimeType)) {
// embedded font url
availableFonts.push(apiClient.getUrl(i.DeliveryUrl));
fontsToPreload.push(apiClient.getUrl(i.DeliveryUrl));
}
});
const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', {
const subtitlesFontListUrl = getSubtitlesFontsUrl(track, item); // will return false if we shouldn't request this
const fallbackFontListUrl = apiClient.getUrl('/FallbackFont/Fonts', {
api_key: apiClient.accessToken()
});
const htmlVideoPlayer = this;
import('@jellyfin/libass-wasm').then(({ default: SubtitlesOctopus }) => {

Promise.all([
import('@jellyfin/libass-wasm'),
apiClient.getNamedConfiguration('encoding'),
// Worker in Tizen 5 doesn't resolve relative path with async request
resolveUrl(workerUrl),
resolveUrl(legacyWorkerUrl),
subtitlesFontListUrl ? apiClient.getJSON(subtitlesFontListUrl) : null,
encodingOptions.EnableFallbackFont ? apiClient.getJSON(fallbackFontListUrl) : null

Check failure on line 1305 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

'encodingOptions' is not defined
]).then(([
{ default: SubtitlesOctopus },
encodingOptions,

Check failure on line 1308 in src/plugins/htmlVideoPlayer/plugin.js

View workflow job for this annotation

GitHub Actions / Quality checks 👌🧪 / Run lint 🕵️‍♂️

'encodingOptions' is defined but never used
workerUrl,
legacyWorkerUrl,
subtitlesFontFiles,
fallbackFontFiles
]) => {
const options = {
video: videoElement,
subUrl: getTextTrackUrl(track, item),
fonts: availableFonts,
workerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker.js`,
legacyWorkerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker-legacy.js`,
fonts: fontsToPreload,
workerUrl: workerUrl,
legacyWorkerUrl: legacyWorkerUrl,
onError() {
// HACK: Clear JavascriptSubtitlesOctopus: it gets disposed when an error occurs
htmlVideoPlayer.#currentAssRenderer = null;
Expand All @@ -1304,29 +1341,25 @@ export class HtmlVideoPlayer {
renderAhead: 90
};

Promise.all([
apiClient.getNamedConfiguration('encoding'),
// Worker in Tizen 5 doesn't resolve relative path with async request
resolveUrl(options.workerUrl),
resolveUrl(options.legacyWorkerUrl)
]).then(([config, workerUrl, legacyWorkerUrl]) => {
options.workerUrl = workerUrl;
options.legacyWorkerUrl = legacyWorkerUrl;

if (config.EnableFallbackFont) {
apiClient.getJSON(fallbackFontList).then((fontFiles = []) => {
fontFiles.forEach(font => {
const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${encodeURIComponent(font.Name)}`, {
api_key: apiClient.accessToken()
});
availableFonts.push(fontUrl);
});
this.#currentAssRenderer = new SubtitlesOctopus(options);
if (subtitlesFontFiles) {
subtitlesFontFiles.forEach(font => {
const fontUrl = apiClient.getUrl(`/Fonts/${encodeURIComponent(font.Key)}`, {
api_key: apiClient.accessToken()
});
} else {
this.#currentAssRenderer = new SubtitlesOctopus(options);
}
});
fontsToPreload.push(fontUrl);
});
}

if (fallbackFontFiles) {
fallbackFontFiles.forEach(font => {
const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${encodeURIComponent(font.Name)}`, {
api_key: apiClient.accessToken()
});
fontsToPreload.push(fontUrl);
});
}

this.#currentAssRenderer = new SubtitlesOctopus(options);
});
}

Expand Down

0 comments on commit 389702b

Please sign in to comment.