├── .dockerignore
├── .editorconfig
├── .github
├── renovate.json
└── workflows
│ ├── emscripten.yml
│ └── release.yml
├── .gitignore
├── .gitmodules
├── Dockerfile
├── LICENSE
├── Makefile
├── Makefile_licence
├── README.md
├── assets
├── default.woff2
└── fonts.conf
├── build
├── WebIDL.py
├── license_defaults
├── license_extract.sh
├── license_fullnotice
├── license_lint.awk
├── patches
│ ├── brotli
│ │ ├── .gitkeep
│ │ ├── 0001-fix-brotli-js-for-webworkers.patch
│ │ └── 0002-upstream_Enable-install-with-emscripten.patch
│ ├── expat
│ │ └── .gitkeep
│ ├── fontconfig
│ │ ├── .gitkeep
│ │ └── 0001-disable-pthreads.patch
│ ├── freetype
│ │ ├── .gitkeep
│ │ └── 0002-disable-pthread.patch
│ ├── fribidi
│ │ └── .gitkeep
│ ├── harfbuzz
│ │ ├── .gitkeep
│ │ └── 0001-force_disable_pthread.patch
│ └── webidl_binder
│ │ ├── 0001-WebIDL-Add-headers-and-make-it-work-under-SubtitlesO.patch
│ │ ├── 0002-WebIDL-Implement-Integer-Pointer-type-IntPtr.patch
│ │ ├── 0003-WebIDL-Implement-ByteString-type.patch
│ │ └── 0004-WebIDL-Implement-Owner-Extended-Attribute-fix-77.patch
├── tempfiles.py
└── webidl_binder.py
├── functions.mk
├── package-lock.json
├── package.json
├── run-buildah-build.sh
├── run-common.sh
├── run-docker-build.sh
└── src
├── Makefile
├── SubtitleOctopus.cpp
├── SubtitleOctopus.idl
├── libass.cpp
├── polyfill.js
├── post-worker.js
├── pre-worker.js
└── subtitles-octopus.js
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Ignore everything for build image step, /code is passed via a volume
2 | *
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.{yml,yaml}]
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [{Makefile, *.mk}]
17 | indent_style = tab
18 | indent_size = 4
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "github>jellyfin/.github//renovate-presets/default",
5 | ":semanticCommitsDisabled",
6 | ":dependencyDashboard"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.github/workflows/emscripten.yml:
--------------------------------------------------------------------------------
1 | name: Emscripten
2 |
3 | on:
4 | push:
5 | branches: [master, ci]
6 | pull_request:
7 | workflow_dispatch:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Cancel previous runs
14 | uses: styfle/cancel-workflow-action@0.12.1
15 | with:
16 | access_token: ${{ github.token }}
17 | all_but_latest: true
18 |
19 | - name: Checkout Base Repo
20 | uses: actions/checkout@v4.1.1
21 |
22 | - name: Checkout Submodules
23 | run: |
24 | git submodule sync
25 | git submodule update --init --recursive --force
26 |
27 | - name: Build Docker Image
28 | run: |
29 | docker build -t libass/jso .
30 |
31 | - name: Build Binaries
32 | run: |
33 | docker run --rm --env LC_ALL=C.UTF-8 -v "${PWD}":/code libass/jso:latest
34 |
35 | - name: Upload Nightly Build
36 | uses: actions/upload-artifact@v4.3.1
37 | if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master'
38 | with:
39 | name: js
40 | path: dist/js
41 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Build & Publish to NPM
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | jobs:
9 | build:
10 | name: Build & Publish to NPM
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4.1.1
16 |
17 | - name: Checkout submodules
18 | run: |
19 | git submodule sync
20 | git submodule update --init --recursive --force
21 |
22 | - name: Build Docker Image
23 | run: |
24 | docker build -t libass/jso .
25 |
26 | - name: Build Binaries
27 | run: |
28 | docker run --rm --env LC_ALL=C.UTF-8 -v "${PWD}":/code libass/jso:latest
29 |
30 | - name: Setup node environment for NPM
31 | uses: actions/setup-node@v4.0.2
32 | with:
33 | node-version: 20
34 | registry-url: 'https://registry.npmjs.org'
35 | check-latest: true
36 |
37 | - name: Publish to NPM
38 | run: SKIP_PREPARE=1 npm publish --access public
39 | env:
40 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | a.out.js
2 | a.out.wasm
3 | a.out.js.mem
4 | a.out
5 | a.wasm
6 | src/WebIDLGrammar.pkl
7 | src/parser.out
8 | lib/expat/xmlwf/xmlwf
9 | dist
10 | .libs/
11 | *.bc
12 | *.lo
13 | *.la
14 | *.o
15 | *.pyc
16 | *.tgz
17 | venv/
18 | node_modules/
19 | build/lib
20 | src/SubOctpInterface.cpp
21 | src/SubOctpInterface.js
22 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/libass"]
2 | path = lib/libass
3 | url = https://github.com/libass/libass.git
4 | ignore = dirty
5 | [submodule "lib/fribidi"]
6 | path = lib/fribidi
7 | url = https://github.com/fribidi/fribidi.git
8 | ignore = dirty
9 | [submodule "lib/fontconfig"]
10 | path = lib/fontconfig
11 | url = https://gitlab.freedesktop.org/fontconfig/fontconfig.git
12 | ignore = dirty
13 | [submodule "lib/freetype"]
14 | path = lib/freetype
15 | url = https://gitlab.freedesktop.org/freetype/freetype.git
16 | ignore = dirty
17 | [submodule "lib/expat"]
18 | path = lib/expat
19 | url = https://github.com/libexpat/libexpat.git
20 | ignore = dirty
21 | [submodule "lib/harfbuzz"]
22 | path = lib/harfbuzz
23 | url = https://github.com/harfbuzz/harfbuzz.git
24 | ignore = dirty
25 | [submodule "lib/brotli"]
26 | path = lib/brotli
27 | url = https://github.com/google/brotli.git
28 | ignore = dirty
29 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/emscripten/emsdk:2.0.34
2 |
3 | RUN apt-get update && apt-get install -y --no-install-recommends \
4 | build-essential \
5 | cmake \
6 | git \
7 | ragel \
8 | patch \
9 | libtool \
10 | itstool \
11 | pkg-config \
12 | python3 \
13 | python3-ply \
14 | gettext \
15 | autopoint \
16 | automake \
17 | autoconf \
18 | m4 \
19 | gperf \
20 | licensecheck \
21 | gawk \
22 | && rm -rf /var/lib/apt/lists/*
23 |
24 | WORKDIR /code
25 | CMD ["make"]
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-2021 JavascriptSubtitlesOctopus contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # SubtitleOctopus.js - Makefile
2 |
3 | # make - Build Dependencies and the SubtitleOctopus.js
4 | BASE_DIR:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
5 | DIST_DIR:=$(BASE_DIR)dist/libraries
6 |
7 | export LDFLAGS = -O3 -s EVAL_CTORS=1 -flto -s ENVIRONMENT=web,webview,worker -s NO_EXIT_RUNTIME=1
8 | export CFLAGS = -O3 -flto -s USE_PTHREADS=0
9 | export CXXFLAGS = $(CFLAGS)
10 | export PKG_CONFIG_PATH = $(DIST_DIR)/lib/pkgconfig
11 | export EM_PKG_CONFIG_PATH = $(PKG_CONFIG_PATH)
12 |
13 | all: subtitleoctopus
14 | subtitleoctopus: dist
15 |
16 | .PHONY: all subtitleoctopus dist
17 |
18 | include functions.mk
19 |
20 | # FriBidi
21 | build/lib/fribidi/configure: lib/fribidi $(wildcard $(BASE_DIR)build/patches/fribidi/*.patch)
22 | $(call PREPARE_SRC_PATCHED,fribidi)
23 | cd build/lib/fribidi && $(RECONF_AUTO)
24 |
25 | $(DIST_DIR)/lib/libfribidi.a: build/lib/fribidi/configure
26 | cd build/lib/fribidi && \
27 | $(call CONFIGURE_AUTO) --disable-debug && \
28 | $(JSO_MAKE) -C lib/ fribidi-unicode-version.h && \
29 | $(JSO_MAKE) -C lib/ install && \
30 | $(JSO_MAKE) install-pkgconfigDATA
31 |
32 | # Expat
33 | build/lib/expat/configured: lib/expat
34 | $(call PREPARE_SRC_VPATH,expat)
35 | touch build/lib/expat/configured
36 |
37 | $(DIST_DIR)/lib/libexpat.a: build/lib/expat/configured
38 | cd build/lib/expat && \
39 | $(call CONFIGURE_CMAKE,$(BASE_DIR)lib/expat/expat) \
40 | -DEXPAT_BUILD_DOCS=off \
41 | -DEXPAT_SHARED_LIBS=off \
42 | -DEXPAT_BUILD_EXAMPLES=off \
43 | -DEXPAT_BUILD_FUZZERS=off \
44 | -DEXPAT_BUILD_TESTS=off \
45 | -DEXPAT_BUILD_TOOLS=off \
46 | && \
47 | $(JSO_MAKE) install
48 |
49 | # Brotli
50 | build/lib/brotli/js/decode.js: build/lib/brotli/configured
51 | build/lib/brotli/js/polyfill.js: build/lib/brotli/configured
52 | build/lib/brotli/configured: lib/brotli $(wildcard $(BASE_DIR)build/patches/brotli/*.patch)
53 | $(call PREPARE_SRC_PATCHED,brotli)
54 | touch build/lib/brotli/configured
55 |
56 | $(DIST_DIR)/lib/libbrotlidec.a: $(DIST_DIR)/lib/libbrotlicommon.a
57 | $(DIST_DIR)/lib/libbrotlicommon.a: build/lib/brotli/configured
58 | cd build/lib/brotli && \
59 | $(call CONFIGURE_CMAKE) && \
60 | $(JSO_MAKE) install
61 | # Normalise static lib names
62 | cd $(DIST_DIR)/lib/ && \
63 | for lib in *-static.a ; do mv "$$lib" "$${lib%-static.a}.a" ; done
64 |
65 |
66 | # Freetype without Harfbuzz
67 | build/lib/freetype/configure: lib/freetype $(wildcard $(BASE_DIR)build/patches/freetype/*.patch)
68 | $(call PREPARE_SRC_PATCHED,freetype)
69 | cd build/lib/freetype && $(RECONF_AUTO)
70 |
71 | build/lib/freetype/build_hb/dist_hb/lib/libfreetype.a: $(DIST_DIR)/lib/libbrotlidec.a build/lib/freetype/configure
72 | cd build/lib/freetype && \
73 | mkdir -p build_hb && \
74 | cd build_hb && \
75 | $(call CONFIGURE_AUTO,..) \
76 | --prefix="$$(pwd)/dist_hb" \
77 | --with-brotli=yes \
78 | --without-harfbuzz \
79 | && \
80 | $(JSO_MAKE) install
81 |
82 | # Harfbuzz
83 | build/lib/harfbuzz/configure: lib/harfbuzz $(wildcard $(BASE_DIR)build/patches/harfbuzz/*.patch)
84 | $(call PREPARE_SRC_PATCHED,harfbuzz)
85 | cd build/lib/harfbuzz && $(RECONF_AUTO)
86 |
87 | $(DIST_DIR)/lib/libharfbuzz.a: build/lib/freetype/build_hb/dist_hb/lib/libfreetype.a build/lib/harfbuzz/configure
88 | cd build/lib/harfbuzz && \
89 | EM_PKG_CONFIG_PATH=$(PKG_CONFIG_PATH):$(BASE_DIR)build/lib/freetype/build_hb/dist_hb/lib/pkgconfig \
90 | CFLAGS="-DHB_NO_MT $(CFLAGS)" \
91 | CXXFLAGS="-DHB_NO_MT $(CFLAGS)" \
92 | $(call CONFIGURE_AUTO) \
93 | --with-freetype \
94 | && \
95 | cd src && \
96 | $(JSO_MAKE) install-libLTLIBRARIES install-pkgincludeHEADERS install-pkgconfigDATA
97 |
98 | # Freetype with Harfbuzz
99 | $(DIST_DIR)/lib/libfreetype.a: $(DIST_DIR)/lib/libharfbuzz.a $(DIST_DIR)/lib/libbrotlidec.a
100 | cd build/lib/freetype && \
101 | EM_PKG_CONFIG_PATH=$(PKG_CONFIG_PATH):$(BASE_DIR)build/lib/freetype/build_hb/dist_hb/lib/pkgconfig \
102 | $(call CONFIGURE_AUTO) \
103 | --with-brotli=yes \
104 | --with-harfbuzz \
105 | && \
106 | $(JSO_MAKE) install
107 |
108 | # Fontconfig
109 | build/lib/fontconfig/configure: lib/fontconfig $(wildcard $(BASE_DIR)build/patches/fontconfig/*.patch)
110 | $(call PREPARE_SRC_PATCHED,fontconfig)
111 | cd build/lib/fontconfig && $(RECONF_AUTO)
112 |
113 | $(DIST_DIR)/lib/libfontconfig.a: $(DIST_DIR)/lib/libharfbuzz.a $(DIST_DIR)/lib/libexpat.a $(DIST_DIR)/lib/libfribidi.a $(DIST_DIR)/lib/libfreetype.a build/lib/fontconfig/configure
114 | cd build/lib/fontconfig && \
115 | $(call CONFIGURE_AUTO) \
116 | --disable-docs \
117 | --with-default-fonts=/fonts \
118 | && \
119 | $(JSO_MAKE) -C src/ install && \
120 | $(JSO_MAKE) -C fontconfig/ install && \
121 | $(JSO_MAKE) install-pkgconfigDATA
122 |
123 |
124 | # libass
125 | build/lib/libass/configured: lib/libass
126 | cd lib/libass && $(RECONF_AUTO)
127 | $(call PREPARE_SRC_VPATH,libass)
128 | touch build/lib/libass/configured
129 |
130 | $(DIST_DIR)/lib/libass.a: $(DIST_DIR)/lib/libfontconfig.a $(DIST_DIR)/lib/libharfbuzz.a $(DIST_DIR)/lib/libexpat.a $(DIST_DIR)/lib/libfribidi.a $(DIST_DIR)/lib/libfreetype.a $(DIST_DIR)/lib/libbrotlidec.a build/lib/libass/configured
131 | cd build/lib/libass && \
132 | $(call CONFIGURE_AUTO,../../../lib/libass) \
133 | --disable-asm \
134 | --enable-fontconfig \
135 | && \
136 | $(JSO_MAKE) install
137 |
138 | # SubtitleOctopus.js
139 | OCTP_DEPS = \
140 | $(DIST_DIR)/lib/libfribidi.a \
141 | $(DIST_DIR)/lib/libbrotlicommon.a \
142 | $(DIST_DIR)/lib/libbrotlidec.a \
143 | $(DIST_DIR)/lib/libfreetype.a \
144 | $(DIST_DIR)/lib/libexpat.a \
145 | $(DIST_DIR)/lib/libharfbuzz.a \
146 | $(DIST_DIR)/lib/libfontconfig.a \
147 | $(DIST_DIR)/lib/libass.a
148 |
149 | src/subtitles-octopus-worker.bc: $(OCTP_DEPS) all-src
150 | .PHONY: all-src
151 | all-src:
152 | $(MAKE) -C src all
153 |
154 | # Dist Files
155 | EMCC_COMMON_ARGS = \
156 | $(LDFLAGS) \
157 | -s AUTO_NATIVE_LIBRARIES=0 \
158 | -s EXPORTED_FUNCTIONS="['_main', '_malloc']" \
159 | -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE="['\$$Browser']" \
160 | -s EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap', 'getValue', 'FS_createPreloadedFile', 'FS_createPath']" \
161 | --embed-file assets/fonts.conf \
162 | -s ALLOW_MEMORY_GROWTH=1 \
163 | -s NO_FILESYSTEM=0 \
164 | --memory-init-file=0 \
165 | --no-heap-copy \
166 | -o $@
167 |
168 | dist: src/subtitles-octopus-worker.bc dist/js/subtitles-octopus-worker.js dist/js/subtitles-octopus-worker-legacy.js dist/js/subtitles-octopus.js dist/js/COPYRIGHT dist/js/default.woff2
169 |
170 | dist/js/subtitles-octopus-worker.js: src/subtitles-octopus-worker.bc src/pre-worker.js src/SubOctpInterface.js src/post-worker.js build/lib/brotli/js/decode.js
171 | mkdir -p dist/js
172 | emcc src/subtitles-octopus-worker.bc $(OCTP_DEPS) \
173 | --pre-js src/pre-worker.js \
174 | --pre-js build/lib/brotli/js/decode.js \
175 | --post-js src/SubOctpInterface.js \
176 | --post-js src/post-worker.js \
177 | -s WASM=1 \
178 | $(EMCC_COMMON_ARGS)
179 |
180 | dist/js/subtitles-octopus-worker-legacy.js: src/subtitles-octopus-worker.bc src/polyfill.js src/pre-worker.js src/SubOctpInterface.js src/post-worker.js build/lib/brotli/js/decode.js build/lib/brotli/js/polyfill.js
181 | mkdir -p dist/js
182 | emcc src/subtitles-octopus-worker.bc $(OCTP_DEPS) \
183 | --pre-js src/polyfill.js \
184 | --pre-js build/lib/brotli/js/polyfill.js \
185 | --pre-js src/pre-worker.js \
186 | --pre-js build/lib/brotli/js/decode.js \
187 | --post-js src/SubOctpInterface.js \
188 | --post-js src/post-worker.js \
189 | -s WASM=0 \
190 | -s LEGACY_VM_SUPPORT=1 \
191 | -s MIN_CHROME_VERSION=27 \
192 | -s MIN_SAFARI_VERSION=60005 \
193 | $(EMCC_COMMON_ARGS)
194 |
195 | dist/js/subtitles-octopus.js: dist/license/all src/subtitles-octopus.js
196 | mkdir -p dist/js
197 | awk '1 {print "// "$$0}' dist/license/all | cat - src/subtitles-octopus.js > $@
198 |
199 | dist/license/all:
200 | @#FIXME: allow -j in toplevel Makefile and reintegrate licence extraction into this file
201 | make -j "$$(nproc)" -f Makefile_licence all
202 |
203 | dist/js/COPYRIGHT: dist/license/all
204 | cp "$<" "$@"
205 |
206 | dist/js/default.woff2:
207 | cp assets/default.woff2 "$@"
208 |
209 | # Clean Tasks
210 |
211 | clean: clean-dist clean-libs clean-octopus
212 |
213 | clean-dist:
214 | rm -frv dist/libraries/*
215 | rm -frv dist/js/*
216 | rm -frv dist/license/*
217 | clean-libs:
218 | rm -frv dist/libraries build/lib
219 | clean-octopus:
220 | cd src && git clean -fdX
221 |
222 | git-checkout:
223 | git submodule sync --recursive && \
224 | git submodule update --init --recursive
225 |
226 | SUBMODULES := brotli expat fontconfig freetype fribidi harfbuzz libass
227 | git-smreset: $(addprefix git-, $(SUBMODULES))
228 |
229 | $(foreach subm, $(SUBMODULES), $(eval $(call TR_GIT_SM_RESET,$(subm))))
230 |
231 | server: # Node http server npm i -g http-server
232 | http-server
233 |
234 | .PHONY: clean clean-dist clean-libs clean-octopus git-checkout git-smreset server
235 |
--------------------------------------------------------------------------------
/Makefile_licence:
--------------------------------------------------------------------------------
1 | # FIXME: temporarily split Makefile to parallelise licence info extraction
2 | # once -j can be passed to the toplevel Makefile this should be moved
3 | # back into the main Makefile
4 |
5 | all: dist/license/all
6 | .PHONY: all
7 |
8 | LIB_LICENSES := brotli expat freetype fribidi fontconfig harfbuzz libass
9 | LIB_LICENSES_FINDOPT_brotli := -path ./research -prune -false -o ! -path ./js/decode.min.js
10 | LIB_LICENSES_FINDOPT_expat := -path ./expat/fuzz -prune -false -o
11 | LIB_LICENSES_FINDOPT_freetype := -path ./src/tools -prune -false -o
12 | LIB_LICENSES_FINDOPT_fribidi := -path ./bin -prune -false -o
13 | LIB_LICENSES_FINDOPT_harfbuzz := -path ./test -prune -false -o
14 |
15 | $(addprefix dist/license/, $(LIB_LICENSES)): dist/license/%: .git/modules/lib/%/HEAD build/license_extract.sh build/license_defaults
16 | @mkdir -p dist/license
17 | (cd "lib/$*" && FINDOPTS="$(LIB_LICENSES_FINDOPT_$*)" \
18 | ../../build/license_extract.sh ../../build/license_defaults "$*" .) > $@
19 |
20 | dist/license/subtitlesoctopus: .git/HEAD build/license_extract.sh
21 | @mkdir -p dist/license
22 | build/license_extract.sh build/license_defaults subtitlesoctopus src > dist/license/subtitlesoctopus
23 |
24 | dist/license/all: dist/license/subtitlesoctopus $(addprefix dist/license/, $(LIB_LICENSES)) build/license_fullnotice build/license_lint.awk
25 | @echo "# The following lists all copyright notices and licenses for the" > dist/license/all
26 | @echo "# work contained in JavascriptSubtitlesOctopus per project." >> dist/license/all
27 | @echo "" >> dist/license/all
28 |
29 | @echo "Concatenate extracted license info..."
30 | @$(foreach LIB_PROJ, subtitlesoctopus $(LIB_LICENSES), \
31 | echo "# Project: $(LIB_PROJ)" >> dist/license/all && \
32 | cat dist/license/$(LIB_PROJ) >> dist/license/all && \
33 | ) :
34 |
35 | mv dist/license/all dist/license/all.tmp
36 | build/license_lint.awk dist/license/all.tmp build/license_fullnotice
37 | cat dist/license/all.tmp build/license_fullnotice > dist/license/all
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/jellyfin/JavascriptSubtitlesOctopus/actions/workflows/emscripten.yml?query=branch%3Amaster+event%3Apush)
2 |
3 |
4 | SubtitlesOctopus displays subtitles in .ass format and easily integrates with HTML5 videos.
5 | Since it uses [libass](https://github.com/libass/libass), SubtitlesOctopus supports most
6 | SSA/ASS features and enables you to get consistent results in authoring and web-playback,
7 | provided libass is also used locally.
8 |
9 | [ONLINE DEMO](https://jellyfin.github.io/JavascriptSubtitlesOctopus/videojs.html)
10 | / [other examples with demo](https://jellyfin.github.io/JavascriptSubtitlesOctopus/)
11 |
12 | ## Features
13 |
14 | - Supports most SSA/ASS features (everything libass supports)
15 | - Supports all OpenType- and TrueType-fonts (including woff2 fonts)
16 | - Works fast (because uses WebAssembly with fallback to asm.js if it's not available)
17 | - Uses Web Workers thus video and interface doesn't lag even on "heavy" subtitles (working in background)
18 | - Doesn't use DOM manipulations and render subtitles on single canvas
19 | - Fully compatible with [libass'](https://github.com/libass/libass) extensions
20 | (but beware of compatability to other ASS-renderers when using them)
21 | - Easy to use - just connect it to video element
22 |
23 | ## Included Libraries
24 |
25 | * libass
26 | * expat
27 | * fontconfig
28 | * freetype
29 | * fribidi
30 | * harfbuzz
31 | * brotli
32 |
33 | ## Usage
34 |
35 | To start using SubtitlesOctopus you only need to instantiate a new instance of
36 | `SubtitlesOctopus` and specify its [Options](#options).
37 |
38 | ```javascript
39 | var options = {
40 | video: document.getElementById('video'), // HTML5 video element
41 | subUrl: '/test/test.ass', // Link to subtitles
42 | fonts: ['/test/font-1.ttf', '/test/font-2.ttf'], // Links to fonts (not required, default font already included in build)
43 | workerUrl: '/libassjs-worker.js', // Link to WebAssembly-based file "libassjs-worker.js"
44 | legacyWorkerUrl: '/libassjs-worker-legacy.js' // Link to non-WebAssembly worker
45 | };
46 | var instance = new SubtitlesOctopus(options);
47 | ```
48 |
49 | After that SubtitlesOctopus automatically "connects" to your video and it starts
50 | to display subtitles. You can use it with any HTML5 player.
51 |
52 | [See other examples](https://github.com/jellyfin/JavascriptSubtitlesOctopus/tree/gh-pages/).
53 |
54 | ### Using only with canvas
55 | You're also able to use it without any video. However, that requires you to set
56 | the time the subtitles should render at yourself:
57 |
58 | ```javascript
59 | var options = {
60 | canvas: document.getElementById('canvas'), // canvas element
61 | subUrl: '/test/test.ass', // Link to subtitles
62 | fonts: ['/test/font-1.ttf', '/test/font-2.ttf'], // Links to fonts (not required, default font already included in build)
63 | workerUrl: '/libassjs-worker.js' // Link to file "libassjs-worker.js"
64 | };
65 | var instance = new SubtitlesOctopus(options);
66 | // And then...
67 | instance.setCurrentTime(15); // Render subtitles at 00:15 on your canvas
68 | ```
69 |
70 | ### Changing subtitles
71 | You're not limited to only display the subtitle file you referenced in your
72 | options. You're able to dynamically change subtitles on the fly. There's three
73 | methods that you can use for this specifically:
74 |
75 | - `setTrackByUrl(url)`: works the same as the `subUrl` option. It will set the
76 | subtitle to display by its URL.
77 | - `setTrack(content)`: works the same as the `subContent` option. It will set
78 | the subtitle to dispaly by its content.
79 | - `freeTrack()`: this simply removes the subtitles. You can use the two methods
80 | above to set a new subtitle file to be displayed.
81 |
82 | ```JavaScript
83 | var instance = new SubtitlesOctopus(options);
84 |
85 | // ... we want to change the subtitles to the Railgun OP
86 | instance.setTrackByUrl('/test/railgun_op.ass');
87 | ```
88 |
89 | ### Cleaning up the object
90 | After you're finished with rendering the subtitles. You need to call the
91 | `instance.dispose()` method to correctly dispose of the object.
92 |
93 | ```JavaScript
94 | var instance = new SubtitlesOctopus(options);
95 |
96 | // After you've finished using it...
97 |
98 | instance.dispose();
99 | ```
100 |
101 |
102 | ### Options
103 | When creating an instance of SubtitleOctopus, you can set the following options:
104 |
105 | - `video`: The video element to attach listeners to. (Optional)
106 | - `canvas`: The canvas to render the subtitles to. If none is given it will
107 | create a new canvas and insert it as a sibling of the video element (only if
108 | the video element exists). (Optional)
109 | - `subUrl`: The URL of the subtitle file to play. (Require either `subUrl` or
110 | `subContent` to be specified)
111 | - `subContent`: The content of the subtitle file to play. (Require either
112 | `subContent` or `subUrl` to be specified)
113 | - `workerUrl`: The URL of the worker. (Default: `libassjs-worker.js`)
114 | - `fonts`: An array of links to the fonts used in the subtitle. (Optional)
115 | - `availableFonts`: Object with all available fonts - Key is font name in lower
116 | case, value is link: `{"arial": "/font1.ttf"}` (Optional)
117 | - `fallbackFont`: URL to override fallback font, for example, with a CJK one. Default fallback font is Liberation Sans (Optional)
118 | - `lazyFileLoading`: A boolean, whether to load files in a lazy way via [FS.createLazyFile()](https://emscripten.org/docs/api_reference/Filesystem-API.html#FS.createLazyFile). [Requires](https://github.com/emscripten-core/emscripten/blob/c7b21c32fef92799da05d15ba1939b6394fe0373/src/library_fs.js#L1679-L1856) `Access-Control-Expose-Headers` for `Accept-Ranges, Content-Length, and Content-Encoding`. If encoding is compressed or length is not set, file will be fully fetched instead of just a HEAD request.
119 | - `timeOffset`: The amount of time the subtitles should be offset from the
120 | video. (Default: `0`)
121 | - `onReady`: Function that's called when SubtitlesOctopus is ready. (Optional)
122 | - `onError`: Function called in case of critical error meaning the subtitles
123 | wouldn't be shown and you should use an alternative method (for instance it
124 | occurs if browser doesn't support web workers). (Optional)
125 | - `debug`: Whether performance info is printed in the console. (Default:
126 | `false`)
127 | - `renderMode`: Rendering mode.
128 | (If not set, the deprecated option `lossyRender` is evaluated)
129 | - `js-blend` - JS Blending
130 | - `wasm-blend` - WASM Blending, currently the default
131 | - `lossy` - Lossy Render Mode (EXPERIMENTAL)
132 | - `targetFps`: Target FPS (Default: `24`)
133 | - `libassMemoryLimit`: libass bitmap cache memory limit in MiB (approximate)
134 | (Default: `0` - no limit)
135 | - `libassGlyphLimit`: libass glyph cache memory limit in MiB (approximate)
136 | (Default: `0` - no limit)
137 | - `prescaleFactor`: Scale down (`< 1.0`) the subtitles canvas to improve
138 | performance at the expense of quality, or scale it up (`> 1.0`).
139 | (Default: `1.0` - no scaling; must be a number > 0)
140 | - `prescaleHeightLimit`: The height beyond which the subtitles canvas won't be prescaled.
141 | (Default: `1080`)
142 | - `maxRenderHeight`: The maximum rendering height of the subtitles canvas.
143 | Beyond this subtitles will be upscaled by the browser.
144 | (Default: `0` - no limit)
145 | - `dropAllAnimations`: Remove all animation tags, such as karaoke, move, fade, etc.
146 | (Default: `false`)
147 | - `renderAhead`: How many MiB (approximate) of subtitles to render ahead and store.
148 | (Default: `0` - don't render ahead)
149 | - `resizeVariation`: The resize threshold at which the cache of pre-rendered events is cleared.
150 | (Default: `0.2`)
151 |
152 | ### Rendering Modes
153 | #### JS Blending
154 | To use this mode set `renderMode` to `js-blend` upon instance creation.
155 | This will do all the processing of the bitmaps produced by libass outside of WebAssembly.
156 |
157 | #### WASM Blending
158 | To use this mode set `renderMode` to `wasm-blend` upon instance creation.
159 | This will blend the bitmaps of the different events together in WebAssembly,
160 | so the JavaScript-part only needs to process a single image.
161 | If WebAssembly-support is available this will be faster than the default mode,
162 | especially for many and/or complex simultaneous subtitles.
163 | Without WebAssembly-support it will fallback to asm.js and
164 | should at least not be slower than the default mode.
165 |
166 | #### Lossy Render Mode (EXPERIMENTAL)
167 | To use this mode set `renderMode` to `lossy` upon instance creation.
168 | The Lossy Render mode has been created by @no1d as a suggestion for fix browser
169 | freezing when rendering heavy subtitles (#46), it uses
170 | [createImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap)
171 | to render the bitmap in the Worker, using Promises instead of direct render on
172 | canvas in the Main Thread. When the browser start to hang, it will not lock main
173 | thread, instead will run Async, so if the function createImageBitmap fail, it
174 | will not stop the rendering process at all and may cause some bitmap loss or
175 | simply will not draw anything in canvas, mostly on low end devices.
176 |
177 | **WARNING: Experimental, not stable and not working in some browsers**
178 |
179 | #### Render Ahead (WASM Blending with pre-rendering) (EXPERIMENTAL)
180 | Upon creating the SubtitleOctopus instance, set `renderAhead` in the options to a positive value to use this mode.
181 | In this mode, SubtitleOctopus renders events in advance (using WASM blending) so that they are ready in time.
182 | The amount of pre-rendered events is controlled by the `renderAhead` option.
183 | Each pre-rendered event is provided with information about its start time, end time, and end time of the gap after (if any).
184 | This mode will analyse the events to avoid rendering empty sections or rerendering non-animated events.
185 | Resizing the video player clears the cache of pre-rendered events (the threshold is set by `resizeVariation`).
186 |
187 | > The `renderMode` and `lossyRender` options are ignored.
188 |
189 | > **WARNING: Experimental, may stall on heavily animated subtitles**
190 | This mode tries to render every transition - at worst, every frame - in advance.
191 | If the rendering of many frames takes too long and the cache of prepared frames gets depleted
192 | (e.g. during a long section with heavy animations), the current subtitle-frame will continue to
193 | be displayed until the prerendering can catch up again.
194 | Adjusting `prescaleFactor`, `prescaleHeightLimit` and `maxRenderHeight` to lower the resolution of
195 | the rendering canvas can work around this at the expense of visual quality.
196 |
197 |
198 | ### Brotli Compressed Subtitles (DEPRECATED)
199 | Manual support for brotli-compressed subtitles is tentatively deprecated
200 | and may be removed with the next release.
201 |
202 | Instead use HTTP's `Content-Encoding:` header to transmit files compressed and
203 | let the browser handle decompression before it reaches JSO. This supports more
204 | compression algorithms and is likely faster.
205 | Do not use a `.br` file extension if you use `Content-Ecoding:` as this will
206 | conflict with the still existing manual support which tries to decompress any data
207 | with a `.br` extension.
208 |
209 | ## How to build?
210 |
211 | ### Dependencies
212 | * git
213 | * emscripten (Configure the enviroment)
214 | * make
215 | * python3
216 | * cmake
217 | * pkgconfig
218 | * patch
219 | * libtool
220 | * autotools (autoconf, automake, autopoint)
221 | * gettext
222 | * ragel - Required by Harfbuzz
223 | * itstool - Required by Fontconfig
224 | * python3-ply - Required by WebIDL
225 | * gperf - Required by Fontconfig
226 | * licensecheck
227 |
228 | ### Get the Source
229 |
230 | Run `git clone --recursive https://github.com/jellyfin/JavascriptSubtitlesOctopus.git`
231 |
232 | ### Build inside a Container
233 | #### Docker
234 | 1) Install Docker
235 | 2) `./run-docker-build.sh`
236 | 3) Artifacts are in /dist/js
237 | #### Buildah
238 | 1) Install Buildah and a suitable backend for `buildah run` like `crun` or `runc`
239 | 2) `./run-buildah-build.sh`
240 | 3) Artifacts are in /dist/js
241 |
242 | ### Build without Containers
243 | 1) Install the dependency packages listed above
244 | 2) `make`
245 | - If on macOS with libtool from brew, `LIBTOOLIZE=glibtoolize make`
246 | 3) Artifacts are in /dist/js
247 |
248 | ## Why "Octopus"?
249 | How am I an Octopus? [Ba da ba da ba!](https://www.youtube.com/watch?v=tOzOD-82mW0)
250 |
--------------------------------------------------------------------------------
/assets/default.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jellyfin/JavascriptSubtitlesOctopus/950dbb4b5bcb713e4bf0d33dec63957e5806bbfd/assets/default.woff2
--------------------------------------------------------------------------------
/assets/fonts.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /fonts
5 |
6 |
7 | mono
8 |
9 |
10 | monospace
11 |
12 |
13 |
14 |
15 | sans serif
16 |
17 |
18 | sans-serif
19 |
20 |
21 |
22 |
23 | sans
24 |
25 |
26 | sans-serif
27 |
28 |
29 | /fontconfig
30 |
31 |
32 | 30
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/build/license_defaults:
--------------------------------------------------------------------------------
1 | # Tab seperated list of default license
2 | # and if given copyrightholder per project
3 | subtitlesoctopus Expat 2017-2021 JavascriptSubtitlesOctopus contributors
4 |
5 | brotli Expat 2009, 2010, 2013-2016 by the Brotli Authors
6 | expat Expat 2000-2017 Expat development team / 1997-2000 Thai Open Source Software Center Ltd
7 | libass ISC 2006-2016 libass contributors
8 | fontconfig NTP~disclaimer 2000-2007 Keith Packard / 2005 Patrick Lam / 2009 Roozbeh Pournader / 2008,2009 Red Hat, Inc. / 2008 Danilo Šegan / 2012 Google, Inc.
9 |
10 | fribidi LGPL-2.1+
11 | harfbuzz MIT~old
12 | freetype FTL
13 |
--------------------------------------------------------------------------------
/build/license_extract.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2021 Oneric
4 | # SPDX-License-Identifier: ISC
5 |
6 | # Postprocesses the output of licensecheck to automatically
7 | # generate our distribution notice.
8 | # licensecheck is packaged in Debian and its derivatives
9 | # and can be obtained from CPAN everywhere.
10 |
11 | usage() {
12 | echo "$0