diff --git a/package.json b/package.json index be18777..8e98ff9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "youtube-tv-electron", "productName": "YouTube TV", - "version": "2.3.4", + "version": "2.4.0", "description": "Cliente de YouTube TV sencillo para escritorio basado en electron.", "main": "main.js", "scripts": { diff --git a/renderers/main/injection.js b/renderers/main/injection.js index 514270a..99a0165 100644 --- a/renderers/main/injection.js +++ b/renderers/main/injection.js @@ -1,5 +1,3 @@ -console.log('Archivo de customización cargado... Esperando evento de carga.'); - // Mensajes de estado. const msgDB = { es: { @@ -161,7 +159,16 @@ const loadConnectionEvents = () => { } -console.warn('Inyectando parches'); +const listenLocalStorageQueries = () => { + + window.ipc.on('localStorageQuery', (event, { type, data }) => { + + const inf = window.localStorage.getItem(data); + event.sender.send('localStorageQueryResponse', inf); + + }) + +} // Carga la anulación de eventos de cambios de visibilidad. visibilityChangeOverriding(); @@ -174,3 +181,10 @@ loadConnectionWarnings(); // Carga los eventos de cambio de conexión con el servidor de YouTube TV. loadConnectionEvents(); + +// Escucha las peticiones de consultas al localStorage. +listenLocalStorageQueries(); + +// Indica al proceso principal que el código se ha cargado. + +console.log('JavaScript enhancements loaded at', new Date(Date.now()).toISOString()); \ No newline at end of file diff --git a/renderers/main/main.renderer.ts b/renderers/main/main.renderer.ts index 0645266..2eac513 100644 --- a/renderers/main/main.renderer.ts +++ b/renderers/main/main.renderer.ts @@ -4,80 +4,60 @@ import { cwd } from 'process'; import { join } from 'path'; import { Settings } from '../settings/settings.renderer'; - import { app, BrowserWindow, nativeImage, globalShortcut, Menu, - ipcMain, - Size} from 'electron'; + ipcMain } from 'electron'; export interface resolution { /** Screen width */ - widht: number, + width: number /** Screen height */ height: number } +interface windowParams { + + bounds: Electron.Rectangle + fullscreen: boolean + cursor: boolean + +} + export class Renderer { /** userAgent allowed by YouTube TV. */ - private readonly userAgent: string = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.77 Large Screen Safari/534.24 GoogleTV/092754'; + private readonly userAgent: string = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.77 Large Screen Safari/534.24 GoogleTV/092754'; /** Electron process */ - private window: BrowserWindow; + private window: BrowserWindow; /** Settings window */ - private settings: Settings | null; + private settings: Settings | null; /** Cursor visibility flag. */ - private _cursor: boolean = false; - - /** Resolution object. */ - private resolution: resolution = { widht: 3840, height: 2160 } - - /** Fullscreen flag. */ - private _fullScreen: boolean = false; + private _cursor: boolean = false; /** YouTube TV url with path/params */ private readonly _url: string = 'https://www.youtube.com/tv?'; /** JavaScript injection code */ - private jsic: string = ''; + private jsic: string = ''; constructor() { // Set app menu to null. Menu.setApplicationMenu(null); - app - .on('ready', () => { - this.createWindow(); + app.on('ready', () => { - this.window.webContents.executeJavaScript('localStorage.getItem(\'maxRes\');', true) - .then((data: string) => { - - // If the usen has not set a resolution, set the default one. - if (!data) this.setResEmulator(3840, 2160); - else { - try { - // Try to parse and set the resolution. - const { width, height } = JSON.parse(data); - this.setResEmulator(width, height); + this.createWindow(); - } catch (error) { - // If the data is not valid, set the default resolution. - this.setResEmulator(3840, 2160); - } - } - }) - .catch(err => { - // If the data is not valid or not available, set the default resolution. - this.setResEmulator(3840, 2160); - }) + this.listenWindowMoveEvents(); this.url = '__DFT__'; @@ -85,15 +65,10 @@ export class Renderer { this.setAccelerators(); - this.window.on('ready-to-show', () => { - if (this.cursor) { - this.window.webContents.insertCSS('html {cursor: default;}'); - } else if (!this.cursor) { - this.window.webContents.insertCSS('html {cursor: none;}'); - } else { - this.window.webContents.insertCSS('html {cursor: none;}'); - } - }) + if (platform() === 'darwin') { + this.window.on('enter-full-screen', () => this.fullScreen = true) + this.window.on('leave-full-screen', () => this.fullScreen = false) + } this.window.on('close', () => { if (this.settings) { @@ -113,7 +88,9 @@ export class Renderer { this.window = new BrowserWindow({ width: 1230, height: 720, + titleBarStyle: 'default', fullscreen: false, + fullscreenable: true, title: 'YouTube TV', backgroundColor: '#282828', icon: nativeImage.createFromPath(join(cwd(), 'build', 'icon.png')), @@ -122,9 +99,10 @@ export class Renderer { webSecurity: true, contextIsolation: false } - }) + }); - this.window.fullScreenable = true; + process.nextTick(() => this.loadSettings()); + } /** Inject a JavaScript code into the renderer process to patch events and add some features. */ @@ -148,19 +126,17 @@ export class Renderer { const { width, height, reload } = params; - this.window.webContents.executeJavaScript(` - localStorage.setItem('maxRes', JSON.stringify({ width: ${width}, height: ${height} })); - `); + this.localStorageQuery('set', 'maxRes', { width, height }); if (reload) { this.setResEmulator(width, height); this.window.webContents.reload(); - } + } else this.updateWindowParams(); } - + /** Emulate a screen with assigned parameters */ - private setResEmulator(emuWidth: number, emuHeight: number) { + private setResEmulator(emuWidth: number = 3840, emuHeight: number = 2160) { // Delete all listeners. this.window.removeAllListeners('resize'); @@ -169,7 +145,10 @@ export class Renderer { this.calcEmulatedDisplay(emuWidth, emuHeight); // Add a listener to the window to recalculate the emulator. - this.window.on('resize', () => { this.calcEmulatedDisplay(emuWidth, emuHeight) }); + this.window.on('resize', () => { + this.calcEmulatedDisplay(emuWidth, emuHeight); + this.updateWindowParams(); + }); } private calcEmulatedDisplay(emuWidth: number, emuHeight: number) { @@ -204,34 +183,119 @@ export class Renderer { } }) - globalShortcut.register('ctrl+f', () => { this.fullScreen = null; }) + globalShortcut.register('ctrl+f', () => { this.fullScreen = !this.window.isFullScreen(); }) globalShortcut.register('ctrl+d', () => { this.window.webContents.toggleDevTools(); }) - globalShortcut.register('ctrl+a', () => { - if (this.cursor) { - this.window.webContents.insertCSS('html {cursor: none;}') - this.cursor = false; - } else if (!this.cursor) { - this.window.webContents.insertCSS('html {cursor: default;}'); - this.cursor = true; - } else { - this.window.webContents.insertCSS('html {cursor: none;}') - } - }) + globalShortcut.register('ctrl+a', () => this.cursor = null); } /** - * Load new user connection **and reload the renderer process**. - * If value is '__DFT__', the default YouTube TV url will be loaded. + * Performs a query to the local storage of the renderer process. + * @param type Query type. + * @param key Key of the object to be stored in the localStorage. + * @param value Value to be set for the given key. + */ + public async localStorageQuery(type: 'set', key: string, value: any): Promise; + public async localStorageQuery(type: 'delete', key: any): Promise; + public async localStorageQuery(type: 'get', key: any): Promise; + public async localStorageQuery(type: 'clear'): Promise; + public async localStorageQuery(type: 'raw', data: string): Promise; + public async localStorageQuery(type: 'get' | 'set' | 'delete' | 'clear' | 'raw', key?: string, value?: any, data?: string): Promise { + + if (type === 'get' || type === 'set' || type === 'delete' || type === 'clear' || type === 'raw') { + + let query = 'localStorage.'; + + if (type === 'get') query += `getItem('${ key }')` + else if (type === 'set') { + + if (typeof value === 'object') value = `'${JSON.stringify(value)}'`; + query += `setItem('${ key }', ${ value })`; + + } else if (type === 'delete') query += `removeItem('${ key }')` + else if (type === 'clear') query += 'clear()' + else if (type === 'raw') query = data as string; + + const unresolvedQuery = this.window.webContents.executeJavaScript(query); + + if (type === 'get') { + try { + + const resolver = await unresolvedQuery; + const parsed = JSON.parse(resolver); + return Promise.resolve(parsed); + + } catch (error) { + return unresolvedQuery; + } + } else return unresolvedQuery; + + } else return Promise.reject('unknown query type'); + } + + private listenWindowMoveEvents() { + this.window.on('moved', () => { this.updateWindowParams() }) + } + + private getWindowParams() { + + const bounds = this.window.getBounds(); + const fullscreen = this.window.isFullScreen(); + const cursor = this._cursor ? true : false; + + return { bounds, fullscreen, cursor } as windowParams; + + } + + private updateWindowParams() { + const params = this.getWindowParams(); + this.localStorageQuery('set', 'windowParams', params); + } + + private loadSettings() { + + this.localStorageQuery('get', 'windowParams') + .then((data: windowParams) => { + + this.window.setBounds(data.bounds) + this.window.fullScreen = data.fullscreen; + this.cursor = data.cursor; + + this.window.on('resized', () => { + this.updateWindowParams(); + }); + + }); + + this.localStorageQuery('get', 'maxRes') + .then((data: resolution) => { + + // If the usen has not set a resolution, set the default one. + if (!data) this.setResEmulator(); + else { + if (data.width && data.height) this.setResEmulator(data.width, data.height) + else this.setResEmulator(); + } + }) + .catch(err => { + // If the data is not valid or not available, set the default resolution. + this.setResEmulator(3840, 2160); + }) + + } + + /** + * Load new user connection **and reload the renderer process**.\ + * If value is '\_\_DFT\_\_', the default YouTube TV url will be loaded. * */ public set url(value: string) { let url = value; if (typeof value !== 'string') return; if (value.length < 1) return; if (value === '__DFT__') url = ''; - + this.window.loadURL(this._url + url, { userAgent: this.userAgent }) .catch(async() => { @@ -247,7 +311,7 @@ export class Renderer { if (typeof value !== 'string') return; if (value.length < 1) return; - this.fullScreen = true; + this.window.fullScreen = true; this.window.webContents.loadURL(this._url + value, { userAgent: this.userAgent }) // This should never happen... @@ -263,19 +327,29 @@ export class Renderer { public set fullScreen(value: boolean | null) { if (value === null) { - this._fullScreen = !this._fullScreen; - this.window.fullScreen = this._fullScreen; + this.fullScreen = !this.window.isFullScreen(); + return; } else { if (typeof value !== 'boolean') return; - this._fullScreen = value; this.window.fullScreen = value; + this.updateWindowParams(); } } /** Toggle cursor visibility */ public set cursor(value: boolean | null) { - if (typeof value !== 'boolean') this._cursor = !this.cursor + if (typeof value !== 'boolean') this._cursor = !this._cursor else this._cursor = value; + + if (this._cursor) { + this.window.webContents.insertCSS('html {cursor: default;}'); + } else if (!this._cursor) { + this.window.webContents.insertCSS('html {cursor: none;}'); + } else { + this.window.webContents.insertCSS('html {cursor: none;}'); + } + + this.updateWindowParams(); } } \ No newline at end of file