diff --git a/.gitignore b/.gitignore index 81a0c2f..fa3e7a8 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/app/src/main.ts b/app/src/main.ts index 78d8ec3..fbba8aa 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -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'); }); diff --git a/docs/api.md b/docs/api.md index 7aa9965..53eebbb 100644 --- a/docs/api.md +++ b/docs/api.md @@ -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 ``` diff --git a/src/build/prepareElectronApp.ts b/src/build/prepareElectronApp.ts index 5105322..84cf90e 100644 --- a/src/build/prepareElectronApp.ts +++ b/src/build/prepareElectronApp.ts @@ -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, diff --git a/src/options/model.ts b/src/options/model.ts index 6380624..296cb4d 100644 --- a/src/options/model.ts +++ b/src/options/model.ts @@ -53,6 +53,7 @@ export interface AppOptions { verbose: boolean; versionString: string; width: number; + widevine: boolean; height: number; minWidth: number; minHeight: number; diff --git a/src/options/optionsMain.ts b/src/options/optionsMain.ts index 12e92c2..b1c4549 100644 --- a/src/options/optionsMain.ts +++ b/src/options/optionsMain.ts @@ -101,6 +101,7 @@ export async function getOptions(rawOptions: any): Promise { 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 { 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 + }`, ); }