videojs / videojs-contrib-hls

HLS library for video.js
http://videojs.github.io/videojs-contrib-hls/
Other
2.84k stars 792 forks source link

Support for webpack? #600

Closed hanfeisun closed 6 years ago

hanfeisun commented 8 years ago

In the version of 1.x videojs-contrib-hls, I can shim the package like this:

import vjs  from 'video.js';
window.videojs = vjs;

require('imports?this=>window!videojs-contrib-hls');

However, in 2.0.1 videojs-contrib-hls, this method doesn't work.

A dependency called webworkify will throw an error

Uncaught TypeError: Cannot convert undefined or null to object
module.exports  @   index.js:10
VirtualSourceBuffer @   virtual-source-buffer.js:66
addSourceBuffer @   html-media-source.js:168
setupSourceBuffer_  @   videojs-contrib-hls.js?./~/imports-loader?this=>window:621
handleSourceOpen    @   videojs-contrib-hls.js?./~/imports-loader?this=>window:532
data.dispatcher @   video.js:18626
trigger @   video.js:18730
EventTarget.trigger @   video.js:7950

Will videojs-contrib-hls plan to support webpack in the future?

dmlap commented 8 years ago

I think this was the issue behind https://github.com/videojs/mux.js/pull/53. That was fixed in mux.js >= 2.0.1, which you should use if you build this project yourself (and in our next release when that happens). Do you see the same error if you build contrib-hls yourself?

samward1985 commented 8 years ago

+1

Going to try building contrib-hls myself now.

samward1985 commented 8 years ago

Eh - no luck... @hanfeisun did you find a solution?

hanfeisun commented 8 years ago

@samward1985 No, it doesn't work under Webpack now.. So I am still using videojs-contrib-hls 1.3.11..

andrewmnlv commented 8 years ago

Hello all!) You can set alias for hls in webpack config. It works for me: resolve: { alias: { 'videojs-contrib-hls': __dirname + '/node_modules/videojs-contrib-hls/dist/videojs-contrib-hls' } }

videojs-contrib-hls v2.2.0

gkatsev commented 8 years ago

I think contrib-hls 2.2.0 won't require any aliases anymore. Though, unfortunately, that version also requires videojs 5.10.1 and up.

andrewmnlv commented 8 years ago

@gkatsev i have videojs v5.10.2, but webworkify breaks webpack build because of no fallbacks for arguments

gkatsev commented 8 years ago

If webworkify is the only problem, we should consider swapping it out for another module with similar functionality that works in both webpack and browserify.

gkatsev commented 8 years ago

There seems to be a webworkify-webpack module which may work for webpack but it doesnt work for browserify. Someone should write a module that works for both.

gkatsev commented 8 years ago

After some thought and some discussion in babel's chatroom, it seems like the correct solution is to not rely on a packer transform or loader to the web worker inline but rather do it as a separate source transform. I saw https://github.com/mohayonao/inline-worker which may help but also maybe there's some babel plugin that allows us to do this. Unfortunately, this is a low priority for us because there's more pressing specific issues and it can be worked around by using the dist file or maybe running webworkify-webpack over the code. If anyone is willing to submit a PR to fix this issue we'll definitely take a look and work with you to land it. Thanks!

ScottLNorvell commented 8 years ago

I was having the same issue with webpack and contrib-hls. I was able to get it working by aliasing webworkify to webworkify-webpack in my webpack config like so:

// ...
resolve: {
  alias: {
    webworkify$: 'webworkify-webpack'
  }
},
// ...

The strange caveat is that I need point to version 1.0.6 in my package.json instead of the current version (which is 1.1ish). Got that little tidbit from a separate issue here

Hope this helps anyone!

nehakhandelwal commented 8 years ago

I am facing the same and added below code too.

resolve: { alias: { 'videojs-contrib-hls': **dirname + '/node_modules/videojs-contrib-hls/dist/videojs-contrib-hls', webworkify$: 'webworkify-webpack' } }, and getting this error 19:31:03.939 TypeError: _videoJs2.default is undefined [72]</<() bundle.js%20line%205005%20%3E%20eval:8312 [72]<() bundle.js%20line%205005%20%3E%20eval:7848 s() bundle.js%20line%205005%20%3E%20eval:7 s/<() bundle.js%20line%205005%20%3E%20eval:7 [71]</<() bundle.js%20line%205005%20%3E%20eval:7668 [71]<() bundle.js%20line%205005%20%3E%20eval:7640 s() bundle.js%20line%205005%20%3E%20eval:7 s/<() bundle.js%20line%205005%20%3E%20eval:7 [76]</<() bundle.js%20line%205005%20%3E%20eval:8886 [76]<() bundle.js%20line%205005%20%3E%20eval:8870 s() bundle.js%20line%205005%20%3E%20eval:7 s/<() bundle.js%20line%205005%20%3E%20eval:7 [94]</<() bundle.js%20line%205005%20%3E%20eval:14668 [94]<() bundle.js%20line%205005%20%3E%20eval:14627 s() bundle.js%20line%205005%20%3E%20eval:7 e() bundle.js%20line%205005%20%3E%20eval:7

bundle.js%20line%205005%20%3E%20eval:7 bundle.js%20line%205005%20%3E%20eval:7 bundle.js%20line%205005%20%3E%20eval:7 bundle.js%20line%205005%20%3E%20eval:1 bundle.js:5005 __webpack_require**() bundle.js:556 hotCreateRequire/fn() bundle.js:87 bundle.js%20line%204993%20%3E%20eval:29 bundle.js%20line%204993%20%3E%20eval:1 bundle.js:4993 **webpack_require**() bundle.js:556 hotCreateRequire/fn() bundle.js:87 bundle.js%20line%204081%20%3E%20eval:21 bundle.js:4081 **webpack_require**() bundle.js:556 hotCreateRequire/fn() bundle.js:87 bundle.js%20line%20691%20%3E%20eval:19 bundle.js:691 **webpack_require**() bundle.js:556 hotCreateRequire/fn() bundle.js:87 bundle.js:588 **webpack_require**() bundle.js:556 bundle.js:579 bundle.js:1 1 bundle.js%20line%205005%20%3E%20eval:8312:1
nehakhandelwal commented 8 years ago

this is giving the error of webworkify-webpack: Could not locate module containing worker function! Make sure you aren't using eval sourcemaps and that you pass named functions to webworkify-webpack!

Ambroos commented 7 years ago

If anyone else encounters this issue again (or a similar one) and wants to use videojs-contrib-hls with Webpack:

webworkify-webpack shims window to {}, which causes global/window to return the wrong context in a web worker, which in turn causes things to fail. I've created a fork of webworkify-webpack that's compatible with the original webworkify and works with videojs-contrib-hls!

Just add webworkify-webpack-dropin@^1.1.9 to your dependencies and add the following alias to your Webpack config:

resolve: {
  alias: {
    webworkify: 'webworkify-webpack-dropin',
  },
}

It's been tested on videojs-contrib-hls v3.7.0-beta3 and video.js 5.12.6.

gkatsev commented 7 years ago

Anyone know of a babel webworkify plugin? If we can inline the webworker at that point, we can easily support both webpack and browserify without extra work on the user's part

gkatsev commented 7 years ago

@Ambroos also, thanks for making the dropin replacement package, seems like it'll make things a lot simpler in the meantime.

vpowers commented 7 years ago

@Ambroos I've tried your solution and I'm getting an error thrown here in webworkify-webpack-dropin:

if (typeof key === 'undefined') {
        throw new Error('webworkify-webpack: Could not locate module containing worker function! Make sure you aren\'t using eval sourcemaps and that you pass named functions to webworkify-webpack!');
}

I've made sure that I'm not using eval sourcemaps.
I've upgraded my versions of videojs-contrib-hls and video.js to v3.7.0-beta3 and 5.12.6, respectively. The only difference is my project is a react app using nwb, if that makes any difference. Any ideas?

Ambroos commented 7 years ago

@vpowers Could you show me your webpack config? I can't really think of any reason for this error to appear.

vpowers commented 7 years ago

@Ambroos Here is my nwb.config.js. Webpack configuration is passed in:

module.exports = {
...
  webpack: {
    devtool: 'cheap-source-map',
    html: {
      favicon: 'vendors/favicon.ico'
    },
    extra: {
      resolve: {
        alias: {
          webworkify: 'webworkify-webpack-dropin'
        }
      }
    }
  }
}

Here is the line in virtual-source-buffer.js (es5) from videojs-contrib-media-sources that executes webworkify-webpack-dropin:

// append muxed segments to their respective native buffers as
// soon as they are available
this.transmuxer_ = (0, _webworkify2['default'])(_transmuxerWorker2['default']);
vpowers commented 7 years ago

I've attempted to isolate the issue by creating a small app using videojs-contrib-hls and bundling with webpack and @Ambroos 's solution does work. I had been testing this within a larger react app that uses nwb so somethings going on there. I'll provide an update if I can figure out the issue.

dbryand commented 7 years ago

@Ambroos's solution worked for me, but I had to turn off eval for source maps in my large react app, @vpowers. (edit: I see now that you're using cheap-source-map, so that probably won't help you)

vpowers commented 7 years ago

@dbryand My react app is using nwb for bundling instead of plain webpack. (nwb uses webpack under the hood). This seems to be the difference. Are you just using webpack?

dbryand commented 7 years ago

Yes, a home-rolled webpack config.

vpowers commented 7 years ago

I've created two small applications. One using webpack and video.js with hls plugin and a react app using nwb (https://github.com/insin/nwb) and video.js with hls plugin. Both of them are swapping out the webworkify module with @Ambroos's webworkify-webpack-dropin module. The webpack app is working with this solution but the react app using nwb is not. If anyone has any experience with nwb or is feeling helpful, take a look at these projects. Any help would be appreciated!

https://github.com/vpowers/simple-react-app https://github.com/vpowers/simple-webpack-app

vpowers commented 7 years ago

In case someone comes along and needs to support videojs-contrib-hls in a react app using nwb, I have forked @Ambroos webworkify-webpack-dropin to support how nwb bundles code:

https://github.com/vpowers/webworkify-nwb

Follow the instructions in this comment but use the module above instead.

gkatsev commented 7 years ago

I wonder if we can turn webworkify into a babel plugin. Then we could just run it at build time and users of contrib-hls won't need to bother with configuring their bundlers.

oleynikd commented 7 years ago

Hi, guys, could please someone explain how to use this inside SPA? I tried @vpowers solution, but I still get webworkify-webpack: Could not locate module containing worker function! Make sure... I'm using vuejs

Thank you in advance.

chrispappas commented 7 years ago

Running into this issue here as well, same error as @oleynikd is reporting - any updates since last month on this? Also using vuejs and importing as es6 module, compiling with webpack. Tried the webworkify-webpack-dropin plugin but no dice.

lionxcr commented 7 years ago

So Basically here is the setup you need to get this up and running.

I am using node 7.4 and a boiler from create-react-app.

This is my webpack config:

resolve: {
        // This allows you to set a fallback for where Webpack should look for modules.
        // We read `NODE_PATH` environment variable in `paths.js` and pass paths here.
        // We use `fallback` instead of `root` because we want `node_modules` to "win"
        // if there any conflicts. This matches Node resolution mechanism.
        // https://github.com/facebookincubator/create-react-app/issues/253
        fallback: paths.nodePaths,
        // These are the reasonable defaults supported by the Node ecosystem.
        // We also include JSX as a common component filename extension to support
        // some tools, although we do not recommend using it, see:
        // https://github.com/facebookincubator/create-react-app/issues/290
        extensions: ['.js', '.json', '.jsx', ''],
        alias: {
            'videojs-contrib-hls':paths.appNodeModules+'/videojs-contrib-hls/dist/videojs-contrib-hls.min.js'
        }
    }

I am also using this plugin to set videojs globally on my scope:

new webpack.ProvidePlugin({
            videojs: "video.js",
            "window.videojs": "video.js"
        }),

Additionally on my video component JS I use the following:

import 'videojs-contrib-hls';
let video;

Later on my component did mount:

componentDidMount(){
        const options = {
            height: "100%",
            width: "100%",
            hls: {
                withCredentials: true
            }
        };
        video = window.videojs("video-player", options);
    }

finally you need to set your src m3u8 file and type using Javascript if not this will not work:

video.src({
                src:'https://master.m3u8',
                type: 'application/x-mpegURL'
            });
video.play();

render(){
    return(
         <video id="video-player" controls preload className="video-js" poster="videoposter.jpg"/>
     );
}

Finally got my video to play hope this helps!

You will need to use the same plugin and change the alias in your production configuration so the library can run properly:

USE THE ES5 VERSION

'videojs-contrib-hls':paths.appNodeModules+'/videojs-contrib-hls/es5/videojs-contrib-hls.js'

ADD THE WEBWORKIFY DROPIN

'webworkify$': 'webworkify-webpack-dropin',

NPM Modules used:

"video.js": "^5.12.6",
 "videojs-contrib-hls": "^3.7.0-beta3",
"webworkify-webpack-dropin": "^1.1.9"
icetronics commented 7 years ago

Thanks for this @lionxcr, I was able to set it up following your guide. Is there no other way to integrate it more seamlessly without additional Webpack config? Thanks!

lionxcr commented 7 years ago

@icetronics You are so welcome! As far as I know contrib-hls is not supported in webpack just yet so by far this is the best hack to get m3u8 videos to work properly in react.

gkatsev commented 7 years ago

The reason why is because we use browserify to build out the pre-build standalone file. To do that we use the webworkify module that you're replacing with the dropin one for webpack. What would need to happen is to switch to either a babel plugin or something external to the bundler to do the webworker inlining.

icetronics commented 7 years ago

Thanks @gkatsev! Would you be able to provide an example or some further pointers so I could try setting it up?

cansin commented 7 years ago

In case there are people having the same problems as I did:

I have been trying to make this work with webpack 2.2.1, video.js 5.18.4 and videojs-contrib-hls 5.3.3. I was able to get webworkify-webpack-dropin to work with a small change: https://github.com/Ambroos/webworkify-webpack-dropin/pull/1/files . I am hoping @Ambroos will merge the PR and release the version to NPM soon. Change in plans. I am trying to release a fix as a separate npm package, yet still trying to figure out how to make it work with uglify-js atm. Gave up :(

everett1989 commented 7 years ago

@cansin and anyone else who's using webpack's UglifyJsPlugin. I found that if I set compress to false and mangle to true, like so:

plugins: [
    ...
    new webpack.optimize.UglifyJsPlugin({
      compress: false, // leave false for now. This breaks the videojs-contrib-hls package
      mangle: true,
      sourceMap: true
    }),
    ...
],

This will uglify it without breaking the videojs-contrib-hls package. For me, it was like less than 40KB difference between compress being true vs false.

I'm also importing the package like so: import 'videojs-contrib-hls/dist/videojs-contrib-hls.min.js'

This is a nice workaround for me until videojs-contrib-hls is compliant with the compression tool

cansin commented 7 years ago

I actually couldn't figure out how to make webworkify-webpack-dropin work with my case. Instead I ended up separating videojs-contrib-hls through:

require.ensure(['videojs-contrib-hls/dist/videojs-contrib-hls.min'], require => {
    require('videojs-contrib-hls/dist/videojs-contrib-hls.min');
}, 'package-videojs-contrib-hls');

and then disabled UglifyJS for this separate bundle via:

new webpack.optimize.UglifyJsPlugin({
    sourceMap: false,
    compress: {
        warnings: false,
    },
    exclude: /package-videojs-contrib-hls/,
})

I know this is less than ideal, yet it works for now. Could be an alternative to what you did @everett1989 .

everett1989 commented 7 years ago

@cansin I'll try it out, thanks for the example

cjdeleon62 commented 7 years ago

hi all, does anyone know if this webpack fix works when deploying to heroku? I am currently trying to deploy to heroku and I am getting an 'Uncaught SyntaxError: Unexpected token {'

MCDELTAT commented 7 years ago

EDIT: With the help of zshenker on Slack I followed @Ambroos but that required me to turn off my source maps. This got me over the error referenced above, but then it ultimately fails to load because I get a simple syntax error "Uncaught SyntaxError: Unexpected token {". Is their really no way to turn on source map with webworkify?

EDIT2: So the syntax error problem appears to be occurring because of the common UglifyJs plugin in my webpack.config. When I get rid of it, the error goes away, but unfortunately that leaves me with a file that went from 1.04MB to 2.86MB. Ideas on how to fix those syntax errors and still use Uglify?

EDIT3: It appears to be a problem with the version of uglify that is bundled with the latest Webpack v1 (1.14.0). I installed uglifyjs globally and ran the bundle.js file through the command line manually, with no syntax error. It also maintains roughly the same as listed above. So I guess I'll try to update to Webpack 2 soon and verify if the error exists there.

thecotne commented 7 years ago

it's not UglifyJs's bug it's bug in this code https://github.com/Ambroos/webworkify-webpack-dropin/blob/master-v1/index.js#L92

it appears when code is minimized and unused function names are removed

moduleWrapperStrings[moduleId] = wrapperFuncString.substring(0, wrapperFuncString.length - 1) 
+ '\n' + fnString.match(/function\s?(.+?)\s?\(.*/)[1] + '();\n}';

this regex is matching function name but if there is none it will match some weird shit

and syntax error will appear after compilation in webworker code generated at runtime in browser

MCDELTAT commented 7 years ago

@thecotne Thanks. I had suspected that because for debugging purposes I also moved to Webpack 2, and that didn't fix the issue (has more up to date Uglifyjs) I temporarily solved it by running uglify as part of my "npm build" script outside of webpack. Looks like he just needs to pull from where he forked from because they claim to have a fix that works with Uglify. I'll look at that today.

serv commented 7 years ago

When I try to compress the js bundle using webpack and uglify, in the browser console, I get this error

Uncaught ReferenceError: e is not defined

MCDELTAT commented 7 years ago

@serv Yep. As was discussed in my posts above, there is a very strict regex function in the current version of webworkify-webpack-dropin. Uglify gains one of its shrinkage abilities by deleting functions that it determines are unused, but webworkify is still looking for them. For now, just turn off uglify in webpack, or you can make a npm script that builds your webpack and then runs uglify, which I did with success. I will be trying to get @Ambroos to update his fork here soon, given that I suggested that people use it in the webpack docs.

ScottLNorvell commented 7 years ago

Ok... So webworkify-webpack worked for a few weeks to months, then webworkify-webpack-dropin worked for a few more. Now I am on the latest of everything (contrib-hls: 5.4.1, video.js: 5.19.2 as of this writing) and everything is working! Here is how I did it:

Alias videojs-contrib-hls per @lionxcr's suggestion:

// webpack.config.js
var path = require('path');
// ...
  resolve: {
    alias: {
      'videojs-contrib-hls': path.join(
        __dirname,
        'node_modules',
        'videojs-contrib-hls',
        'dist',
        // You can use the unminified version and let the minifier minify!
        'videojs-contrib-hls.js'
      ),
    }
  }
// ...

Make videojs global (also per @lionxcr's suggesiont):

// webpack.config.js
// ...
  plugins: [
    new webpack.ProvidePlugin({
        videojs: "video.js",
        "window.videojs": "video.js"
    })
  ]
// ...

After all of that, I was getting the same error as @serv. After some digging, I found the DefinePlugin. It turns out uglify was doing strange things with global, so I aliased typeof global to "undefined" so that the minifier would remove it!

// webpack.config.js
// ...
  plugins: [
    new webpack.DefinePlugin({
      'typeof global': JSON.stringify('undefined')
    })
  ]
// ...

Also, if at some point had you aliased webworkify, un-alias it!

// webpack.config.js
// ...
  resolve: {
    alias: {
      // webworkify$: 'webworkify-webpack',
      // webworkify$: 'webworkify-webpack-dropin',
    }
  }
// ...

Phew! That works for now... Hope this helps!

correju commented 7 years ago

So far I can create development bundle without a problem and plugin works perfectly, but when I build production I get this error in run-time:

Uncaught ReferenceError: n is not defined blob:http://localhost/659f9283-bbc2-4836-ae79-ebb3cd70a83a:1

I solve this issue passing: devtool: (isProd ? '#eval' : '#source-map') my original set up is devtool: (isProd ? false : '#source-map')

The problem is that my bundle pass from 1.3MB to 4.3MB when I set eval to devtool. has anyone solve this issue completely?

dbryand commented 7 years ago

The solution by @ScottLNorvell worked for me, but this part led to some very tricky to debug issues with another library (GSAP) in my app.

       new webpack.DefinePlugin({
         'typeof global': JSON.stringify('undefined')
       }),

In retrospect it seems obvious that other libs may rely on the typeof global and this could blow things up.

EDIT: In removing this I don't see any impact to video.js / hls.

MCDELTAT commented 7 years ago

?? Decided to visit this again today because I got the classic error 'fs is not defined' in Safari (but not Chrome). But I still can't get webpack to run uglify from the config file. It passes, but then gives like above, 't is not defined in blob' when the page is opened. So I'm going back to my method of just running webpack, and then using uglify after the fact. Somehow that doesn't cause any problems.

dbryand commented 7 years ago

Now have confirmed that I'm getting the dreaded 't is not defined', too, when I package and ship to s3.

EDIT: I turned off uglification in the app and it works.

emilioastarita commented 7 years ago

@dbryand same here "t is not defined" . Did you find any solution?

dbryand commented 7 years ago

Yes, I dropped videojs and just went with html5 + hls.js. Sorry I can't be more helpful :)