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 26, 2024
1 parent 207318c commit 1c7fadc
Showing 1 changed file with 69 additions and 31 deletions.
100 changes: 69 additions & 31 deletions src/plugins/htmlVideoPlayer/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,27 @@ function getTextTrackUrl(track, item, format) {
return url;
}

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

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

const apiClient = ServerConnections.getApiClient(item.ServerId);

const replacementRegex = /(.*Videos\/[0-9a-f-]+\/[0-9a-f]+\/Subtitles\/[0-9]+(?:\/[0-9]+)\/)(Stream\.[^?]*)(.*)/;
if (!replacementRegex.test(track.DeliveryUrl)) {
return false;
}

return apiClient.getUrl(track.DeliveryUrl.replace(replacementRegex, '$1Fonts$3'), {
api_key: apiClient.accessToken()
});
}

function getDefaultProfile() {
return profileBuilder({});
}
Expand Down Expand Up @@ -1258,28 +1279,49 @@ 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 = [];

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

View workflow job for this annotation

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

Either use this collection's contents or remove the collection
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 }) => {

const fallbackFontListPromise = apiClient.getNamedConfiguration('encoding').then(encodingOptions => {
return encodingOptions.EnableFallbackFont ? apiClient.getJSON(fallbackFontListUrl) : null;
});

Promise.all([
import('@jellyfin/libass-wasm'),
// Worker in Tizen 5 doesn't resolve relative path with async request
resolveUrl(workerUrl),
resolveUrl(legacyWorkerUrl),
subtitlesFontListUrl ? apiClient.getJSON(subtitlesFontListUrl) : null,
fallbackFontListPromise
]).then(([
{ default: SubtitlesOctopus },
workerUrlResolved,
legacyWorkerUrlResolved,

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

View workflow job for this annotation

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

'legacyWorkerUrlResolved' is defined but never used
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: fontsToPreloadResolved,

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

View workflow job for this annotation

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

'fontsToPreloadResolved' is not defined
workerUrl: workerUrlResolved,
legacyWorkerUrl: legacyWorkerUrl,
onError() {
// HACK: Clear JavascriptSubtitlesOctopus: it gets disposed when an error occurs
htmlVideoPlayer.#currentAssRenderer = null;
Expand All @@ -1304,29 +1346,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 1c7fadc

Please sign in to comment.