mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-19 07:22:07 +00:00
imports, icons, fonts, styles etc
This commit is contained in:
parent
0e32204712
commit
e889f4f414
17 changed files with 1148 additions and 115 deletions
|
|
@ -10,13 +10,15 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
|
||||
"anitomyscript": "^2.0.4",
|
||||
"builtin-modules": "^3.2.0",
|
||||
"concurrently": "^7.0.0",
|
||||
"electron": "^16.0.10",
|
||||
"electron-builder": "^22.14.13",
|
||||
"svelte": "^3.46.4",
|
||||
"vite": "^2.8.6",
|
||||
"vite-plugin-commonjs-externals": "^0.1.1"
|
||||
"vite-plugin-commonjs-externals": "^0.1.1",
|
||||
"webtorrent": "^1.8.5"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [
|
||||
|
|
|
|||
12
src/index.js
12
src/index.js
|
|
@ -8,8 +8,8 @@ let mainWindow
|
|||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
width: 1600,
|
||||
height: 900,
|
||||
backgroundColor: '#2e2c29',
|
||||
autoHideMenuBar: true,
|
||||
experimentalFeatures: true,
|
||||
|
|
@ -20,10 +20,10 @@ function createWindow () {
|
|||
enableRemoteModule: true,
|
||||
backgroundThrottling: false
|
||||
},
|
||||
icon: path.join(__dirname, 'public/favicon.png'),
|
||||
icon: path.join(__dirname, '/renderer/public/favicon.ico'),
|
||||
show: false
|
||||
})
|
||||
// mainWindow.removeMenu()
|
||||
mainWindow.removeMenu()
|
||||
|
||||
// This block of code is intended for development purpose only.
|
||||
// Delete this entire block of code when you are ready to package the application.
|
||||
|
|
@ -40,8 +40,8 @@ function createWindow () {
|
|||
// loadURL(mainWindow);
|
||||
|
||||
// Open the DevTools and also disable Electron Security Warning.
|
||||
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true;
|
||||
// mainWindow.webContents.openDevTools();
|
||||
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true
|
||||
mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Miru</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
<title>Miru</title>
|
||||
|
||||
<link rel='icon' href='/favicon.ico'>
|
||||
<link href='/lib/halfmoon.css' rel='stylesheet'>
|
||||
<link href="/lib/Material-Icons.css" rel="stylesheet">
|
||||
|
||||
<script defer src='/lib/halfmoon.js'></script>
|
||||
<script defer type="module" src="/src/main.js"></script>
|
||||
</head>
|
||||
|
||||
<body class='with-custom-webkit-scrollbars with-custom-css-scrollbars dark-mode bg-very-dark'>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
src/renderer/public/anitomyscript.wasm
Normal file
BIN
src/renderer/public/anitomyscript.wasm
Normal file
Binary file not shown.
23
src/renderer/public/lib/Material-Icons.css
Normal file
23
src/renderer/public/lib/Material-Icons.css
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* fallback */
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(./flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
BIN
src/renderer/public/lib/Roboto.ttf
Normal file
BIN
src/renderer/public/lib/Roboto.ttf
Normal file
Binary file not shown.
BIN
src/renderer/public/lib/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
Normal file
BIN
src/renderer/public/lib/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
Normal file
Binary file not shown.
11
src/renderer/public/lib/halfmoon.css
Normal file
11
src/renderer/public/lib/halfmoon.css
Normal file
File diff suppressed because one or more lines are too long
11
src/renderer/public/lib/halfmoon.js
Normal file
11
src/renderer/public/lib/halfmoon.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/renderer/public/lib/subtitles-octopus-worker.data
Normal file
BIN
src/renderer/public/lib/subtitles-octopus-worker.data
Normal file
Binary file not shown.
638
src/renderer/public/lib/subtitles-octopus-worker.js
Normal file
638
src/renderer/public/lib/subtitles-octopus-worker.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/renderer/public/lib/subtitles-octopus-worker.wasm
Normal file
BIN
src/renderer/public/lib/subtitles-octopus-worker.wasm
Normal file
Binary file not shown.
|
|
@ -1,66 +1,9 @@
|
|||
<script>
|
||||
import logo from '@/assets/svelte.png'
|
||||
import Counter from '@/lib/Counter.svelte'
|
||||
const process = require('process')
|
||||
// import anitomyscript from 'anitomyscript'
|
||||
// const WebTorrent = require('webtorrent')
|
||||
// const wt = new WebTorrent()
|
||||
// console.log(wt)
|
||||
// anitomyscript('[GJM] Irozuku Sekai no Ashita kara - 08 [F0C587E8].mkv').then(console.log)
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<img src={logo} alt="Svelte Logo" />
|
||||
<h1>Hello world! {process.cwd()}</h1>
|
||||
|
||||
<Counter />
|
||||
|
||||
<p>
|
||||
Visit <a href="https://svelte.dev">svelte.dev</a> to learn how to build Svelte
|
||||
apps.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Check out <a href="https://github.com/sveltejs/kit#readme">SvelteKit</a> for
|
||||
the officially supported framework, also powered by Vite!
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
main {
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 16rem;
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-transform: uppercase;
|
||||
font-size: 4rem;
|
||||
font-weight: 100;
|
||||
line-height: 1.1;
|
||||
margin: 2rem auto;
|
||||
max-width: 14rem;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 14rem;
|
||||
margin: 1rem auto;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
h1 {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
<script>
|
||||
let count = 0
|
||||
const increment = () => {
|
||||
count += 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={increment}>
|
||||
Clicks: {count}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
button {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 1em 2em;
|
||||
color: #ff3e00;
|
||||
background-color: rgba(255, 62, 0, 0.1);
|
||||
border-radius: 2em;
|
||||
border: 2px solid rgba(255, 62, 0, 0);
|
||||
outline: none;
|
||||
width: 200px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
border: 2px solid #ff3e00;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: rgba(255, 62, 0, 0.2);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import App from './App.svelte'
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')
|
||||
target: document.body
|
||||
})
|
||||
|
||||
export default app
|
||||
|
|
|
|||
431
src/renderer/src/modules/subtitles-octopus.js
Normal file
431
src/renderer/src/modules/subtitles-octopus.js
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
/* eslint-env browser */
|
||||
|
||||
// test ImageData constructor
|
||||
; (() => {
|
||||
if (typeof ImageData.prototype.constructor === 'function') {
|
||||
try {
|
||||
// try actually calling ImageData, as on some browsers it's reported
|
||||
// as existing but calling it errors out as "TypeError: Illegal constructor"
|
||||
return new ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1)
|
||||
} catch (e) {
|
||||
console.log('detected that ImageData is not constructable despite browser saying so')
|
||||
}
|
||||
}
|
||||
|
||||
const ctx = document.createElement('canvas').getContext('2d')
|
||||
|
||||
window.ImageData = (data, width, height) => {
|
||||
const imageData = ctx.createImageData(width, height)
|
||||
if (data) imageData.data.set(data)
|
||||
return imageData
|
||||
}
|
||||
})()
|
||||
|
||||
export default class SubtitlesOctopus extends EventTarget {
|
||||
constructor (options = {}) {
|
||||
super()
|
||||
if (!window.Worker) {
|
||||
this.destroy('Worker not supported')
|
||||
}
|
||||
// blending mode, 'js' for hardware acceleration [if browser supports it], 'wasm' for devices/browsers that don't benetit from hardware acceleration
|
||||
const _blendMode = options.blendMode || 'wasm'
|
||||
// drop frames when under heavy load, good for website performance
|
||||
const _asyncRender = typeof createImageBitmap !== 'undefined' && (options.asyncRender ?? true)
|
||||
// render in worker, rather than on main thread
|
||||
const _offscreenRender = typeof OffscreenCanvas !== 'undefined' && (options.offscreenRender ?? true)
|
||||
// render subs as video player renders video frames, rather than "predicting" the time with events
|
||||
this._onDemandRender = 'requestVideoFrameCallback' in HTMLVideoElement.prototype && (options.onDemandRender ?? true)
|
||||
|
||||
this.timeOffset = options.timeOffset || 0
|
||||
this._video = options.video // HTML video element (optional if canvas specified)
|
||||
this._canvasParent = null // HTML canvas parent element
|
||||
if (this._video) {
|
||||
this._canvasParent = document.createElement('div')
|
||||
this._canvasParent.className = 'subtitles-octopus'
|
||||
this._canvasParent.style.position = 'relative'
|
||||
|
||||
if (this._video.nextSibling) {
|
||||
this._video.parentNode.insertBefore(this._canvasParent, this._video.nextSibling)
|
||||
} else {
|
||||
this._video.parentNode.appendChild(this._canvasParent)
|
||||
}
|
||||
} else if (!this._canvas) {
|
||||
this.destroy('Don\'t know where to render: you should give video or canvas in options.')
|
||||
}
|
||||
|
||||
this._canvas = options.canvas || document.createElement('canvas') // HTML canvas element (optional if video specified)
|
||||
this._canvas.style.display = 'block'
|
||||
this._canvas.style.position = 'absolute'
|
||||
this._canvas.style.pointerEvents = 'none'
|
||||
this._canvasParent.appendChild(this._canvas)
|
||||
|
||||
this._bufferCanvas = document.createElement('canvas')
|
||||
this._bufferCtx = this._bufferCanvas.getContext('2d')
|
||||
|
||||
this._canvasctrl = _offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas
|
||||
this._ctx = !_offscreenRender && this._canvasctrl.getContext('2d')
|
||||
|
||||
this._lastRenderTime = 0 // Last time we got some frame from worker
|
||||
this.debug = !!options.debug // When debug enabled, some performance info printed in console.
|
||||
|
||||
this.prescaleFactor = options.prescaleFactor || 1.0
|
||||
this.prescaleHeightLimit = options.prescaleHeightLimit || 1080
|
||||
this.maxRenderHeight = options.maxRenderHeight || 0 // 0 - no limit
|
||||
|
||||
this._worker = new Worker(options.workerUrl || 'subtitles-octopus-worker.js')
|
||||
this._worker.onmessage = e => this._onmessage(e)
|
||||
this._worker.onerror = e => this._error(e)
|
||||
|
||||
// test alpha bug
|
||||
const canvas2 = document.createElement('canvas')
|
||||
const ctx2 = canvas2.getContext('2d')
|
||||
|
||||
// test for alpha bug, where e.g. WebKit can render a transparent pixel
|
||||
// (with alpha == 0) as non-black which then leads to visual artifacts
|
||||
this._bufferCanvas.width = 1
|
||||
this._bufferCanvas.height = 1
|
||||
this._bufferCtx.clearRect(0, 0, 1, 1)
|
||||
ctx2.clearRect(0, 0, 1, 1)
|
||||
const prePut = ctx2.getImageData(0, 0, 1, 1).data
|
||||
this._bufferCtx.putImageData(new ImageData(new Uint8ClampedArray([0, 255, 0, 0]), 1, 1), 0, 0)
|
||||
ctx2.drawImage(this._bufferCanvas, 0, 0)
|
||||
const postPut = ctx2.getImageData(0, 0, 1, 1).data
|
||||
this.hasAlphaBug = prePut[1] !== postPut[1]
|
||||
if (this.hasAlphaBug) console.log('Detected a browser having issue with transparent pixels, applying workaround')
|
||||
|
||||
this._worker.postMessage({
|
||||
target: 'init',
|
||||
asyncRender: _asyncRender,
|
||||
width: this._canvas.width,
|
||||
height: this._canvas.height,
|
||||
URL: document.URL,
|
||||
currentScript: options.workerUrl || 'subtitles-octopus-worker.js', // Link to WebAssembly worker
|
||||
preMain: true,
|
||||
blendMode: _blendMode,
|
||||
subUrl: options.subUrl, // Link to sub file (optional if subContent specified)
|
||||
subContent: options.subContent || null, // Sub content (optional if subUrl specified)
|
||||
fonts: options.fonts || [], // Array with links to fonts used in sub (optional)
|
||||
availableFonts: options.availableFonts || [], // Object with all available fonts (optional). Key is font name in lower case, value is link: {"arial": "/font1.ttf"}
|
||||
debug: this.debug,
|
||||
targetFps: options.targetFps,
|
||||
libassMemoryLimit: options.libassMemoryLimit || 0, // set libass bitmap cache memory limit in MiB (approximate)
|
||||
libassGlyphLimit: options.libassGlyphLimit || 0, // set libass glyph cache memory limit in MiB (approximate),
|
||||
hasAlphaBug: this.hasAlphaBug
|
||||
})
|
||||
if (_offscreenRender === true) this.sendMessage('offscreenCanvas', null, [this._canvasctrl])
|
||||
this.setVideo(options.video)
|
||||
if (this._onDemandRender) this._video.requestVideoFrameCallback(this._demandRender.bind(this))
|
||||
}
|
||||
|
||||
resize (width = 0, height = 0, top = 0, left = 0) {
|
||||
let videoSize = null
|
||||
if ((!width || !height) && this._video) {
|
||||
videoSize = this._getVideoPosition()
|
||||
const newsize = this._computeCanvasSize(videoSize.width || 0 * (window.devicePixelRatio || 1), videoSize.height || 0 * (window.devicePixelRatio || 1))
|
||||
width = newsize.width
|
||||
height = newsize.height
|
||||
top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top)
|
||||
left = videoSize.x
|
||||
}
|
||||
|
||||
if (this._canvas.style.top !== top || this._canvas.style.left !== left) {
|
||||
if (videoSize != null) {
|
||||
this._canvas.style.top = top + 'px'
|
||||
this._canvas.style.left = left + 'px'
|
||||
this._canvas.style.width = videoSize.width + 'px'
|
||||
this._canvas.style.height = videoSize.height + 'px'
|
||||
}
|
||||
if (!(this._canvasctrl.width === width && this._canvasctrl.height === height)) {
|
||||
// only re-paint if dimensions actually changed
|
||||
// dont spam re-paints like crazy when re-sizing with animations, but still update instantly without them
|
||||
if (this._resizeTimeoutBuffer) {
|
||||
clearTimeout(this._resizeTimeoutBuffer)
|
||||
this._resizeTimeoutBuffer = setTimeout(() => {
|
||||
this._resizeTimeoutBuffer = undefined
|
||||
this._canvasctrl.width = width
|
||||
this._canvasctrl.height = height
|
||||
this.sendMessage('canvas', { width, height })
|
||||
}, 50)
|
||||
} else {
|
||||
this._canvasctrl.width = width
|
||||
this._canvasctrl.height = height
|
||||
this.sendMessage('canvas', { width, height })
|
||||
this._resizeTimeoutBuffer = setTimeout(() => {
|
||||
this._resizeTimeoutBuffer = undefined
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getVideoPosition () {
|
||||
const videoRatio = this._video.videoWidth / this._video.videoHeight
|
||||
const { offsetWidth, offsetHeight } = this._video
|
||||
const elementRatio = offsetWidth / offsetHeight
|
||||
let width = offsetWidth
|
||||
let height = offsetHeight
|
||||
if (elementRatio > videoRatio) {
|
||||
width = Math.floor(offsetHeight * videoRatio)
|
||||
} else {
|
||||
height = Math.floor(offsetWidth / videoRatio)
|
||||
}
|
||||
|
||||
const x = (offsetWidth - width) / 2
|
||||
const y = (offsetHeight - height) / 2
|
||||
|
||||
return { width, height, x, y }
|
||||
}
|
||||
|
||||
_computeCanvasSize (width = 0, height = 0) {
|
||||
const scalefactor = this.prescaleFactor <= 0 ? 1.0 : this.prescaleFactor
|
||||
|
||||
if (height <= 0 || width <= 0) {
|
||||
width = 0
|
||||
height = 0
|
||||
} else {
|
||||
const sgn = scalefactor < 1 ? -1 : 1
|
||||
let newH = height
|
||||
if (sgn * newH * scalefactor <= sgn * this.prescaleHeightLimit) {
|
||||
newH *= scalefactor
|
||||
} else if (sgn * newH < sgn * this.prescaleHeightLimit) {
|
||||
newH = this.prescaleHeightLimit
|
||||
}
|
||||
|
||||
if (this.maxRenderHeight > 0 && newH > this.maxRenderHeight) newH = this.maxRenderHeight
|
||||
|
||||
width *= newH / height
|
||||
height = newH
|
||||
}
|
||||
|
||||
return { width, height }
|
||||
}
|
||||
|
||||
_timeupdate ({ type }) {
|
||||
const eventmap = {
|
||||
seeking: true,
|
||||
waiting: true,
|
||||
playing: false
|
||||
}
|
||||
const playing = eventmap[type]
|
||||
if (playing != null) this._playstate = playing
|
||||
this.setCurrentTime(this._video.paused || this._playstate, this._video.currentTime + this.timeOffset)
|
||||
}
|
||||
|
||||
setVideo (video) {
|
||||
if (video instanceof HTMLVideoElement) {
|
||||
this._removeListeners()
|
||||
this._video = video
|
||||
if (this._onDemandRender !== true) {
|
||||
this._playstate = video.paused
|
||||
|
||||
video.addEventListener('timeupdate', e => this._timeupdate(e), false)
|
||||
video.addEventListener('progress', e => this._timeupdate(e), false)
|
||||
video.addEventListener('waiting', e => this._timeupdate(e), false)
|
||||
video.addEventListener('seeking', e => this._timeupdate(e), false)
|
||||
video.addEventListener('playing', e => this._timeupdate(e), false)
|
||||
video.addEventListener('ratechange', e => this.setRate(e), false)
|
||||
}
|
||||
if (video.videoWidth > 0) {
|
||||
this.resize()
|
||||
} else {
|
||||
video.addEventListener('loadedmetadata', () => this.resize(0, 0, 0, 0), false)
|
||||
}
|
||||
// Support Element Resize Observer
|
||||
if (typeof ResizeObserver !== 'undefined') {
|
||||
if (!this._ro) this._ro = new ResizeObserver(() => this.resize(0, 0, 0, 0))
|
||||
this._ro.observe(video)
|
||||
}
|
||||
} else {
|
||||
this._error('Video element invalid!')
|
||||
}
|
||||
}
|
||||
|
||||
runBenchmark () {
|
||||
this.sendMessage('runBenchmark')
|
||||
}
|
||||
|
||||
setTrackByUrl (url) {
|
||||
this.sendMessage('setTrackByUrl', { url })
|
||||
}
|
||||
|
||||
setTrack (content) {
|
||||
this.sendMessage('setTrack', { content })
|
||||
}
|
||||
|
||||
freeTrack () {
|
||||
this.sendMessage('freeTrack')
|
||||
}
|
||||
|
||||
setIsPaused (isPaused) {
|
||||
this.sendMessage('video', { isPaused })
|
||||
}
|
||||
|
||||
setRate (rate) {
|
||||
this.sendMessage('video', { rate })
|
||||
}
|
||||
|
||||
setCurrentTime (isPaused, currentTime, rate) {
|
||||
this.sendMessage('video', { isPaused, currentTime, rate })
|
||||
}
|
||||
|
||||
createEvent (event) {
|
||||
this.sendMessage('createEvent', { event })
|
||||
}
|
||||
|
||||
setEvent (event, index) {
|
||||
this.sendMessage('setEvent', { event, index })
|
||||
}
|
||||
|
||||
removeEvent (index) {
|
||||
this.sendMessage('removeEvent', { index })
|
||||
}
|
||||
|
||||
getEvents (onError, onSuccess) {
|
||||
this._fetchFromWorker({
|
||||
target: 'getEvents'
|
||||
}, onError, ({ events }) => {
|
||||
onSuccess(events)
|
||||
})
|
||||
}
|
||||
|
||||
createStyle (style) {
|
||||
this.sendMessage('createStyle', { style })
|
||||
}
|
||||
|
||||
setStyle (event, index) {
|
||||
this.sendMessage('setStyle', { event, index })
|
||||
}
|
||||
|
||||
removeStyle (index) {
|
||||
this.sendMessage('removeStyle', { index })
|
||||
}
|
||||
|
||||
getStyles (onError, onSuccess) {
|
||||
this._fetchFromWorker({
|
||||
target: 'get-styles'
|
||||
}, onError, ({ styles }) => {
|
||||
onSuccess(styles)
|
||||
})
|
||||
}
|
||||
|
||||
_demandRender (now, metadata) {
|
||||
if (this._destroyed) return null
|
||||
this.sendMessage('demand', { time: metadata.mediaTime + this.timeOffset })
|
||||
this._video.requestVideoFrameCallback(this._demandRender.bind(this))
|
||||
}
|
||||
|
||||
_render (data) {
|
||||
this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height)
|
||||
for (const image of data.images) {
|
||||
if (image.buffer) {
|
||||
if (data.async) {
|
||||
this._ctx.drawImage(image.buffer, image.x, image.y)
|
||||
} else {
|
||||
this._bufferCanvas.width = image.w
|
||||
this._bufferCanvas.height = image.h
|
||||
this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(image.buffer)), image.w, image.h), 0, 0)
|
||||
this._ctx.drawImage(this._bufferCanvas, image.x, image.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_fixAlpha (uint8) {
|
||||
if (this.hasAlphaBug) {
|
||||
for (let j = 3; j < uint8.length; j += 4) {
|
||||
uint8[j] = uint8[j] > 1 ? uint8[j] : 1
|
||||
}
|
||||
}
|
||||
return uint8
|
||||
}
|
||||
|
||||
_ready () {
|
||||
this.dispatchEvent(new CustomEvent('ready'))
|
||||
}
|
||||
|
||||
sendMessage (target, data = {}, transferable) {
|
||||
if (transferable) {
|
||||
this._worker.postMessage({
|
||||
target,
|
||||
transferable,
|
||||
...data
|
||||
}, [...transferable])
|
||||
} else {
|
||||
this._worker.postMessage({
|
||||
target,
|
||||
...data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_fetchFromWorker (workerOptions, onError, onSuccess) {
|
||||
try {
|
||||
const target = workerOptions.target
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('Error: Timeout while try to fetch ' + target))
|
||||
}, 5000)
|
||||
|
||||
const resolve = ({ data }) => {
|
||||
if (data.target === target) {
|
||||
onSuccess(data)
|
||||
this._worker.removeEventListener('message', resolve)
|
||||
this._worker.removeEventListener('error', reject)
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
const reject = function (event) {
|
||||
onError(event)
|
||||
this._worker.removeEventListener('message', resolve)
|
||||
this._worker.removeEventListener('error', reject)
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
this._worker.addEventListener('message', resolve)
|
||||
this._worker.addEventListener('error', reject)
|
||||
|
||||
this._worker.postMessage(workerOptions)
|
||||
} catch (error) {
|
||||
this._error(error)
|
||||
}
|
||||
}
|
||||
|
||||
_console ({ content, command }) {
|
||||
console[command].apply(console, JSON.parse(content))
|
||||
}
|
||||
|
||||
_onmessage ({ data }) {
|
||||
if (this['_' + data.target]) this['_' + data.target](data)
|
||||
}
|
||||
|
||||
_error (err) {
|
||||
this.dispatchEvent(new CustomEvent('error', { detail: err }))
|
||||
throw err
|
||||
}
|
||||
|
||||
_removeListeners () {
|
||||
if (this._video) {
|
||||
if (this._ro) this._ro.unobserve(this._video)
|
||||
this._video.removeEventListener('timeupdate', this._timeupdate)
|
||||
this._video.removeEventListener('progress', this._timeupdate)
|
||||
this._video.removeEventListener('waiting', this._timeupdate)
|
||||
this._video.removeEventListener('seeking', this._timeupdate)
|
||||
this._video.removeEventListener('playing', this._timeupdate)
|
||||
this._video.removeEventListener('ratechange', this.setRate)
|
||||
}
|
||||
}
|
||||
|
||||
destroy (err) {
|
||||
if (err) this._error(err)
|
||||
if (this._video) this._video.parentNode.removeChild(this._canvasParent)
|
||||
this._destroyed = true
|
||||
this._removeListeners()
|
||||
this.sendMessage('destroy')
|
||||
this._worker.terminate()
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof exports !== 'undefined' && typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = SubtitlesOctopus
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ const commonjsPackages = [
|
|||
'electron/common',
|
||||
'electron/renderer',
|
||||
'original-fs',
|
||||
'webtorrent',
|
||||
...builtinModules
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue