castlabs / electron-releases

castLabs Electron for Content Security
https://castlabs.com/resources/downstream/
MIT License
225 stars 42 forks source link

Widevine components not installed correctly for first time load #118

Closed danielxu7 closed 2 years ago

danielxu7 commented 2 years ago

I am currently using the latest version of Castlabs Electron as of this post (16.0.4+wvcus.1) however I noticed that Widevine does not work for the first time I load up Electron. If I close Electron window and open it again, Widevine always works on the subsequent loads. I am navigating to https://bitmovin.com/demos/drm to test this.

app.whenReady().then(async () => {
  await components.whenReady().catch(e => console.log(`component ready fail:`, e));
  console.log('components ready:', components.status());
  createWindow();
})

image

When using the components.status() call, it always says that Widevine actually is installed, even though the screenshot above says it is not and I also cannot use Netflix.

components ready: {
  oimompecagnajdejgnnjijobebaeigek: {
    name: 'Widevine Content Decryption Module',
    status: 'new',
    version: '4.10.2391.0'
  }
}

Interestingly, I was able to get it working on first time load by introducing a delay for loading up Electron window. 30 seconds did not work but changing this 60 seconds did work.

app.whenReady().then(async () => {
  await components.whenReady().catch(e => console.log(`component ready fail:`, e));
  console.log('components ready:', components.status());
  setTimeout(() => {
    createWindow()
  }, 60 * 1000);
})

The await call seems to work because it correctly displays the Widevine version and I can see the files it installs. What could be the reason that Electron thinks it's not installed?

khwaaj commented 2 years ago

Which platform is this on?

danielxu7 commented 2 years ago

Which platform is this on?

This is for Linux.

I also downgraded the version to 15.3.3 and used the following:

app.whenReady().then(async () => {
  createWindow()
})

Widevine works all the time now, no matter if it's first time load or not. This suggests that there must be some installation issue regarding the migration from Electron 15 to 16 since Electron 16 uses the components API.

khwaaj commented 2 years ago

For v15.x.x (or earlier) you actually need to wait for the widevine-ready event, not just ready or whenReady(), to be sure that the component is installed before creating your window. But you are right, the move to using the Component Updater Service is likely what is behind the issue. And Linux is a bit of a special child here as the CDM is handled a bit differently.

I'll see if I can reproduce the problem, but I could use some more details about your implementation. Is it just the minimal implementation that we have in the example README, or is there something else going on as well? E.g. are you using react, babel or similar? Are you doing something before the whenReady() call, opening additional windows, setting some configuration, or passing extra command line parameters? Can you maybe even provide a minimal example for reproduction?

khwaaj commented 2 years ago

Another thought, did you by any chance first install v16.0.4+wvcus? If so could the update to v16.0.4+wvcus.1 have failed, and you are actually seeing the effects of #117?

danielxu7 commented 2 years ago

The installation for v16.0.4+wvcus.1 was fine, that always worked and I was able to verify it was on the correct version by looking in node_modules/electron/. I did not have any errors relating to .so files like in #117. Originally before I upgraded to 16, the configuration was more like:

function createWindow(): void {
 mainWindow = new BrowserWindow({
  width: 1366, height: 768,
 })
mainWindow.loadURL('...')
}

async function start(): Promise<void> {
  // unrelated config code
  createWindow()
}

app.on('ready', start)

I am using TypeScript with Babel but that only gave me issues regarding #116, when I had issues with v16.0.2, which was resolved by upgrading to v16.0.4.

{
    "compilerOptions": {
      "target": "ES2019",
      "module": "commonjs",
      "noImplicitAny": true,
      "sourceMap": true,
      "outDir": "dist",
      "baseUrl": ".",
      "paths": {
        "*": ["node_modules/*"]
      }
    },
}

Here is my tsconfig.json file which I don't think is causing any issues. There are some command line parameters which affect the URL to open and whether to enable Electron debug mode or not but other than that, the snippet above should be an accurate minimal example for reproduction.

Since everything is working with v15.3.3, and the only lines of code changed were to use import { components, ... } from 'electron' and await components.whenReady(), I think something is going wrong with the installation rather than the code.

danielxu7 commented 2 years ago

And Linux is a bit of a special child here as the CDM is handled a bit differently.

Maybe this could be one of the causes? If I was able to get Widevine working with a 60 second delay, maybe this has something to do with it?

khwaaj commented 2 years ago

Thanks for the additional info! I can actually reproduce that Bitmovin is reporting "No DRM" if I open the window immediately after a clean install. I'm adding some functionality to my testing tool to try and catch this in more detail.

And yes, I think this is a Linux only issue because of how CDM loading work there. There is an extra intermediate step to get the CDM loaded in the right process, which is probably the culprit here. Chrome itself would actually require a restart after installation if it didn't have an already bundled CDM to fall back on. With that in mind you can probably work around this by checking the return value of components.status() for status: 'updated' and relaunching the application for that case.

danielxu7 commented 2 years ago

I tried the following and can confirm that this allows Widevine to work on first time load.

while (true) {
    const record = components.status();
    if (record[0].status === 'updated') {
        break;
    }
}
createWindow()

I used a stopwatch and it took around 32 seconds before createWindow was called. This coincides with when I was testing using setTimeout and 30 seconds was not long enough, but 60 was. Still, half a minute of delay for the extra immediate step seems add half a minute of delay seems quite lengthy, considering that v15.3.3 was able to install and have Widevine working (< 0.5s). The installation of Widevine components itself is extremely fast, so something else must be going on before its status is switched to updated.

khwaaj commented 2 years ago

Hm, that appears to differ from what I'm seeing. I get the updated status immediately after await components.waitReady(), when calling components.status(). Are you saying this returns something else in your case?

I agree that the time seems excessive too, it is typically very fast to install the CDM (unless the download is severely throttled).

danielxu7 commented 2 years ago

Yes, I get new for the most part. Code (note status being called after whenReady):

await components.whenReady().catch(e => console.log(`component ready fail:`, e));
console.log('components ready:', components.status());

Output:

components ready: {
  oimompecagnajdejgnnjijobebaeigek: {
    name: 'Widevine Content Decryption Module',
    status: 'new',
    version: '4.10.2391.0'
  }
}
danielxu7 commented 2 years ago

I can confirm that the CDM installation is very quick and the files itself show up almost instantaneously. This also makes sense considering the whenReady() call works fine, and quickly, without any errors. I get new for 32 seconds before it switches to updated which is when Widevine starts working in the browser.

khwaaj commented 2 years ago

I just published v16.0.4+wvcus.2, which fixes the first part of the issue, where the CDM is not correctly registered on first download (on Linux).

I've not been able to reproduce the delay you are seeing though, but please try the release out and see if it perhaps fixes that part of the problem as well.

danielxu7 commented 2 years ago

Good news! I can confirm that using v16.0.4+wvcus.2 resolves all issues with the original code snippet I posted:

app.whenReady().then(async () => {
  await components.whenReady().catch(e => console.log(`component ready fail:`, e));
  console.log('components ready:', components.status());
  createWindow();
})

Widevine now works on both first time load and subsequent loads, with the installation being near instantaneous. There is no longer a 32 second delay, although I'm not exactly sure what was causing it in the first place.

Thank you so much for the help and for releasing a new version this quickly. Cheers!

khwaaj commented 2 years ago

Great! Thanks for the report and for your contributions during the investigation.