Fix Widevine by properly listening to widevine-... events, and update docs (fix #1153) (PR #1164)

As documented in #1153, for Widevine support to work properly, we need to listen for the Widevine ready event, and as well for certain sites the app must be signed.

This PR adds the events, and as well adds better documentation on the signing limitation.

This may also help resolve #1147
This commit is contained in:
Adam Weeden 2021-04-29 13:27:55 -04:00 committed by GitHub
parent d2c7de37a2
commit f6e1ebd876
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 72 deletions

2
.gitignore vendored
View File

@ -43,6 +43,8 @@ build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
# Python virtual environment in case it's created for the Castlabs code signing tool
venv
# IntelliJ project files
.idea

View File

@ -190,83 +190,107 @@ if (shouldQuit) {
}
});
app.on('ready', () => {
mainWindow = createMainWindow(appArgs, app.quit.bind(this), setDockBadge);
createTrayIcon(appArgs, mainWindow);
if (appArgs.widevine !== undefined) {
// @ts-ignore This event only appear on the widevine version of electron, which we'd see at runtime
app.on('widevine-ready', (version, lastVersion) => {
console.log('widevine-ready', version, lastVersion);
onReady();
});
// Register global shortcuts
if (appArgs.globalShortcuts) {
appArgs.globalShortcuts.forEach((shortcut) => {
globalShortcut.register(shortcut.key, () => {
shortcut.inputEvents.forEach((inputEvent) => {
mainWindow.webContents.sendInputEvent(inputEvent);
});
// @ts-ignore This event only appear on the widevine version of electron, which we'd see at runtime
app.on('widevine-update-pending', (currentVersion, pendingVersion) => {
console.log(
`Widevine ${currentVersion} is ready to be upgraded to ${pendingVersion}`,
);
});
// @ts-ignore This event only appear on the widevine version of electron, which we'd see at runtime
app.on('widevine-error', (error) => {
console.error('WIDEVINE ERROR: ', error);
});
} else {
app.on('ready', () => {
console.log('ready');
onReady();
});
}
}
function onReady(): void {
mainWindow = createMainWindow(appArgs, app.quit.bind(this), setDockBadge);
createTrayIcon(appArgs, mainWindow);
// Register global shortcuts
if (appArgs.globalShortcuts) {
appArgs.globalShortcuts.forEach((shortcut) => {
globalShortcut.register(shortcut.key, () => {
shortcut.inputEvents.forEach((inputEvent) => {
mainWindow.webContents.sendInputEvent(inputEvent);
});
});
});
if (isOSX() && appArgs.accessibilityPrompt) {
const mediaKeys = [
'MediaPlayPause',
'MediaNextTrack',
'MediaPreviousTrack',
'MediaStop',
];
const globalShortcutsKeys = appArgs.globalShortcuts.map((g) => g.key);
const mediaKeyWasSet = globalShortcutsKeys.find((g) =>
mediaKeys.includes(g),
);
if (
mediaKeyWasSet &&
!systemPreferences.isTrustedAccessibilityClient(false)
) {
// Since we're trying to set global keyboard shortcuts for media keys, we need to prompt
// the user for permission on Mac.
// For reference:
// https://www.electronjs.org/docs/api/global-shortcut?q=MediaPlayPause#globalshortcutregisteraccelerator-callback
const accessibilityPromptResult = dialog.showMessageBoxSync(null, {
type: 'question',
message: 'Accessibility Permissions Needed',
buttons: ['Yes', 'No', 'No and never ask again'],
defaultId: 0,
detail:
`${appArgs.name} would like to use one or more of your keyboard's media keys (start, stop, next track, or previous track) to control it.\n\n` +
`Would you like Mac OS to ask for your permission to do so?\n\n` +
`If so, you will need to restart ${appArgs.name} after granting permissions for these keyboard shortcuts to begin working.`,
});
switch (accessibilityPromptResult) {
// User clicked Yes, prompt for accessibility
case 0:
systemPreferences.isTrustedAccessibilityClient(true);
break;
// User cliecked Never Ask Me Again, save that info
case 2:
appArgs.accessibilityPrompt = false;
saveAppArgs(appArgs);
break;
// User clicked No
default:
break;
}
if (isOSX() && appArgs.accessibilityPrompt) {
const mediaKeys = [
'MediaPlayPause',
'MediaNextTrack',
'MediaPreviousTrack',
'MediaStop',
];
const globalShortcutsKeys = appArgs.globalShortcuts.map((g) => g.key);
const mediaKeyWasSet = globalShortcutsKeys.find((g) =>
mediaKeys.includes(g),
);
if (
mediaKeyWasSet &&
!systemPreferences.isTrustedAccessibilityClient(false)
) {
// Since we're trying to set global keyboard shortcuts for media keys, we need to prompt
// the user for permission on Mac.
// For reference:
// https://www.electronjs.org/docs/api/global-shortcut?q=MediaPlayPause#globalshortcutregisteraccelerator-callback
const accessibilityPromptResult = dialog.showMessageBoxSync(null, {
type: 'question',
message: 'Accessibility Permissions Needed',
buttons: ['Yes', 'No', 'No and never ask again'],
defaultId: 0,
detail:
`${appArgs.name} would like to use one or more of your keyboard's media keys (start, stop, next track, or previous track) to control it.\n\n` +
`Would you like Mac OS to ask for your permission to do so?\n\n` +
`If so, you will need to restart ${appArgs.name} after granting permissions for these keyboard shortcuts to begin working.`,
});
switch (accessibilityPromptResult) {
// User clicked Yes, prompt for accessibility
case 0:
systemPreferences.isTrustedAccessibilityClient(true);
break;
// User cliecked Never Ask Me Again, save that info
case 2:
appArgs.accessibilityPrompt = false;
saveAppArgs(appArgs);
break;
// User clicked No
default:
break;
}
}
}
if (
!appArgs.disableOldBuildWarning &&
new Date().getTime() - appArgs.buildDate > OLD_BUILD_WARNING_THRESHOLD_MS
) {
const oldBuildWarningText =
appArgs.oldBuildWarningText ||
'This app was built a long time ago. Nativefier uses the Chrome browser (through Electron), and it is insecure to keep using an old version of it. Please upgrade Nativefier and rebuild this app.';
// eslint-disable-next-line @typescript-eslint/no-floating-promises
dialog.showMessageBox(null, {
type: 'warning',
message: 'Old build detected',
detail: oldBuildWarningText,
});
}
});
}
if (
!appArgs.disableOldBuildWarning &&
new Date().getTime() - appArgs.buildDate > OLD_BUILD_WARNING_THRESHOLD_MS
) {
const oldBuildWarningText =
appArgs.oldBuildWarningText ||
'This app was built a long time ago. Nativefier uses the Chrome browser (through Electron), and it is insecure to keep using an old version of it. Please upgrade Nativefier and rebuild this app.';
// eslint-disable-next-line @typescript-eslint/no-floating-promises
dialog.showMessageBox(null, {
type: 'warning',
message: 'Old build detected',
detail: oldBuildWarningText,
});
}
}
app.on('new-window-for-tab', () => {
mainWindow.emit('new-tab');
});

View File

@ -225,7 +225,7 @@ Electron version without the `v`, see https://github.com/atom/electron/releases.
Use a Widevine-enabled version of Electron for DRM playback, see https://github.com/castlabs/electron-releases.
Note: some sites (like Udemy or HBO Max) using Widevine may still refuse to load videos. Try signing your app using CastLabs tools. See [#1147](https://github.com/nativefier/nativefier/issues/1147#issuecomment-828750362). TL;DR:
Note: some sites using Widevine (like Udemy or HBO Max) may still refuse to load videos, and require EVS-signing your Nativefier app to work. Try signing your app using CastLabs tools. See https://github.com/castlabs/electron-releases/wiki/EVS and [#1147](https://github.com/nativefier/nativefier/issues/1147#issuecomment-828750362). TL;DR:
```bash
# Install CastLabs tools:
@ -234,7 +234,7 @@ pip install --upgrade castlabs-evs
# Sign up:
python3 -m castlabs_evs.account signup
# Signed the generated app
# Sign your app
python -m castlabs_evs.vmp sign-pkg Udemy-win32-x64
```

View File

@ -83,6 +83,7 @@ function pickElectronAppArgs(options: AppOptions): any {
userAgentOverriden: options.nativefier.userAgentOverriden,
versionString: options.nativefier.versionString,
width: options.nativefier.width,
widevine: options.nativefier.widevine,
win32metadata: options.packager.win32metadata,
x: options.nativefier.x,
y: options.nativefier.y,

View File

@ -53,6 +53,7 @@ export interface AppOptions {
verbose: boolean;
versionString: string;
width: number;
widevine: boolean;
height: number;
minWidth: number;
minHeight: number;

View File

@ -101,6 +101,7 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
minHeight: rawOptions.minHeight,
maxWidth: rawOptions.maxWidth,
maxHeight: rawOptions.maxHeight,
widevine: rawOptions.widevine || false,
x: rawOptions.x,
y: rawOptions.y,
zoom: rawOptions.zoom || 1.0,
@ -158,7 +159,11 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
log.warn(
`\nATTENTION: Using the **unofficial** Electron from castLabs`,
"\nIt implements Google's Widevine Content Decryption Module (CDM) for DRM-enabled playback.",
`\nSimply abort & re-run without passing the widevine flag to default to ${DEFAULT_ELECTRON_VERSION}`,
`\nSimply abort & re-run without passing the widevine flag to default to ${
rawOptions.electronVersion !== undefined
? (rawOptions.electronVersion as string)
: DEFAULT_ELECTRON_VERSION
}`,
);
}