diff options
author | Feross Aboukhadijeh <feross@feross.org> | 2015-12-28 16:13:45 +0300 |
---|---|---|
committer | Feross Aboukhadijeh <feross@feross.org> | 2015-12-28 16:13:45 +0300 |
commit | 0210d5592652407d135e8b01c9d8ca1d87976c68 (patch) | |
tree | f07321b54e0e2b76cbab6ac4213fa2e937d36aea /lib | |
parent | 8a551101fc69f8f3859e15379e6dd68f56a54b75 (diff) | |
parent | 41dc75b8e7420297043d2fb294ce334a498498df (diff) |
Merge pull request #425 from valeriangalliat/feature/render-to
[Feature proposition] Allow to render a torrent into an existing element
Diffstat (limited to 'lib')
-rw-r--r-- | lib/append-to.js | 169 | ||||
-rw-r--r-- | lib/file.js | 12 | ||||
-rw-r--r-- | lib/render-to.js | 17 | ||||
-rw-r--r-- | lib/render.js | 150 |
4 files changed, 197 insertions, 151 deletions
diff --git a/lib/append-to.js b/lib/append-to.js index c68e3f9..672d711 100644 --- a/lib/append-to.js +++ b/lib/append-to.js @@ -1,26 +1,6 @@ -var debug = require('debug')('webtorrent:append-to') -var MediaSourceStream = require('mediasource') -var path = require('path') -var videostream = require('videostream') - -var VIDEOSTREAM_EXTS = [ '.mp4', '.m4v', '.m4a' ] - -var MEDIASOURCE_VIDEO_EXTS = [ '.mp4', '.m4v', '.webm' ] -var MEDIASOURCE_AUDIO_EXTS = [ '.m4a', '.mp3' ] -var MEDIASOURCE_EXTS = MEDIASOURCE_VIDEO_EXTS.concat(MEDIASOURCE_AUDIO_EXTS) - -var AUDIO_EXTS = [ '.wav', '.aac', '.ogg', '.oga' ] -var IMAGE_EXTS = [ '.jpg', '.jpeg', '.png', '.gif', '.bmp' ] -var IFRAME_EXTS = [ '.css', '.html', '.js', '.md', '.pdf', '.txt' ] - -var MediaSource = typeof window !== 'undefined' && window.MediaSource +var render = require('./render') module.exports = function appendTo (file, rootElem, cb) { - if (!cb) cb = noop - var elem - var extname = path.extname(file.name).toLowerCase() - var currentTime = 0 - if (rootElem && (rootElem.nodeName === 'VIDEO' || rootElem.nodeName === 'AUDIO')) { throw new Error( 'Invalid video/audio node argument. Argument must be root element that ' + @@ -28,141 +8,28 @@ module.exports = function appendTo (file, rootElem, cb) { ) } - if (MEDIASOURCE_EXTS.indexOf(extname) >= 0) appendToMediaSource() - else if (AUDIO_EXTS.indexOf(extname) >= 0) appendToAudio() - else if (IMAGE_EXTS.indexOf(extname) >= 0) appendToImage() - else if (IFRAME_EXTS.indexOf(extname) >= 0) appendToIframe() - else nextTick(cb, new Error('Unsupported file type "' + extname + '": Cannot append to DOM')) - - function appendToMediaSource () { - if (!MediaSource) { - return nextTick(cb, new Error( - 'Video/audio streaming is not supported in your browser. You can still share ' + - 'or download ' + file.name + ' (once it\'s fully downloaded). Use Chrome for ' + - 'MediaSource support.' - )) - } - - var tagName = MEDIASOURCE_VIDEO_EXTS.indexOf(extname) >= 0 ? 'video' : 'audio' - - if (VIDEOSTREAM_EXTS.indexOf(extname) >= 0) useVideostream() - else useMediaSource() - - function useVideostream () { - debug('Use `videostream` package for ' + file.name) - createElem() - elem.addEventListener('error', fallbackToMediaSource) - elem.addEventListener('playing', onPlaying) - videostream(file, elem) - } - - function useMediaSource () { - debug('Use MediaSource API for ' + file.name) - createElem() - elem.addEventListener('error', fallbackToBlobURL) - elem.addEventListener('playing', onPlaying) - - file.createReadStream().pipe(new MediaSourceStream(elem, { extname: extname })) - if (currentTime) elem.currentTime = currentTime - } - - function useBlobURL () { - debug('Use Blob URL for ' + file.name) - createElem() - elem.addEventListener('error', fatalError) - elem.addEventListener('playing', onPlaying) - file.getBlobURL(function (err, url) { - if (err) return fatalError(err) - elem.src = url - if (currentTime) elem.currentTime = currentTime - }) - } - - function fallbackToMediaSource (err) { - debug('videostream error: fallback to MediaSource API: %o', err.message || err) - elem.removeEventListener('error', fallbackToMediaSource) - elem.removeEventListener('playing', onPlaying) - - useMediaSource() - } - - function fallbackToBlobURL (err) { - debug('MediaSource API error: fallback to Blob URL: %o', err.message || err) - elem.removeEventListener('error', fallbackToBlobURL) - elem.removeEventListener('playing', onPlaying) - - useBlobURL() - } - - function createElem (time) { - if (!elem) { - elem = document.createElement(tagName) - elem.controls = true - elem.autoplay = true // for chrome - elem.play() // for firefox - - elem.addEventListener('progress', function () { - currentTime = elem.currentTime - }) - - rootElem.appendChild(elem) - } + render(file, function (tagName) { + if (tagName === 'video' || tagName === 'audio') return createMedia(tagName) + else return createElem(tagName) + }, function (err, elem) { + if (err && elem) { + elem.remove() } - } - function onPlaying () { - elem.removeEventListener('playing', onPlaying) - cb(null, elem) - } + cb(err, elem) + }) - function appendToAudio () { - elem = document.createElement('audio') + function createMedia (tagName) { + var elem = createElem(tagName) elem.controls = true - elem.autoplay = true - rootElem.appendChild(elem) - file.getBlobURL(function (err, url) { - if (err) return fatalError(err) - elem.addEventListener('error', fatalError) - elem.addEventListener('playing', onPlaying) - elem.src = url - elem.play() - }) + elem.autoplay = true // for chrome + elem.play() // for firefox + return elem } - function appendToImage () { - file.getBlobURL(function (err, url) { - if (err) return fatalError(err) - elem = document.createElement('img') - elem.src = url - elem.alt = file.name - rootElem.appendChild(elem) - cb(null, elem) - }) - } - - function appendToIframe () { - file.getBlobURL(function (err, url) { - if (err) return fatalError(err) - elem = document.createElement('iframe') - elem.src = url - if (extname !== '.pdf') elem.sandbox = 'allow-forms allow-scripts' - rootElem.appendChild(elem) - cb(null, elem) - }) - } - - function fatalError (err) { - if (elem) elem.remove() - err.message = 'Error appending file "' + file.name + '" to DOM: ' + err.message - debug(err.message) - if (cb) cb(err) + function createElem (tagName) { + var elem = document.createElement(tagName) + rootElem.appendChild(elem) + return elem } } - -function noop () {} - -function nextTick (cb, err, val) { - process.nextTick(function () { - if (cb) cb(err, val) - }) -} diff --git a/lib/file.js b/lib/file.js index 340ff37..09d3af8 100644 --- a/lib/file.js +++ b/lib/file.js @@ -1,6 +1,7 @@ module.exports = File var appendTo = require('./append-to') +var renderTo = require('./render-to') var eos = require('end-of-stream') var EventEmitter = require('events').EventEmitter var FileStream = require('./file-stream') @@ -130,3 +131,14 @@ File.prototype.appendTo = function (elem, cb) { if (typeof elem === 'string') elem = document.querySelector(elem) appendTo(this, elem, cb) } + +/** + * Render the file in an existing DOM element. + * @param {Element|string} elem + * @param {function} cb + */ +File.prototype.renderTo = function (elem, cb) { + if (typeof window === 'undefined') throw new Error('browser-only method') + if (typeof elem === 'string') elem = document.querySelector(elem) + renderTo(this, elem, cb) +} diff --git a/lib/render-to.js b/lib/render-to.js new file mode 100644 index 0000000..d2b47cc --- /dev/null +++ b/lib/render-to.js @@ -0,0 +1,17 @@ +var path = require('path') +var render = require('./render') + +module.exports = function renderTo (file, elem, cb) { + render(file, function (tagName) { + if (elem.nodeName !== tagName.toUpperCase()) { + var extname = path.extname(file.name).toLowerCase() + + throw new Error( + 'Cannot render "' + extname + '" inside a "' + + elem.nodeName.toLowerCase() + '" element, expected "' + tagName + '"' + ) + } + + return elem + }, cb) +} diff --git a/lib/render.js b/lib/render.js new file mode 100644 index 0000000..84c3a29 --- /dev/null +++ b/lib/render.js @@ -0,0 +1,150 @@ +var debug = require('debug')('webtorrent:render') +var MediaSourceStream = require('mediasource') +var path = require('path') +var videostream = require('videostream') + +var VIDEOSTREAM_EXTS = [ '.mp4', '.m4v', '.m4a' ] + +var MEDIASOURCE_VIDEO_EXTS = [ '.mp4', '.m4v', '.webm' ] +var MEDIASOURCE_AUDIO_EXTS = [ '.m4a', '.mp3' ] +var MEDIASOURCE_EXTS = MEDIASOURCE_VIDEO_EXTS.concat(MEDIASOURCE_AUDIO_EXTS) + +var AUDIO_EXTS = [ '.wav', '.aac', '.ogg', '.oga' ] +var IMAGE_EXTS = [ '.jpg', '.jpeg', '.png', '.gif', '.bmp' ] +var IFRAME_EXTS = [ '.css', '.html', '.js', '.md', '.pdf', '.txt' ] + +var MediaSource = typeof window !== 'undefined' && window.MediaSource + +module.exports = function render (file, getElem, cb) { + if (!cb) cb = noop + var elem + var extname = path.extname(file.name).toLowerCase() + var currentTime = 0 + + if (MEDIASOURCE_EXTS.indexOf(extname) >= 0) renderMediaSource() + else if (AUDIO_EXTS.indexOf(extname) >= 0) renderAudio() + else if (IMAGE_EXTS.indexOf(extname) >= 0) renderImage() + else if (IFRAME_EXTS.indexOf(extname) >= 0) renderIframe() + else nextTick(cb, new Error('Unsupported file type "' + extname + '": Cannot append to DOM')) + + function renderMediaSource () { + if (!MediaSource) { + return nextTick(cb, new Error( + 'Video/audio streaming is not supported in your browser. You can still share ' + + 'or download ' + file.name + ' (once it\'s fully downloaded). Use Chrome for ' + + 'MediaSource support.' + )) + } + + var tagName = MEDIASOURCE_VIDEO_EXTS.indexOf(extname) >= 0 ? 'video' : 'audio' + + if (VIDEOSTREAM_EXTS.indexOf(extname) >= 0) useVideostream() + else useMediaSource() + + function useVideostream () { + debug('Use `videostream` package for ' + file.name) + prepareElem() + elem.addEventListener('error', fallbackToMediaSource) + elem.addEventListener('playing', onPlaying) + videostream(file, elem) + } + + function useMediaSource () { + debug('Use MediaSource API for ' + file.name) + prepareElem() + elem.addEventListener('error', fallbackToBlobURL) + elem.addEventListener('playing', onPlaying) + + file.createReadStream().pipe(new MediaSourceStream(elem, { extname: extname })) + if (currentTime) elem.currentTime = currentTime + } + + function useBlobURL () { + debug('Use Blob URL for ' + file.name) + prepareElem() + elem.addEventListener('error', fatalError) + elem.addEventListener('playing', onPlaying) + file.getBlobURL(function (err, url) { + if (err) return fatalError(err) + elem.src = url + if (currentTime) elem.currentTime = currentTime + }) + } + + function fallbackToMediaSource (err) { + debug('videostream error: fallback to MediaSource API: %o', err.message || err) + elem.removeEventListener('error', fallbackToMediaSource) + elem.removeEventListener('playing', onPlaying) + + useMediaSource() + } + + function fallbackToBlobURL (err) { + debug('MediaSource API error: fallback to Blob URL: %o', err.message || err) + elem.removeEventListener('error', fallbackToBlobURL) + elem.removeEventListener('playing', onPlaying) + + useBlobURL() + } + + function prepareElem () { + if (!elem) { + elem = getElem(tagName) + + elem.addEventListener('progress', function () { + currentTime = elem.currentTime + }) + } + } + } + + function onPlaying () { + elem.removeEventListener('playing', onPlaying) + cb(null, elem) + } + + function renderAudio () { + elem = getElem('audio') + file.getBlobURL(function (err, url) { + if (err) return fatalError(err) + elem.addEventListener('error', fatalError) + elem.addEventListener('playing', onPlaying) + elem.src = url + }) + } + + function renderImage () { + elem = getElem('img') + file.getBlobURL(function (err, url) { + if (err) return fatalError(err) + elem.src = url + elem.alt = file.name + cb(null) + }) + } + + function renderIframe () { + elem = getElem('iframe') + + file.getBlobURL(function (err, url) { + if (err) return fatalError(err) + elem.src = url + if (extname !== '.pdf') elem.sandbox = 'allow-forms allow-scripts' + cb(null, elem) + }) + } + + function fatalError (err) { + err.message = 'Error rendering file "' + file.name + '": ' + err.message + debug(err.message) + if (cb) cb(err) + } +} + +function noop () {} + +function nextTick (cb, err, val) { + process.nextTick(function () { + if (cb) cb(err, val) + }) +} |