Using subtitles-octopus to render subtitles

This commit is contained in:
Zoe Roux 2020-10-26 03:03:20 +01:00
parent 199e541997
commit cdb3cf696c
18 changed files with 865 additions and 4405 deletions

View File

@ -3,9 +3,10 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = tab
indent_size = 2 indent_size = 4
insert_final_newline = true insert_final_newline = true
max_line_length = 120
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.md] [*.md]

View File

@ -1,108 +1,108 @@
{ {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"kyoo": { "kyoo": {
"projectType": "application", "projectType": "application",
"schematics": { "schematics": {
"@schematics/angular:component": { "@schematics/angular:component": {
"style": "scss" "style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"./node_modules/hls.js/dist/hls.js",
"./src/libraries/subtitles.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
} }
], },
"optimization": true, "root": "",
"outputHashing": "all", "sourceRoot": "src",
"sourceMap": false, "prefix": "app",
"extractCss": true, "architect": {
"namedChunks": false, "build": {
"aot": true, "builder": "@angular-devkit/build-angular:browser",
"extractLicenses": true, "options": {
"vendorChunk": false, "outputPath": "dist",
"buildOptimizer": true, "index": "src/index.html",
"budgets": [ "main": "src/main.ts",
{ "polyfills": "src/polyfills.ts",
"type": "initial", "tsConfig": "tsconfig.json",
"maximumWarning": "3mb", "aot": false,
"maximumError": "5mb" "assets": [
"src/assets",
{
"input": "node_modules/libass-wasm/dist/js",
"glob": "subtitles-octopus-worker*",
"output": "."
}
],
"styles": [
"src/styles.scss"
],
"scripts": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"./node_modules/hls.js/dist/hls.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "3mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
}, },
{ "serve": {
"type": "anyComponentStyle", "builder": "@angular-devkit/build-angular:dev-server",
"maximumWarning": "6kb", "options": {
"maximumError": "10kb" "browserTarget": "kyoo:build"
},
"configurations": {
"production": {
"browserTarget": "kyoo:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "kyoo:build"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "tsconfig.json",
"exclude": [
"**/node_modules/**"
]
}
} }
]
} }
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "kyoo:build"
},
"configurations": {
"production": {
"browserTarget": "kyoo:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "kyoo:build"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
} }
} },
"defaultProject": "kyoo",
"cli": {
"analytics": false
} }
}, }
"defaultProject": "kyoo",
"cli": {
"analytics": false
}
}

View File

@ -1,12 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

1130
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,56 @@
{ {
"name": "kyoo", "name": "kyoo",
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build",
"lint": "ng lint" "lint": "ng lint",
}, "postinstall": "./subtitles-octopus-fix.sh"
"private": true, },
"dependencies": { "private": true,
"@angular/animations": "^10.1.4", "browserslist": [
"@angular/cdk": "^10.2.3", "> 0.5%",
"@angular/common": "^10.1.4", "last 2 versions",
"@angular/compiler": "^10.1.4", "Firefox ESR",
"@angular/core": "^10.1.4", "not dead",
"@angular/forms": "^10.1.4", "not IE 9-11"
"@angular/material": "^10.2.3", ],
"@angular/platform-browser": "^10.1.4", "dependencies": {
"@angular/platform-browser-dynamic": "^10.1.4", "@angular/animations": "^10.2.0",
"@angular/router": "^10.1.4", "@angular/cdk": "^10.2.5",
"angular-auth-oidc-client": "11.2.0", "@angular/common": "^10.2.0",
"bootstrap": "^4.5.2", "@angular/compiler": "^10.2.0",
"detect-browser": "^5.1.1", "@angular/core": "^10.2.0",
"hls.js": "^0.14.13", "@angular/forms": "^10.2.0",
"jquery": "^3.5.1", "@angular/material": "^10.2.5",
"ngx-infinite-scroll": "^9.1.0", "@angular/platform-browser": "^10.2.0",
"popper.js": "^1.16.1", "@angular/platform-browser-dynamic": "^10.2.0",
"rxjs": "^6.6.3", "@angular/router": "^10.2.0",
"zone.js": "^0.11.1" "angular-auth-oidc-client": "11.2.1",
}, "bootstrap": "^4.5.3",
"devDependencies": { "detect-browser": "^5.2.0",
"@angular-devkit/build-angular": "^0.1001.4", "hls.js": "^0.14.16",
"@angular/cli": "^10.1.4", "jquery": "^3.5.1",
"@angular/compiler-cli": "^10.1.4", "libass-wasm": "^4.0.0",
"@angular/language-service": "^10.1.4", "ngx-infinite-scroll": "^9.1.0",
"@types/bootstrap": "^4.5.0", "popper.js": "^1.16.1",
"@types/hls.js": "^0.13.1", "rxjs": "^6.6.3",
"@types/jquery": "^3.5.1", "zone.js": "^0.11.2"
"@types/node": "^14.11.2", },
"@types/video.js": "^7.3.11", "devDependencies": {
"codelyzer": "^6.0.1", "@angular-devkit/build-angular": "^0.1002.0",
"ts-node": "~9.0.0", "@angular/cli": "^10.2.0",
"tslint": "^6.1.3", "@angular/compiler-cli": "^10.2.0",
"typescript": "~4.0.3" "@angular/language-service": "^10.2.0",
} "@types/bootstrap": "^5.0.0",
"@types/hls.js": "^0.13.2",
"@types/jquery": "^3.5.3",
"@types/node": "^14.14.2",
"@types/video.js": "^7.3.11",
"codelyzer": "^6.0.1",
"ts-node": "~9.0.0",
"tslint": "^6.1.3",
"typescript": "~4.0.3"
}
} }

View File

@ -1,7 +1,7 @@
import {ItemType, LibraryItem} from "../models/resources/library-item"; import { ItemType, LibraryItem } from "../models/resources/library-item";
import {Show, ShowRole} from "../models/resources/show"; import { Show, ShowRole } from "../models/resources/show";
import {Collection} from "../models/resources/collection"; import { Collection } from "../models/resources/collection";
import {People} from "../models/resources/people"; import { People } from "../models/resources/people";
export class ItemsUtils export class ItemsUtils
{ {

View File

@ -1,4 +1,3 @@
@import "../../../libraries/subtitles.css";
@import "vtt-subtitles"; @import "vtt-subtitles";
.player .player

View File

@ -22,8 +22,8 @@ import {
} from "./playbackMethodDetector"; } from "./playbackMethodDetector";
import { AppComponent } from "../../app.component"; import { AppComponent } from "../../app.component";
import { Track, WatchItem } from "../../models/watch-item"; import { Track, WatchItem } from "../../models/watch-item";
import SubtitlesOctopus from "libass-wasm/dist/js/subtitles-octopus.js"
declare var SubtitleManager: any;
@Pipe({ @Pipe({
name: "formatTime", name: "formatTime",
@ -151,6 +151,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
displayStats: boolean = false; displayStats: boolean = false;
private subtitlesManager: SubtitlesOctopus;
private hlsPlayer: Hls = new Hls(); private hlsPlayer: Hls = new Hls();
private oidcSecurity: OidcSecurityService; private oidcSecurity: OidcSecurityService;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
@ -220,6 +221,8 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
ngOnDestroy() ngOnDestroy()
{ {
if (this.subtitlesManager)
this.subtitlesManager.dispose();
if (this.isFullScreen) if (this.isFullScreen)
document.exitFullscreen(); document.exitFullscreen();
@ -439,7 +442,8 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
duration: 750, duration: 750,
panelClass: "info-panel" panelClass: "info-panel"
}); });
SubtitleManager.remove(this.player); if (this.subtitlesManager)
this.subtitlesManager.freeTrack();
this.removeHtmlTrack(); this.removeHtmlTrack();
} }
else else
@ -453,10 +457,21 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
this.removeHtmlTrack(); this.removeHtmlTrack();
if (subtitle.codec == "ass") if (subtitle.codec == "ass")
SubtitleManager.add(this.player, `subtitle/${subtitle.slug}`, true); {
if (!this.subtitlesManager)
{
this.subtitlesManager = new SubtitlesOctopus({
video: this.player,
subUrl: `subtitle/${subtitle.slug}`
});
}
else
this.subtitlesManager.setTrackByUrl(`subtitle/${subtitle.slug}`);
}
else if (subtitle.codec == "subrip") else if (subtitle.codec == "subrip")
{ {
SubtitleManager.remove(this.player); if (this.subtitlesManager)
this.subtitlesManager.freeTrack();
let track = document.createElement("track"); let track = document.createElement("track");
track.kind = "subtitles"; track.kind = "subtitles";

View File

@ -1,23 +0,0 @@
.subtitle_container {
line-height: normal;
position: absolute;
pointer-events: none;
transform-origin: 0 0 0;
top: 0;
left: 0;
}
.subtitle_container text, .subtitle_container path {
dominant-baseline: text-before-edge;
text-anchor: start;
transform-box: view-box;
paint-order: stroke;
position: absolute;
top: 0;
left: 0;
}
.subtitle_container tspan {
white-space: pre;
}
.subtitle_container mask path {
fill: white;
}

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@ h6
//Material Angular Configuration //Material Angular Configuration
@import '~@angular/material/theming'; @import "~@angular/material/theming";
@include mat-core(); @include mat-core();
$primary: (default: #0a1128); $primary: (default: #0a1128);
@ -51,21 +51,23 @@ $theme: mat-dark-theme($primary, $accent);
} }
//Material Icons //Material Icons
//noinspection CssUnknownTarget
@font-face { @font-face {
font-family: 'Material Icons'; font-family: "Material Icons";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: url(/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */ src: url(/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
src: local('Material Icons'), src: local("Material Icons"),
local('MaterialIcons-Regular'), local("MaterialIcons-Regular"),
url(/iconfont/MaterialIcons-Regular.woff2) format('woff2'), url(/iconfont/MaterialIcons-Regular.woff2) format("woff2"),
url(/iconfont/MaterialIcons-Regular.woff) format('woff'), url(/iconfont/MaterialIcons-Regular.woff) format("woff"),
url(/iconfont/MaterialIcons-Regular.ttf) format('truetype'); url(/iconfont/MaterialIcons-Regular.ttf) format("truetype");
} }
//noinspection CssNoGenericFontName
.material-icons .material-icons
{ {
font-family: 'Material Icons'; font-family: "Material Icons";
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-size: 24px; /* Preferred icon size */ font-size: 24px; /* Preferred icon size */
@ -83,7 +85,7 @@ $theme: mat-dark-theme($primary, $accent);
/* Support for Firefox. */ /* Support for Firefox. */
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
/* Support for IE. */ /* Support for IE. */
font-feature-settings: 'liga'; font-feature-settings: "liga";
} }
mat-icon mat-icon
@ -106,4 +108,4 @@ mat-icon
.cdk-overlay-container .cdk-overlay-container
{ {
z-index: 2000 !important; z-index: 2000 !important;
} }

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

Before

Width:  |  Height:  |  Size: 365 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" fill="#ffffff"/></svg>

Before

Width:  |  Height:  |  Size: 298 B

7
subtitles-octopus-fix.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/bash
LINE_204=" self.video.addEventListener(\\\"loadedmetadata\\\", function listener(e) {"
LINE_205=" e.target.removeEventListener(e.type, listener);"
sed -i "204s/.*/$LINE_204/" node_modules/libass-wasm/dist/js/subtitles-octopus.js
sed -i "205s/.*/$LINE_205/" node_modules/libass-wasm/dist/js/subtitles-octopus.js

View File

@ -1,18 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.ts"
],
"exclude": [
"src/test.ts",
"src/**/*.spec.ts"
]
}

View File

@ -18,7 +18,8 @@
"lib": [ "lib": [
"es2018", "es2018",
"dom" "dom"
] ],
"allowJs": true
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"fullTemplateTypeCheck": true, "fullTemplateTypeCheck": true,

View File

@ -1,18 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@ -1,153 +1,149 @@
{ {
"extends": "tslint:recommended", "extends": "tslint:recommended",
"rulesDirectory": [ "rulesDirectory": [
"codelyzer" "codelyzer"
], ],
"rules": { "rules": {
"align": { "align": {
"options": [ "options": [
"parameters", "parameters",
"statements" "statements"
] ]
}, },
"array-type": false, "array-type": false,
"arrow-return-shorthand": true, "arrow-return-shorthand": true,
"curly": true, "curly": true,
"deprecation": { "deprecation": {
"severity": "warning" "severity": "warning"
}, },
"eofline": true, "eofline": true,
"import-blacklist": [ "import-spacing": true,
true, "indent": {
"rxjs/Rx" "options": [
], "tabs"
"import-spacing": true, ]
"indent": { },
"options": [ "max-classes-per-file": false,
"tabs" "max-line-length": [
] true,
}, 120
"max-classes-per-file": false, ],
"max-line-length": [ "member-ordering": [
true, true,
120 {
], "order": [
"member-ordering": [ "static-field",
true, "instance-field",
{ "static-method",
"order": [ "instance-method"
"static-field", ]
"instance-field", }
"static-method", ],
"instance-method" "no-console": [
] true,
} "debug",
], "info",
"no-console": [ "time",
true, "timeEnd",
"debug", "trace"
"info", ],
"time", "no-empty": false,
"timeEnd", "no-inferrable-types": [
"trace" true,
], "ignore-params"
"no-empty": false, ],
"no-inferrable-types": [ "no-non-null-assertion": true,
true, "no-redundant-jsdoc": true,
"ignore-params" "no-switch-case-fall-through": true,
], "no-var-requires": false,
"no-non-null-assertion": true, "object-literal-key-quotes": [
"no-redundant-jsdoc": true, true,
"no-switch-case-fall-through": true, "as-needed"
"no-var-requires": false, ],
"object-literal-key-quotes": [ "quotemark": [
true, true,
"as-needed" "double"
], ],
"quotemark": [ "semicolon": {
true, "options": [
"double" "always"
], ]
"semicolon": { },
"options": [ "space-before-function-paren": {
"always" "options": {
] "anonymous": "never",
}, "asyncArrow": "always",
"space-before-function-paren": { "constructor": "never",
"options": { "method": "never",
"anonymous": "never", "named": "never"
"asyncArrow": "always", }
"constructor": "never", },
"method": "never", "typedef": [
"named": "never" true,
} "call-signature"
}, ],
"typedef": [ "typedef-whitespace": {
true, "options": [
"call-signature" {
], "call-signature": "nospace",
"typedef-whitespace": { "index-signature": "nospace",
"options": [ "parameter": "nospace",
{ "property-declaration": "nospace",
"call-signature": "nospace", "variable-declaration": "nospace"
"index-signature": "nospace", },
"parameter": "nospace", {
"property-declaration": "nospace", "call-signature": "onespace",
"variable-declaration": "nospace" "index-signature": "onespace",
}, "parameter": "onespace",
{ "property-declaration": "onespace",
"call-signature": "onespace", "variable-declaration": "onespace"
"index-signature": "onespace", }
"parameter": "onespace", ]
"property-declaration": "onespace", },
"variable-declaration": "onespace" "variable-name": {
} "options": [
] "ban-keywords",
}, "check-format",
"variable-name": { "allow-pascal-case"
"options": [ ]
"ban-keywords", },
"check-format", "whitespace": {
"allow-pascal-case" "options": [
] "check-branch",
}, "check-decl",
"whitespace": { "check-operator",
"options": [ "check-separator",
"check-branch", "check-type",
"check-decl", "check-typecast",
"check-operator", "check-module"
"check-separator", ]
"check-type", },
"check-typecast", "component-class-suffix": true,
"check-module" "contextual-lifecycle": true,
] "directive-class-suffix": true,
}, "no-conflicting-lifecycle": true,
"component-class-suffix": true, "no-host-metadata-property": true,
"contextual-lifecycle": true, "no-input-rename": true,
"directive-class-suffix": true, "no-inputs-metadata-property": true,
"no-conflicting-lifecycle": true, "no-output-native": true,
"no-host-metadata-property": true, "no-output-on-prefix": true,
"no-input-rename": true, "no-output-rename": true,
"no-inputs-metadata-property": true, "no-outputs-metadata-property": true,
"no-output-native": true, "template-banana-in-box": true,
"no-output-on-prefix": true, "template-no-negated-async": true,
"no-output-rename": true, "use-lifecycle-interface": true,
"no-outputs-metadata-property": true, "use-pipe-transform-interface": true,
"template-banana-in-box": true, "directive-selector": [
"template-no-negated-async": true, true,
"use-lifecycle-interface": true, "attribute",
"use-pipe-transform-interface": true, "app",
"directive-selector": [ "camelCase"
true, ],
"attribute", "component-selector": [
"app", true,
"camelCase" "element",
], "app",
"component-selector": [ "kebab-case"
true, ]
"element", }
"app",
"kebab-case"
]
}
} }