Skip to content

Commit 810cda5

Browse files
committed
Frame the canvas
1 parent dcc8dd2 commit 810cda5

13 files changed

+242
-67
lines changed

.changeset/config.json

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
3-
"changelog": "@changesets/cli/changelog",
4-
"commit": false,
5-
"fixed": [],
6-
"linked": [],
7-
"access": "public",
8-
"baseBranch": "main",
9-
"updateInternalDependencies": "patch",
10-
"ignore": []
2+
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
3+
"changelog": "@changesets/cli/changelog",
4+
"commit": false,
5+
"fixed": [],
6+
"linked": [],
7+
"access": "public",
8+
"baseBranch": "main",
9+
"updateInternalDependencies": "patch",
10+
"ignore": []
1111
}

.github/workflows/main.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: CI
22
on:
33
push:
44
branches:
5-
- "**"
5+
- '**'
66

77
jobs:
88
build:
@@ -15,7 +15,7 @@ jobs:
1515
- uses: actions/setup-node@v3
1616
with:
1717
node-version: 18.x
18-
cache: "pnpm"
18+
cache: 'pnpm'
1919

2020
- run: pnpm install --frozen-lockfile
2121
- run: pnpm run lint && pnpm run build

.github/workflows/publish.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
name: Publish
22
on:
33
workflow_run:
4-
workflows: ["CI"]
4+
workflows: ['CI']
55
types:
66
- completed
77
push:
88
branches:
9-
- "main"
9+
- 'main'
1010

1111
concurrency: ${{ github.workflow }}-${{ github.ref }}
1212

@@ -22,7 +22,7 @@ jobs:
2222
- uses: actions/setup-node@v3
2323
with:
2424
node-version: 18.x
25-
cache: "pnpm"
25+
cache: 'pnpm'
2626

2727
- run: pnpm install --frozen-lockfile
2828
- name: Create Release Pull Request or Publish

.prettierignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.DS_Store
2+
node_modules
3+
/dist
4+
/.changeset
5+
6+
# Ignore files for PNPM, NPM and YARN
7+
pnpm-lock.yaml
8+
package-lock.json
9+
yarn.lock

.prettierrc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"semi": false,
3+
"tabWidth": 2,
4+
"useTabs": true,
5+
"singleQuote": true,
6+
"trailingComma": "none",
7+
"printWidth": 100
8+
}

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# ass-html5
44

5-
Display ASS/SSA subtitles on html5 videos
5+
Display ASS/SSA subtitles on html5 videos
66

77
![CI workflow](https://github.com/luxluth/ass-html5/actions/workflows/main.yml/badge.svg)
88
![Publish workflow](https://github.com/luxluth/ass-html5/actions/workflows/publish.yml/badge.svg)

package.json

+40-36
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,42 @@
11
{
2-
"name": "ass-html5",
3-
"version": "0.0.0",
4-
"description": "Display ASS/SSA subtitles on html5 videos",
5-
"main": "dist/index.js",
6-
"module": "dist/index.mjs",
7-
"types": "dist/index.d.ts",
8-
"scripts": {
9-
"build": "tsup",
10-
"dev": "tsup --watch",
11-
"release": "pnpm run build && changeset publish",
12-
"lint": "tsc"
13-
},
14-
"repository": {
15-
"type": "git",
16-
"url": "git+https://github.com/luxluth/ass-html5.git"
17-
},
18-
"keywords": [
19-
"ASS",
20-
"SSA",
21-
"subtitles",
22-
"aegisub",
23-
"libass"
24-
],
25-
"author": "luxluth",
26-
"license": "MIT",
27-
"bugs": {
28-
"url": "https://github.com/luxluth/ass-html5/issues"
29-
},
30-
"homepage": "https://github.com/luxluth/ass-html5#readme",
31-
"devDependencies": {
32-
"@changesets/cli": "^2.26.2",
33-
"prettier": "^3.0.0",
34-
"terser": "^5.18.2",
35-
"tsup": "^7.1.0",
36-
"typescript": "^5.1.6"
37-
}
2+
"name": "ass-html5",
3+
"version": "0.0.0",
4+
"description": "Display ASS/SSA subtitles on html5 videos",
5+
"main": "dist/index.js",
6+
"module": "dist/index.mjs",
7+
"types": "dist/index.d.ts",
8+
"scripts": {
9+
"build": "tsup",
10+
"dev": "tsup --watch",
11+
"release": "pnpm run build && changeset publish",
12+
"lint": "tsc",
13+
"format": "prettier --write ."
14+
},
15+
"repository": {
16+
"type": "git",
17+
"url": "git+https://github.com/luxluth/ass-html5.git"
18+
},
19+
"keywords": [
20+
"ASS",
21+
"SSA",
22+
"subtitles",
23+
"aegisub",
24+
"libass"
25+
],
26+
"author": "luxluth",
27+
"license": "MIT",
28+
"bugs": {
29+
"url": "https://github.com/luxluth/ass-html5/issues"
30+
},
31+
"homepage": "https://github.com/luxluth/ass-html5#readme",
32+
"devDependencies": {
33+
"@changesets/cli": "^2.26.2",
34+
"prettier": "^3.0.0",
35+
"terser": "^5.18.2",
36+
"tsup": "^7.1.0",
37+
"typescript": "^5.1.6"
38+
},
39+
"dependencies": {
40+
"ass-compiler": "^0.1.8"
41+
}
3842
}

pnpm-lock.yaml

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { parse } from 'ass-compiler'
2+
import { ASSOptions } from './types'
3+
import { newCanvas } from './utils'
4+
5+
export default class ASS {
6+
assText: string
7+
video: HTMLVideoElement | string
8+
videoElement: HTMLVideoElement | null = null
9+
canvas: HTMLCanvasElement| null = null
10+
constructor(
11+
options: ASSOptions
12+
) {
13+
this.assText = options.assText
14+
this.video = options.video
15+
}
16+
17+
init() {
18+
if (typeof this.video == 'string') {
19+
this.videoElement = document.querySelector(this.video)
20+
if (this.videoElement === null) {
21+
throw new Error("Unable to find the video element")
22+
}
23+
} else {
24+
this.videoElement = this.video
25+
}
26+
console.log(this.videoElement)
27+
this.setCanvasSize()
28+
29+
this.videoElement?.addEventListener('loadedmetadata', () => {
30+
this.setCanvasSize()
31+
})
32+
33+
window.addEventListener('resize', () => {
34+
this.setCanvasSize();
35+
});
36+
}
37+
38+
39+
setCanvasSize() {
40+
console.log("set Canva size")
41+
const { videoWidth, videoHeight, offsetTop, offsetLeft } = this.videoElement as HTMLVideoElement
42+
const aspectRatio = videoWidth / videoHeight;
43+
44+
const maxWidth = this.videoElement?.clientWidth || 0;
45+
const maxHeight = this.videoElement?.clientHeight || 0;
46+
47+
let width = maxWidth;
48+
let height = maxHeight;
49+
let x = offsetLeft;
50+
let y = offsetTop;
51+
52+
if (maxHeight * aspectRatio > maxWidth) {
53+
width = maxWidth;
54+
height = width / aspectRatio;
55+
y += (maxHeight - height) / 2;
56+
} else {
57+
height = maxHeight;
58+
width = height * aspectRatio;
59+
x += (maxWidth - width) / 2;
60+
}
61+
62+
if (this.canvas === null) {
63+
this.canvas = newCanvas(y, x, width, height, this.videoElement as HTMLVideoElement)
64+
} else {
65+
this.canvas.style.position = 'absolute';
66+
this.canvas.style.top = y + 'px';
67+
this.canvas.style.left = x + 'px';
68+
this.canvas.style.width = width + 'px';
69+
this.canvas.style.height = height + 'px';
70+
this.canvas.width = width;
71+
this.canvas.height = height;
72+
}
73+
74+
}
75+
}

src/types.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export type ASSOptions = {
2+
/**
3+
* The ass text string
4+
*/
5+
assText: string,
6+
/**
7+
* The video to display the subtile on.
8+
* Can be either an `HTMLVideoElement` or `string` (html query selector)
9+
*/
10+
video: HTMLVideoElement | string
11+
}

src/utils.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Convert a color in RGBA format to Aegisub format
3+
* @param aegisubColor The color in Aegisub format
4+
* @returns The color in RGBA format
5+
*/
6+
export function convertAegisubToRGBA(aegisubColor: string) {
7+
aegisubColor = aegisubColor.replace(/&H/g, '').replace(/&/g, '')
8+
let alpha = parseInt(aegisubColor.slice(0, 2), 16) / 255 + 1
9+
let red = parseInt(aegisubColor.slice(2, 4), 16)
10+
let green = parseInt(aegisubColor.slice(4, 6), 16)
11+
let blue = parseInt(aegisubColor.slice(6, 8), 16)
12+
13+
return 'rgba(' + red + ',' + green + ',' + blue + ',' + alpha + ')'
14+
}
15+
16+
export function ruleOfThree(
17+
value: number,
18+
valueMin: number,
19+
) {
20+
return (valueMin * 100) / value
21+
}
22+
23+
export function genRandomString(ln: number) {
24+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
25+
let randomString = '';
26+
27+
for (let i = 0; i < ln; i++) {
28+
const randomIndex = Math.floor(Math.random() * characters.length);
29+
randomString += characters.charAt(randomIndex);
30+
}
31+
32+
return randomString;
33+
}
34+
35+
export function newCanvas(top: number, left: number, width: number, height: number, insertAfter?: HTMLElement, zIndex?: number) {
36+
const canvas = document.createElement('canvas');
37+
canvas.style.position = 'absolute';
38+
canvas.style.width = width + 'px';
39+
canvas.style.height = height + 'px';
40+
canvas.width = width;
41+
canvas.height = height;
42+
canvas.style.top = top + 'px';
43+
canvas.style.left = left + 'px';
44+
canvas.style.pointerEvents = 'none';
45+
canvas.width = width;
46+
canvas.height = height;
47+
48+
if (zIndex) {
49+
canvas.style.zIndex = zIndex.toString();
50+
}
51+
52+
if (insertAfter) {
53+
insertAfter.after(canvas);
54+
}
55+
return canvas;
56+
}
57+

tsconfig.json

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
2-
"compilerOptions": {
3-
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
4-
"module": "commonjs", /* Specify what module code is generated. */
5-
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
6-
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
7-
"strict": true, /* Enable all strict type-checking options. */
8-
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
9-
"noUncheckedIndexedAccess": true,
10-
"noEmit": true
11-
}
2+
"compilerOptions": {
3+
"target": "esnext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
4+
"module": "commonjs" /* Specify what module code is generated. */,
5+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
6+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
7+
"strict": true /* Enable all strict type-checking options. */,
8+
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
9+
"noUncheckedIndexedAccess": true,
10+
"noEmit": true,
11+
"lib": ["esnext", "dom"]
12+
}
1213
}

tsup.config.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { defineConfig } from 'tsup'
22

33
export default defineConfig({
4-
entryPoints: ['src/index.ts'],
5-
format: ['cjs', 'esm'],
6-
dts: true,
7-
clean: true,
8-
splitting: true,
9-
platform: 'browser',
4+
entryPoints: ['src/index.ts'],
5+
format: ['cjs', 'esm'],
6+
dts: true,
7+
clean: true,
8+
splitting: true,
9+
platform: 'browser',
10+
target: 'es2018',
1011
})

0 commit comments

Comments
 (0)