Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
take this project seriously
Browse files Browse the repository at this point in the history
  • Loading branch information
ThaUnknown committed Jul 28, 2021
1 parent 170ee5c commit e75b34c
Show file tree
Hide file tree
Showing 12 changed files with 669 additions and 493 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
package-lock.json
27 changes: 27 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// I can't get this to work https://github.com/remorses/esbuild-plugins/issues/8 will switch to esbuild if I find a fix
// type module in package.json
// "@esbuild-plugins/node-globals-polyfill": "^0.1.0",
// "@esbuild-plugins/node-modules-polyfill": "^0.1.1",
// "esbuild": "^0.12.16",
// "esbuild-plugin-node-polyfills": "^1.0.2",
import NodeModulesPolyfillPlugin from '@esbuild-plugins/node-modules-polyfill'
import NodeGlobalsPolyfillPlugin from '@esbuild-plugins/node-globals-polyfill'
import ESBuild from 'esbuild'

ESBuild.build({
plugins: [NodeModulesPolyfillPlugin.default(),
NodeGlobalsPolyfillPlugin.default({
process: true,
buffer: true
})],
entryPoints: ['index.js'],
bundle: true,
sourcemap: true,
format: 'esm',
target: ['esnext'],
platform: 'browser',
outfile: 'out.js',
define: {
global: 'globalThis'
}
}).catch(() => process.exit(1))
3 changes: 3 additions & 0 deletions dist/bundle.js

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions dist/bundle.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <[email protected]> <http://feross.org>
* @license MIT
*/

/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/

/*!
* range-parser
* Copyright(c) 2012-2014 TJ Holowaychuk
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/

/*! bittorrent-protocol. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! blob-to-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! cache-chunk-store. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! create-torrent. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! https://mths.be/punycode v1.3.2 by @mathias */

/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! immediate-chunk-store. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! lt_donthave. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! magnet-uri. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! mediasource. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! multistream. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! parse-torrent. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! render-media. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! run-parallel-limit. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! run-parallel. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! simple-concat. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! simple-get. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! simple-peer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! simple-websocket. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! stream-to-blob-url. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! stream-to-blob. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! stream-with-known-length-to-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! torrent-discovery. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! torrent-piece. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! ut_metadata. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */

/*! webtorrent. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */
1 change: 1 addition & 0 deletions dist/bundle.js.map

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@

General motivation is to get rid of all duplicate RAM usage and reduce CPU usage, provide better/working alternatives for existing WebTorrent functionality, and provide any features you'd see in a modern video player, ex: YouTube's.

# Install
```
npm install https://github.com/ThaUnknown/webtorrent-player
```
Then
```js
import WebTorrentPlayer from 'webtorrent-player'

new WebTorrentPlayer({ opts })
```
Or you can include the bundle file [see index.html for an example]

Note: the user needs to include the `/sw.js` and `/lib/subtitles-octopus-worker.*` since they are imported externally by the browser when registering workers.
# Features
- Support for ludicrous file sizes [upwards of 100GB]*
- Browser based support for streaming video containers, codecs and multi-audio*
Expand Down Expand Up @@ -92,13 +105,3 @@ Multi-audio tracks is only supported in after enabling flags:
Offline storage also uses IDB which on Firefox has a 2.14GB limit, meaning you can't actually store all that much when using Firefox, this isn't an issue on any Chromium browser.

This feature allows you to download torrents across multiple sessions, and even play them back without any internet connection.
# Dependencies
- [WebTorrent](https://github.com/webtorrent/webtorrent)
- [webtorrent-server-brower](https://github.com/jimmywarting/webtorrent-server-browser) [Included in lib]
- [range-parser](https://github.com/jshttp/range-parser) [Included in lib]
- [matroska-subtitles](https://github.com/mathiasvr/matroska-subtitles)
- [SubtitlesOctopus](https://github.com/Dador/JavascriptSubtitlesOctopus/) [Included in lib]
- [indexeddb-chunk-store](https://github.com/xuset/indexeddb-chunk-store)
- [material icons](https://material.io/resources/icons/)

Include these in any way you want.
11 changes: 3 additions & 8 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebTorrentPlayer Example</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="webtorrent-player.css" rel="stylesheet">
<link href="lib/webtorrent-player.css" rel="stylesheet">
<style>
:root {
font-size: 12px;
Expand Down Expand Up @@ -122,12 +122,7 @@
<input type="text" id='magn' placeholder="Magnet/Torrent">
<button type="button" onclick=client.playTorrent(magn.value)>Play Magnet/Torrent</button>

<script src="https://cdn.jsdelivr.net/npm/matroska-subtitles@latest/dist/matroska-subtitles.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/indexeddb-chunk-store@latest/idbchunkstore.min.js"></script>
<script src="lib/range-parser.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/webtorrent.min.js"></script>
<script src="lib/subtitles-octopus.js"></script>
<script src="index.js"></script>
<script src="dist/bundle.js"></script>
<script>
const playerControls = {}
for (const item of document.getElementsByClassName('ctrl')) {
Expand All @@ -137,7 +132,7 @@
playerControls[item.dataset.name] = [playerControls[item.dataset.name], item]
}
}
const client = new WebTorrentPlayer({
const client = new WebTorrentPlayer.default({
controls: playerControls,
video: video,
player: player,
Expand Down
61 changes: 35 additions & 26 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
/* eslint-env browser */
/* global MediaMetadata, navNowPlaying */
import WebTorrent from 'webtorrent'
import rangeParser from 'range-parser'
import { SubtitleParser, SubtitleStream } from 'matroska-subtitles'
import HybridChunkStore from 'hybrid-chunk-store'
import mime from 'mime'
import SubtitlesOctopus from './lib/subtitles-octopus.js'

const keepAliveTime = 20000
const units = [' B', ' kB', ' MB', ' GB', ' TB']
class WebTorrentPlayer extends WebTorrent {

export default class WebTorrentPlayer extends WebTorrent {
constructor (options = {}) {
super(options.WebTorrentOpts)

this.storeOpts = options.storeOpts || {}

this.scope = location.pathname.substr(0, location.pathname.lastIndexOf('/') + 1)
this.worker = navigator.serviceWorker.controller
if (!this.worker) {
Expand Down Expand Up @@ -323,7 +334,7 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
headers: {
// Support range-requests
'Accept-Ranges': 'bytes',
'Content-Type': file._getMimeType(),
'Content-Type': mime.getType(file.name),
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
Expires: '0'
},
Expand Down Expand Up @@ -354,7 +365,7 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
// parser is really a passthrough mkv stream now
const stream = file.createReadStream(range)
if (file.name.endsWith('.mkv') && !this.subtitleData.parsed) {
this.subtitleData.stream = new MatroskaSubtitles.SubtitleStream(this.subtitleData.stream)
this.subtitleData.stream = new SubtitleStream(this.subtitleData.stream)
this.handleSubtitleParser(this.subtitleData.stream)
stream.pipe(this.subtitleData.stream)
}
Expand Down Expand Up @@ -389,7 +400,7 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
this.nowPlaying = (opts.media && (this.videoFiles.length === 1 || (opts.forceMedia && opts.file))) ? opts.media : this.resolveFileMedia ? await this.resolveFileMedia({ fileName: this.currentFile.name, method: 'SearchName' }) : undefined

if (this.nowPlaying) {
navNowPlaying.classList.remove('d-none') // TODO: fix
if (navNowPlaying) navNowPlaying.classList.remove('d-none')

const episodeInfo = [this.nowPlaying.episodeNumber, this.nowPlaying.episodeTitle].filter(s => s).join(' - ')

Expand Down Expand Up @@ -452,7 +463,7 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
this.changeControlsIcon('selectCaptions', '')
this.changeControlsIcon('selectAudio', '')
if (this.controls.openPlaylist) this.controls.openPlaylist.setAttribute('disabled', '')
navNowPlaying.classList.add('d-none') // TODO: fix
if (navNowPlaying) navNowPlaying.classList.add('d-none')
if ('mediaSession' in navigator) navigator.mediaSession.metadata = undefined
this.fps = 23.976
}
Expand Down Expand Up @@ -548,13 +559,13 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
}, 200)
}

async togglePopout () {
togglePopout () {
if (this.video.readyState) {
if (!(this.burnIn && this.subtitleData.renderer)) {
this.video !== document.pictureInPictureElement ? await this.video.requestPictureInPicture() : await document.exitPictureInPicture()
this.video !== document.pictureInPictureElement ? this.video.requestPictureInPicture() : document.exitPictureInPicture()
} else {
if (document.pictureInPictureElement && !document.pictureInPictureElement.id) { // only exit if pip is the custom one, else overwrite existing pip with custom
await document.exitPictureInPicture()
document.exitPictureInPicture()
} else {
const canvas = document.createElement('canvas')
const canvasVideo = document.createElement('video')
Expand Down Expand Up @@ -852,10 +863,10 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
subtitle.text || ''
}

async parseSubtitles (file) { // parse subtitles fully after a download is finished
return new Promise((resolve, reject) => {
parseSubtitles (file) { // parse subtitles fully after a download is finished
return new Promise((resolve) => {
if (file.name.endsWith('.mkv')) {
let parser = new MatroskaSubtitles.SubtitleParser()
let parser = new SubtitleParser()
this.handleSubtitleParser(parser, true)
parser.on('finish', () => {
console.log('Sub parsing finished', this.toTS((performance.now() - t0) / 1000))
Expand Down Expand Up @@ -1020,16 +1031,17 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
}
}

async postDownload () {
postDownload () {
this.onDownloadDone(this.currentFile)
await this.parseSubtitles(this.currentFile)
if (this.generateThumbnails) {
this.finishThumbnails(this.video.src)
}
this.parseSubtitles(this.currentFile).then(() => {
if (this.generateThumbnails) {
this.finishThumbnails(this.video.src)
}
})
}

async playTorrent (torrentID, opts = {}) { // TODO: clean this up
const handleTorrent = async (torrent, opts) => {
playTorrent (torrentID, opts = {}) { // TODO: clean this up
const handleTorrent = (torrent, opts) => {
torrent.on('noPeers', () => {
if (this.onWarn) this.onWarn('no peers', torrent)
})
Expand Down Expand Up @@ -1057,13 +1069,9 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
} else if (this.get(torrentID)) {
handleTorrent(this.get(torrentID), opts)
} else {
const store = opts.expectedSize && performance.memory
? (performance.memory.jsHeapSizeLimit - performance.memory.totalJSHeapSize) * 0.8 < this.getBytes(opts.expectedSize)
? IdbChunkStore
: undefined
: IdbChunkStore
this.add(torrentID, {
store: store,
storeOpts: this.storeOpts,
store: HybridChunkStore,
announce: this.tracker.announce || [
'wss://tracker.openwebtorrent.com',
'wss://tracker.sloppyta.co:443/announce',
Expand All @@ -1084,14 +1092,15 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
// add torrent for offline download
offlineDownload (torrentID) {
const torrent = this.add(torrentID, {
store: IdbChunkStore,
storeOpts: this.storeOpts,
store: HybridChunkStore,
announce: this.tracker.announce || [
'wss://tracker.openwebtorrent.com',
'wss://tracker.sloppyta.co:443/announce',
'wss://hub.bugout.link:443/announce'
]
})
torrent.on('metadata', async () => {
torrent.on('metadata', () => {
if (!this.offlineTorrents[torrent.infoHash]) {
this.offlineTorrents[torrent.infoHash] = Array.from(torrent.torrentFile)
localStorage.setItem('offlineTorrents', JSON.stringify(this.offlineTorrents))
Expand Down
9 changes: 0 additions & 9 deletions lib/range-parser.js

This file was deleted.

Loading

0 comments on commit e75b34c

Please sign in to comment.