├── .airtap.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib └── mime.json ├── package.json └── test ├── basic.js ├── cat.jpg └── package.json /.airtap.yml: -------------------------------------------------------------------------------- 1 | sauce_connect: true 2 | loopback: airtap.local 3 | browsers: 4 | - name: chrome 5 | version: latest 6 | - name: firefox 7 | version: latest 8 | - name: safari 9 | version: latest 10 | - name: microsoftedge 11 | version: latest 12 | - name: iphone 13 | version: latest 14 | - name: android 15 | version: latest 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .airtap.yml 2 | .travis.yml 3 | test/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | addons: 5 | sauce_connect: true 6 | hosts: 7 | - airtap.local 8 | env: 9 | global: 10 | - secure: CuB+YK/wbQ7BPanfV64gE2mXZzr/GoJ3QtwKgCW4UAW2Galv/0rzv3C1lKOUdT3p4NwJRmLGCCk2sg8zucAbQP0xkAheeibv/BhCCLS08b3Tgezs6/i5q4VscUZ2xly9L0Qn5oE6VHkMoIXn25KuI0tUwWDcSSFB/602e5Bp2Tp38TTyE+ISLbJHScdbcMJABa8+hDcCOpcM8f+K3iLhfFII4OEQqytNh/faE79rgo5Ua/m6YMGVWFoOr5Nbv1IU9epvw95xBEu/8nLP5ZheubYiPdpw1a2Agqza/gK/ICQKil+u+8vsIhhZHt1gIo5IQJ+x8NPszIlYVANPp5HFPU2O8/Szz18S16aFrQXWc49HQ1jSwh3CCs6cjMlJfGl28FV/v5nmr/4zluw5wil18gpqzvUxmA2WbxTzlvyfehcaIQrcj7V6cNIBl80qi4lxN/XeL6vK0j8/jLqCjCn2bp/TU9ODpzy/bZD2JDO/FmOunLiCBNFxyooGfvYBl5yenygREmq9dmEQpIZTB8hJHhWxIkZUiI893BPyVUMsZX77Vb3obw7m7b5405V0H4lkM+eFSWB+CJLTeMCrojTg5ohXaCaDArEc5YDoR5sIE9m4VQeH9sIriQV5/W3rw+pCx44mCJ2NeBFfj2C9Cz0zXdnRdcpTZBWNCnNwNbeEmN8= 11 | - secure: CdsdYxXGVG5u5U2NqltunGd/KwKxJTL5LR6PpkUQHepgIIg/7lkZO0lSFDWhW4qLbN2w3ZnRX3OQItajC4FuO5RSFQLKFxHfiigk/iglTnJkRWqqNWS4fzpWvJqlOImrHvhlkgzInDlHhbulL1h+fO6K9+3GFfJuqFRmRJDF7JyRFVcH7350bCL/wKOu3bxo3TbuKk+zwI6lX0K0qCDEOZK8y8h5ALc6he1noXu/H25RYn3KT8EIH69hzCeWG01J5lGaxUU3EWJVCTbK9Jt3UKpbmYxNxgPCxSqYxrBPSMyxuyQvnjc4aHxb+GKDaVP2yql/f5VfRnxE24Fiff1E+VmvZ8QouWfoRYS7MIv1954t18E2zcdKkbh+vmT837+f2P2NdDbB2cgo+KuY5EkugXOp30SP9zUyPA2svLpDR53jOuv6CpfAowlQWXNoI/mToszztueemg6AzxAkfIvRXEzXcapWmr3sv7UetnVld3vdjeJFZIOpZ1spB/qq5V5F5ozCD2TeSEZhCFyeSogGQn+dpZtXo/U3sdY+clsnVfBjTxRZDsBO6LUCTEbeCYRHP7l6t7ai1ljHwmT3seMyV6VY5VgO1r8JafPNjKWr23PBYYmN0hr+wkZEHL/7UgPxUV5maoJqy/hdBHOtmdLjLSkwWqjtaX9fI1BFRGQh870= 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Feross Aboukhadijeh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # render-media [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] 2 | 3 | [travis-image]: https://img.shields.io/travis/feross/render-media/master.svg 4 | [travis-url]: https://travis-ci.org/feross/render-media 5 | [npm-image]: https://img.shields.io/npm/v/render-media.svg 6 | [npm-url]: https://npmjs.org/package/render-media 7 | [downloads-image]: https://img.shields.io/npm/dm/render-media.svg 8 | [downloads-url]: https://npmjs.org/package/render-media 9 | [standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg 10 | [standard-url]: https://standardjs.com 11 | 12 | #### Intelligently render media files in the browser 13 | 14 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/render-media.svg)](https://saucelabs.com/u/render-media) 15 | 16 | Show the file in a the browser by appending it to the DOM. This is a powerful 17 | package that handles many file types like video (.mp4, .webm, .m4v, etc.), audio 18 | (.m4a, .mp3, .wav, etc.), images (.jpg, .gif, .png, etc.), and other file formats 19 | (.pdf, .md, .txt, etc.). 20 | 21 | The file will be streamed into the page (if it's video or audio). Seeking the media 22 | element will request a different byte range from the incoming file-like object. 23 | 24 | In some cases, video or audio files will not be streamable because they're not in a 25 | format that the browser can stream, so the file will be fully downloaded before being 26 | played. For other non-streamable file types like images and PDFs, the file will be 27 | downloaded then displayed. 28 | 29 | This module is used by [WebTorrent](https://webtorrent.io). 30 | 31 | ### install 32 | 33 | ``` 34 | npm install render-media 35 | ``` 36 | 37 | ### usage 38 | 39 | ```js 40 | var render = require('render-media') 41 | var from = require('from2') 42 | 43 | var img = new Buffer('some jpg image data') 44 | 45 | var file = { 46 | name: 'cat.jpg', 47 | createReadStream: function (opts) { 48 | if (!opts) opts = {} 49 | return from([ img.slice(opts.start || 0, opts.end || (img.length - 1)) ]) 50 | } 51 | } 52 | 53 | render.append(file, 'body', function (err, elem) { 54 | if (err) return console.error(err.message) 55 | 56 | console.log(elem) // this is the newly created element with the media in it 57 | }) 58 | ``` 59 | 60 | ### api 61 | 62 | #### `render.append(file, rootElem, [opts], [function callback (err, elem) {}])` 63 | 64 | `file` is an object with a `name` (string, with file extension) and `createReadStream` 65 | method which provides the file data. 66 | 67 | Here's an example file: 68 | 69 | ```js 70 | var file = { 71 | name: 'file.mp4' 72 | createReadStream: function (opts) { 73 | var start = opts.start 74 | var end = opts.end 75 | // Return a readable stream that provides the bytes between offsets "start" 76 | // and "end" inclusive. This works just like fs.createReadStream(opts) from 77 | // the node.js "fs" module. 78 | } 79 | } 80 | ``` 81 | 82 | An optional `file.length` property can also be set to specify the length of the 83 | file in bytes. This will ensure that `render-media` does not attempt to load large 84 | files (>200 MB by default) into memory, which it does in the "blob" strategy. (See discussion 85 | of strategies below.) 86 | 87 | `rootElem` is a container element (CSS selector or reference to DOM node) that the 88 | content will be shown in. A new DOM node will be created for the content and 89 | appended to `rootElem`. 90 | 91 | If provided, `opts` can contain the following options: 92 | 93 | - `autoplay`: Autoplay video/audio files (default: `false`) 94 | - `muted`: Mute video/audio files (default: `false`) 95 | - `controls`: Show video/audio player controls (default: `true`) 96 | - `maxBlobLength`: Files above this size will skip the "blob" strategy and fail (default: `200 * 1000 * 1000` bytes) 97 | 98 | Note: Modern browsers tend to block media that autoplays with audio (here's the 99 | [Chrome policy](https://developers.google.com/web/updates/2017/09/autoplay-policy-changes) 100 | for instance) so if you set `autoplay` to `true`, it's a good idea to also set 101 | `muted` to `true`. 102 | 103 | If provided, `callback` will be called once the file is visible to the user. 104 | `callback` is called with an `Error` (or `null`) and the new DOM node that is 105 | displaying the content. 106 | 107 | #### `render.render(file, elem, [opts], [function callback (err, elem) {}])` 108 | 109 | Like `render.append` but renders directly into given element (or CSS selector). 110 | 111 | 112 | ### why does video/audio streaming not work on file X? 113 | 114 | Streaming support depends on support for `MediaSource` API in the browser. All 115 | modern browsers have `MediaSource` support. 116 | 117 | Many file types are supported (again, depending on browser support), but only `.mp4`, 118 | `.m4v`, and `.m4a` have full support, including seeking. 119 | 120 | ### rendering strategies 121 | 122 | For video and audio, `render-media` tries multiple methods of playing the file: 123 | 124 | - [`videostream`][videostream] -- best option, supports streaming **with seeking**, 125 | but only works with MP4-based files for now (uses `MediaSource` API) 126 | - [`mediasource`][mediasource] -- supports more formats, supports streaming 127 | **without seeking** (uses `MediaSource` API) 128 | - Blob URL -- supports the most formats of all (anything the `