diff --git a/web/package.json b/web/package.json index a747da06..3cbb2cc9 100644 --- a/web/package.json +++ b/web/package.json @@ -32,7 +32,7 @@ "foliate-js": "file:vendor/foliate-js", "framer-motion": "^12.38.0", "hls.js": "^1.6.15", - "jassub": "^2.4.2", + "jassub": "^2.5.6", "libpgs": "^0.8.1", "lucide-react": "^0.576.0", "node-unrar-js": "^2.0.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 4472a1d6..65de0463 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -68,8 +68,8 @@ importers: specifier: ^1.6.15 version: 1.6.15 jassub: - specifier: ^2.4.2 - version: 2.4.2 + specifier: ^2.5.6 + version: 2.5.6 libpgs: specifier: ^0.8.1 version: 0.8.1 @@ -1805,8 +1805,8 @@ packages: resolution: {integrity: sha512-RQ4h9F6DOiHxpdocUDrOl6xBM+yOtz+LkUol47AVWcfebGBDpZ7w7Xvz9PS24JgXvLGiXXzSAfdCdVy1tPlaFA==} engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=18.0.0'} - abslink@1.1.6: - resolution: {integrity: sha512-8fQgnUoVSgc1IhOrYzdDY+wTDPktbuYjds2LKf9kWYWKwDnHgXU168gdV3sPei7vevFf5m2fPZ6IQSKZkSjHVg==} + abslink@1.2.2: + resolution: {integrity: sha512-HCOItvzORPjMzddCmEpbH8+W1VM3yjH+CR7DLtxlxFKX/jz5twJbm7KLfblO7eKLnXINLah8Hk2ZAJqxXeSkOg==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -2211,8 +2211,8 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jassub@2.4.2: - resolution: {integrity: sha512-qWECKZWADUYAlXrzTEKPw+zY06XNy4tuZhTTWyk/ML+axRSMuOcbI0tAxL6tdXg+wdROd+EZU83SQFh/frugCg==} + jassub@2.5.6: + resolution: {integrity: sha512-jsbEPVR+JvcOcGa8PbyVvHmFol+rQtAfty7JDp1WWrS3GEfThitjI5sXT0VLfG6/BLuyIuv1+2qykWdWyLkpQw==} jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} @@ -4590,7 +4590,7 @@ snapshots: '@zip.js/zip.js@2.8.26': {} - abslink@1.1.6: {} + abslink@1.2.2: {} acorn-jsx@5.3.2(acorn@8.16.0): dependencies: @@ -4974,9 +4974,9 @@ snapshots: isexe@2.0.0: {} - jassub@2.4.2: + jassub@2.5.6: dependencies: - abslink: 1.1.6 + abslink: 1.2.2 lfa-ponyfill: 1.1.0 rvfc-polyfill: 1.0.8 throughput: 1.0.2 diff --git a/web/src/player/assets/liberation-sans.LICENSE b/web/src/player/assets/liberation-sans.LICENSE new file mode 100644 index 00000000..aba73e8a --- /dev/null +++ b/web/src/player/assets/liberation-sans.LICENSE @@ -0,0 +1,102 @@ +Digitized data copyright (c) 2010 Google Corporation + with Reserved Font Arimo, Tinos and Cousine. +Copyright (c) 2012 Red Hat, Inc. + with Reserved Font Name Liberation. + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + +PREAMBLE The goals of the Open Font License (OFL) are to stimulate +worldwide development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to provide +a free and open framework in which fonts may be shared and improved in +partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. +The fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + + + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. +This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components +as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting ? in part or in whole ? +any of the components of the Original Version, by changing formats or +by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer +or other person who contributed to the Font Software. + + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components,in + Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, + redistributed and/or sold with any software, provided that each copy + contains the above copyright notice and this license. These can be + included either as stand-alone text files, human-readable headers or + in the appropriate machine-readable metadata fields within text or + binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font + Name(s) unless explicit written permission is granted by the + corresponding Copyright Holder. This restriction only applies to the + primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + Software shall not be used to promote, endorse or advertise any + Modified Version, except to acknowledge the contribution(s) of the + Copyright Holder(s) and the Author(s) or with their explicit written + permission. + +5) The Font Software, modified or unmodified, in part or in whole, must + be distributed entirely under this license, and must not be distributed + under any other license. The requirement for fonts to remain under + this license does not apply to any document created using the Font + Software. + + + +TERMINATION +This license becomes null and void if any of the above conditions are not met. + + + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. + diff --git a/web/src/player/assets/liberation-sans.woff2 b/web/src/player/assets/liberation-sans.woff2 new file mode 100644 index 00000000..a5623916 Binary files /dev/null and b/web/src/player/assets/liberation-sans.woff2 differ diff --git a/web/src/player/hooks/useASSSubtitles.test.tsx b/web/src/player/hooks/useASSSubtitles.test.tsx index 12afc0fd..fc516259 100644 --- a/web/src/player/hooks/useASSSubtitles.test.tsx +++ b/web/src/player/hooks/useASSSubtitles.test.tsx @@ -130,7 +130,7 @@ describe("useASSSubtitles font fallback", () => { expect(opts.subContent).not.toContain("Trebuchet MS"); }); - it("keeps JASSUB's built-in default for a Latin (German) ASS track", async () => { + it("keeps the Liberation Sans default for a Latin (German) ASS track", async () => { renderHook(() => useASSSubtitles(makeVideoRef(), [germanTrack], 6, false, 0, 0)); await waitFor(() => expect(constructorOpts).toHaveLength(1)); @@ -138,6 +138,10 @@ describe("useASSSubtitles font fallback", () => { const opts = constructorOpts[0]!; expect(opts.defaultFont).toBeUndefined(); expect(opts.fonts).toBeUndefined(); + // jassub >= 2.5.4 no longer ships its built-in default font file, so the + // hook must always supply Liberation Sans itself or Latin tracks render + // nothing (queryFonts is disabled). + expect(opts.availableFonts).toEqual({ "liberation sans": expect.any(String) }); }); it("passes fetched ASS content into JASSUB", async () => { diff --git a/web/src/player/hooks/useASSSubtitles.ts b/web/src/player/hooks/useASSSubtitles.ts index 3efa129d..21a91035 100644 --- a/web/src/player/hooks/useASSSubtitles.ts +++ b/web/src/player/hooks/useASSSubtitles.ts @@ -8,6 +8,13 @@ import { loadSubtitleFontBundle, loadSubtitleFallbackFontData, } from "../utils/subtitleFonts"; +// Liberation Sans (SIL OFL 1.1; license colocated as liberation-sans.LICENSE), +// the font JASSUB uses as its built-in Latin default, taken verbatim from +// jassub@2.4.2's dist/default.woff2. Vendored because jassub >= 2.5.4 still +// references that file but no longer ships it in the npm package, which would +// leave libass with no usable default font (queryFonts is disabled) and +// silently render nothing. +import liberationSansUrl from "../assets/liberation-sans.woff2?url"; /** * Manages client-side ASS/SSA subtitle rendering via JASSUB (libass WASM). @@ -149,6 +156,7 @@ export function useASSSubtitles( // Letting JASSUB probe it produces noisy console warnings for common ASS // style fonts without making playback reliable across clients. queryFonts: false, + availableFonts: { "liberation sans": liberationSansUrl }, ...(fonts.length > 0 ? { fonts, diff --git a/web/vite.config.ts b/web/vite.config.ts index 97ae316c..2ea428b3 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -15,6 +15,14 @@ export default defineConfig(({ mode }) => { worker: { format: "es", }, + optimizeDeps: { + // jassub spawns its own module worker with import.meta.url paths; the + // dep optimizer rewrites those into .vite/deps where the worker file + // doesn't exist, so the ASS renderer never initializes in dev. + exclude: ["jassub"], + // CJS deps of the excluded package still need prebundling for ESM interop. + include: ["jassub > throughput", "jassub > rvfc-polyfill"], + }, resolve: { alias: { "@": path.resolve(__dirname, "./src"),