From 33eb09838a5eadba4bf21c3b37a60e6e6b05ab03 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Fri, 9 Aug 2019 14:41:59 +0800 Subject: [PATCH 001/123] refactor(browsingview): use browser view instead of webview --- .electron-vue/webpack.renderer.config.js | 2 + src/main/index.js | 118 +++++- src/renderer/browsing.ts | 44 +++ src/renderer/components/BrowsingPip.vue | 147 +++++++ src/renderer/components/BrowsingView.vue | 359 ++++++++++-------- .../BrowsingView/BrowsingFavicons.vue | 9 +- .../BrowsingView/BrowsingHeader.vue | 15 +- src/renderer/containers/LandingView.vue | 12 +- src/renderer/main.ts | 10 +- static/pip/preload.js | 3 +- 10 files changed, 538 insertions(+), 181 deletions(-) create mode 100644 src/renderer/browsing.ts create mode 100644 src/renderer/components/BrowsingPip.vue diff --git a/.electron-vue/webpack.renderer.config.js b/.electron-vue/webpack.renderer.config.js index 4e0a934ea5..0fd263281b 100644 --- a/.electron-vue/webpack.renderer.config.js +++ b/.electron-vue/webpack.renderer.config.js @@ -57,6 +57,7 @@ let rendererConfig = { about: path.join(__dirname, '../src/renderer/about.js'), labor: path.join(__dirname, '../src/renderer/labor.ts'), index: path.join(__dirname, '../src/renderer/main.ts'), + browsing: path.join(__dirname, '../src/renderer/browsing.ts'), }, externals: [ ...Object.keys(Object.assign({}, dependencies, optionalDependencies)).filter(d => !whiteListedModules.includes(d)) @@ -204,6 +205,7 @@ let rendererConfig = { new HtmlWebpackPlugin(generateHtmlWebpackPluginConfig('labor')), new HtmlWebpackPlugin(generateHtmlWebpackPluginConfig('about')), new HtmlWebpackPlugin(generateHtmlWebpackPluginConfig('preference')), + new HtmlWebpackPlugin(generateHtmlWebpackPluginConfig('browsing')), new webpack.HotModuleReplacementPlugin(), new ForkTsCheckerWebpackPlugin({ eslint: true }), ], diff --git a/src/main/index.js b/src/main/index.js index 434c00ac69..3c48dd400f 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,7 +1,7 @@ // Be sure to call Sentry function as early as possible in the main process import '../shared/sentry'; -import { app, BrowserWindow, session, Tray, ipcMain, globalShortcut, nativeImage, splayerx, systemPreferences } from 'electron' // eslint-disable-line +import { app, BrowserWindow, session, Tray, ipcMain, globalShortcut, nativeImage, splayerx, systemPreferences, BrowserView } from 'electron' // eslint-disable-line import { throttle, debounce, uniq } from 'lodash'; import os from 'os'; import path, { @@ -64,6 +64,8 @@ let mainWindow = null; let laborWindow = null; let aboutWindow = null; let preferenceWindow = null; +let browsingWindow = null; +let browserViews = []; let tray = null; let needToRestore = false; let inited = false; @@ -83,6 +85,9 @@ const aboutURL = process.env.NODE_ENV === 'development' const preferenceURL = process.env.NODE_ENV === 'development' ? 'http://localhost:9080/preference.html' : `file://${__dirname}/preference.html`; +const browsingURL = process.env.NODE_ENV === 'development' + ? 'http://localhost:9080/browsing.html' + : `file://${__dirname}/browsing.html`; const tempFolderPath = path.join(app.getPath('temp'), 'splayer'); if (!fs.existsSync(tempFolderPath)) fs.mkdirSync(tempFolderPath); @@ -259,6 +264,35 @@ function createAboutWindow() { }); } +function createBrowsingWindow() { + const browsingWindowOptions = { + useContentSize: true, + frame: false, + titleBarStyle: 'none', + transparent: true, + movable: true, + webPreferences: { + webSecurity: false, + nodeIntegration: true, + experimentalFeatures: true, + webviewTag: true, + }, + backgroundColor: '#6a6a6a', + acceptFirstMouse: false, + show: false, + }; + if (!browsingWindow) { + browsingWindow = new BrowserWindow(browsingWindowOptions); + browsingWindow.loadURL(`${browsingURL}`); + browsingWindow.on('closed', () => { + browsingWindow = null; + }); + } + browsingWindow.once('ready-to-show', () => { + // browsingWindow.show(); + }); +} + function createLaborWindow() { const laborWindowOptions = { show: false, @@ -352,6 +386,13 @@ function registerMainWindowEvent(mainWindow) { } }); + ipcMain.on('callBrowsingWindowMethod', (evt, method, args = []) => { + try { + browsingWindow[method](...args); + } catch (ex) { + console.error('callBrowsingWindowMethod', method, JSON.stringify(args), '\n', ex); + } + }); ipcMain.on('callMainWindowMethod', (evt, method, args = []) => { try { mainWindow[method](...args); @@ -359,6 +400,79 @@ function registerMainWindowEvent(mainWindow) { console.error('callMainWindowMethod', method, JSON.stringify(args), '\n', ex); } }); + ipcMain.on('store-pip-info', () => { + mainWindow.send('store-pip-info', [ + browsingWindow.getPosition()[0], + browsingWindow.getPosition()[1], + browsingWindow.getSize()[0], + browsingWindow.getSize()[1], + ]); + }); + ipcMain.on('remove-pip-browser', () => { // close pip window + browsingWindow.removeBrowserView(browserViews[0]); + if (browserViews[0].webContents.canGoBack()) { + browserViews[0].webContents.goBack(); + } + browsingWindow.hide(); + }); + ipcMain.on('remove-browser', (evt, isPip) => { + if (isPip) { + mainWindow.removeBrowserView(browserViews[1]); + browsingWindow.removeBrowserView(browserViews[0]); + } else { + mainWindow.removeBrowserView(browserViews[0]); + } + browserViews.forEach((view) => { + view.destroy(); + }); + browsingWindow.hide(); + }); + ipcMain.on('create-browser-view', (evt, args) => { + const isDestroyed = browserViews.filter(view => view.isDestroyed()).length; + if (!browserViews.length || isDestroyed) { + browserViews = [ + new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + }, + }), + new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + }, + }), + ]; + } + mainWindow.addBrowserView(browserViews[0]); + browserViews.forEach((view) => { + view.webContents.loadURL(args.url); + view.webContents.openDevTools(); + }); + }); + ipcMain.on('enter-pip', () => { + if (!browsingWindow) { + createBrowsingWindow(); + } + browsingWindow.show(); + browsingWindow.openDevTools(); + mainWindow.removeBrowserView(browserViews[0]); + browsingWindow.removeBrowserView(browserViews[1]); + mainWindow.addBrowserView(browserViews[1]); + browsingWindow.addBrowserView(browserViews[0]); + }); + ipcMain.on('exit-pip', () => { + mainWindow.removeBrowserView(browserViews[1]); + browsingWindow.removeBrowserView(browserViews[0]); + if (browserViews[1].webContents.canGoBack()) { + browserViews[1].webContents.goBack(); + } + mainWindow.addBrowserView(browserViews[0]); + browsingWindow.addBrowserView(browserViews[1]); + browsingWindow.hide(); + }); + ipcMain.on('update-header-to-show', (e, headerToShow) => { + mainWindow.send('update-header-to-show', headerToShow); + }); ipcMain.on('update-route-name', (e, route) => { routeName = route; }); @@ -411,6 +525,7 @@ function registerMainWindowEvent(mainWindow) { app.quit(); }); ipcMain.on('add-preference', createPreferenceWindow); + ipcMain.on('add-browsing', createBrowsingWindow); ipcMain.on('preference-to-main', (e, args) => { if (mainWindow && !mainWindow.webContents.isDestroyed()) { mainWindow.webContents.send('mainDispatch', 'setPreference', args); @@ -714,6 +829,7 @@ app.on('web-contents-created', (webContentsCreatedEvent, contents) => { app.on('bossKey', handleBossKey); app.on('add-preference', createPreferenceWindow); +app.on('add-browsing', createBrowsingWindow); app.on('add-windows-about', createAboutWindow); app.on('menu-create-main-window', () => { diff --git a/src/renderer/browsing.ts b/src/renderer/browsing.ts new file mode 100644 index 0000000000..c0b5b16fb5 --- /dev/null +++ b/src/renderer/browsing.ts @@ -0,0 +1,44 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import VueI18n from 'vue-i18n'; +import { hookVue } from '@/kerning'; +// @ts-ignore +import BrowsingPip from '@/components/BrowsingPip.vue'; +import store from '@/store'; + +hookVue(Vue); +Vue.use(VueI18n); +Vue.use(Vuex); + +Vue.prototype.$bus = new Vue(); // Global event bus +Vue.directive('fade-in', { + bind(el: HTMLElement, binding: unknown) { + if (!el) return; + const { value } = binding as { value: unknown }; + if (value) { + el.classList.add('fade-in'); + el.classList.remove('fade-out'); + } else { + el.classList.add('fade-out'); + el.classList.remove('fade-in'); + } + }, + update(el: HTMLElement, binding) { + const { oldValue, value } = binding; + if (oldValue !== value) { + if (value) { + el.classList.add('fade-in'); + el.classList.remove('fade-out'); + } else { + el.classList.add('fade-out'); + el.classList.remove('fade-in'); + } + } + }, +}); +new Vue({ + components: { BrowsingPip }, + data: {}, + store, + template: '', +}).$mount('#app'); diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue new file mode 100644 index 0000000000..9c6ef23bca --- /dev/null +++ b/src/renderer/components/BrowsingPip.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 5b64c807c1..c103379b7a 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -1,6 +1,9 @@ @@ -136,24 +130,20 @@ export default { othersPip() { return globalPip(this.winSize); }, - availableUrl() { - const parsedUrl = urlParseLax(this.initialUrl); - return parsedUrl.protocol ? parsedUrl.href : `http://${this.initialUrl}`; - }, danmuType() { return this.barrageOpen ? 'danmu' : 'noDanmu'; }, danmuIconState() { return ['youtube', 'others'].includes(this.pipType) || (this.pipType === 'bilibili' && this.bilibiliType === 'others') ? 0.2 : 1; }, + currentBrowserView() { + return this.$electron.remote.BrowserView.getAllViews()[1]; + }, + currentPipBrowserView() { + return this.$electron.remote.BrowserView.getAllViews()[0]; + }, }, watch: { - isFullScreen(val: string) { - if (!val) { - this.headerToShow = true; - this.$refs.webView.executeJavaScript('document.webkitCancelFullScreen();'); - } - }, dropFiles(val: string[]) { this.backToLandingView = false; const onlyFolders = val.every((file: fs.PathLike) => fs.statSync(file).isDirectory()); @@ -172,11 +162,11 @@ export default { isPip(val: boolean) { this.menuService.updatePip(val); this.$electron.ipcRenderer.send('update-enabled', 'window.pip', true); - this.$electron.ipcRenderer.send('update-enabled', 'history.back', !val && this.$refs.webView.canGoBack()); - this.$electron.ipcRenderer.send('update-enabled', 'history.forward', !val && this.$refs.webView.canGoForward()); + this.$electron.ipcRenderer.send('update-enabled', 'history.back', !val && this.currentBrowserView.webContents.canGoBack()); + this.$electron.ipcRenderer.send('update-enabled', 'history.forward', !val && this.currentBrowserView.webContents.canGoForward()); if (!val) { - this.$store.dispatch('updatePipSize', this.winSize); - this.$store.dispatch('updatePipPos', this.winPos); + this.$electron.ipcRenderer.send('exit-pip'); + this.$electron.ipcRenderer.send('store-pip-info'); this.$electron.ipcRenderer.send('update-enabled', 'window.keepPlayingWindowFront', false); this.handleWindowChangeExitPip(); if (this.pipType === 'youtube') { @@ -189,6 +179,7 @@ export default { this.othersRecover(); } } else { + this.$electron.ipcRenderer.send('enter-pip'); this.$store.dispatch('updateBrowsingSize', this.winSize); this.$store.dispatch('updateBrowsingPos', this.winPos); this.$electron.ipcRenderer.send('update-enabled', 'window.keepPlayingWindowFront', true); @@ -214,33 +205,46 @@ export default { } }, loadingState(val: boolean) { - const loadUrl = this.$refs.webView.getURL(); + const loadUrl = this.currentBrowserView.webContents.getURL(); const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); - this.$electron.ipcRenderer.send('update-enabled', 'history.back', this.$refs.webView.canGoBack()); - this.$electron.ipcRenderer.send('update-enabled', 'history.forward', this.$refs.webView.canGoForward()); + this.$electron.ipcRenderer.send('update-enabled', 'history.back', this.currentBrowserView.webContents.canGoBack()); + this.$electron.ipcRenderer.send('update-enabled', 'history.forward', this.currentBrowserView.webContents.canGoForward()); if (val) { this.hasVideo = false; this.$electron.ipcRenderer.send('update-enabled', 'window.pip', false); this.$refs.browsingHeader.updateWebInfo({ hasVideo: this.hasVideo, url: loadUrl, - canGoBack: this.$refs.webView.canGoBack(), - canGoForward: this.$refs.webView.canGoForward(), + canGoBack: this.currentBrowserView.webContents.canGoBack(), + canGoForward: this.currentBrowserView.webContents.canGoForward(), }); } else { if (this.pipRestore) { this.pipAdapter(); this.pipRestore = false; } - this.$refs.webView.executeJavaScript(this.calculateVideoNum, (r: number) => { - this.hasVideo = recordIndex === 0 && !getVideoId(loadUrl).id ? false : !!r; - this.$electron.ipcRenderer.send('update-enabled', 'window.pip', this.hasVideo); - this.$refs.browsingHeader.updateWebInfo({ - hasVideo: this.hasVideo, - url: loadUrl, - canGoBack: this.$refs.webView.canGoBack(), - canGoForward: this.$refs.webView.canGoForward(), + this.currentBrowserView.webContents + .executeJavaScript(this.calculateVideoNum, (r: number) => { + this.hasVideo = recordIndex === 0 && !getVideoId(loadUrl).id ? false : !!r; + this.$electron.ipcRenderer.send('update-enabled', 'window.pip', this.hasVideo); + this.$refs.browsingHeader.updateWebInfo({ + hasVideo: this.hasVideo, + url: loadUrl, + canGoBack: this.currentBrowserView.webContents.canGoBack(), + canGoForward: this.currentBrowserView.webContents.canGoForward(), + }); }); + } + }, + headerToShow(val: boolean) { + const currentView = this.isPip ? this.currentPipBrowserView : this.currentBrowserView; + if (!val) { + currentView.setBounds({ + x: 0, y: 0, width: window.screen.width, height: window.screen.height, + }); + } else { + currentView.setBounds({ + x: 0, y: 36, width: this.browsingSize[0], height: this.browsingSize[1], }); } }, @@ -254,6 +258,10 @@ export default { ); this.$store.dispatch('updateBrowsingPos', this.winPos); this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); + this.currentBrowserView.setBounds({ + x: 0, y: 36, width: this.browsingSize[0], height: this.browsingSize[1] - 36, + }); + this.currentBrowserView.setAutoResize({ width: true, height: true }); }, mounted() { this.menuService = new MenuService(); @@ -271,10 +279,15 @@ export default { }, 0); }); window.addEventListener('beforeunload', (e: BeforeUnloadEvent) => { + this.$electron.ipcRenderer.send('remove-browser', this.isPip); if (!this.asyncTasksDone) { e.returnValue = false; - this.$store.dispatch(this.isPip ? 'updatePipSize' : 'updateBrowsingSize', this.winSize); - this.$store.dispatch(this.isPip ? 'updatePipPos' : 'updateBrowsingPos', this.winPos); + if (this.isPip) { + this.$electron.ipcRenderer.send('store-pip-info'); + } else { + this.$store.dispatch('updateBrowsingSize', this.winSize); + this.$store.dispatch('updateBrowsingPos', this.winPos); + } asyncStorage.set('browsing', { pipSize: this.pipSize, pipPos: this.pipPos, @@ -312,54 +325,32 @@ export default { }); this.$bus.$on('back-to-landingview', () => { this.backToLandingView = true; + this.$bus.$off(); this.$router.push({ name: 'landing-view', }); }); - this.$refs.webView.addEventListener('load-commit', () => { - const loadUrl = this.$refs.webView.getURL(); - this.currentUrl = loadUrl; - const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); - if (recordIndex !== -1) { - switch (recordIndex) { - case 0: - this.updateRecordUrl({ youtube: loadUrl }); - break; - case 1: - this.updateRecordUrl({ bilibili: loadUrl }); - break; - case 2: - this.updateRecordUrl({ iqiyi: loadUrl }); - break; - default: - break; - } - } + this.$electron.ipcRenderer.on('store-pip-info', (e: Event, info: number[]) => { + this.$store.dispatch('updatePipSize', info.slice(2, 4)); + this.$store.dispatch('updatePipPos', info.slice(0, 2)); }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.$refs.webView.addEventListener('ipc-message', (evt: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 - const { channel, args }: { channel: string, args: - { dragover?: boolean, - files?: string[], - isFullScreen?: boolean, - windowSize?: number[] | null, - x?: number, - y?: number, - url?: string, - targetName?: string, - }[] } = evt; + // eslint-disable-next-line @typescript-eslint/no-explicit-any,complexity + this.currentBrowserView.webContents.addListener('ipc-message', (evt: any, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 switch (channel) { case 'open-url': - this.handleOpenUrl(args[0]); + this.handleOpenUrl(args); break; case 'dragover': case 'dragleave': - this.maskToShow = args[0].dragover; + this.maskToShow = args.dragover; break; case 'drop': this.maskToShow = false; - if ((args[0].files as string[]).length) { - this.dropFiles = args[0].files; + if ((args.files as string[]).length) { + this.currentBrowserView.setBounds({ + x: 0, y: 36, width: 0, height: 0, + }); + this.dropFiles = args.files; } break; case 'mousemove': @@ -375,23 +366,25 @@ export default { break; case 'left-drag': if (this.isPip) { - if (args[0].windowSize) { - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setBounds', [{ - x: args[0].x, - y: args[0].y, - width: args[0].windowSize[0], - height: args[0].windowSize[1], + if (args.windowSize) { + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setBounds', [{ + x: args.x, + y: args.y, + width: args.windowSize[0], + height: args.windowSize[1], }]); } else { - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', [args[0].x, args[0].y]); + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [args.x, args.y]); } } break; case 'fullscreenchange': - this.headerToShow = !args[0].isFullScreen; + if (!this.isPip) { + this.headerToShow = !args.isFullScreen; + } break; case 'keydown': - if (['INPUT', 'TEXTAREA'].includes(args[0].targetName as string)) { + if (['INPUT', 'TEXTAREA'].includes(args.targetName as string)) { this.acceleratorAvailable = false; } break; @@ -403,23 +396,50 @@ export default { this.$electron.ipcRenderer.on('quit', () => { this.quit = true; }); - this.$refs.webView.addEventListener('dom-ready', () => { // for webview test + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.$electron.ipcRenderer.on('update-header-to-show', (ev: any, headerToShow: boolean) => { + this.headerToShow = headerToShow; + }); + this.$electron.ipcRenderer.on('quit-pip', () => { + this.isPip = false; + }); + this.currentBrowserView.webContents.addListener('dom-ready', () => { // for webview test window.focus(); - this.$refs.webView.focus(); - if (process.env.NODE_ENV === 'development') this.$refs.webView.openDevTools(); + this.currentBrowserView.webContents.focus(); + if (process.env.NODE_ENV === 'development') this.currentBrowserView.webContents.openDevTools(); }); // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.$refs.webView.addEventListener('new-window', (e: any) => { - if (e.disposition !== 'new-window') { - this.handleOpenUrl(e); + this.currentBrowserView.webContents.addListener('new-window', (e: any, url: string, disposition: string) => { + if (disposition !== 'new-window') { + this.handleOpenUrl({ url }); } }); - this.$refs.webView.addEventListener('did-start-loading', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.currentBrowserView.webContents.addListener('did-start-navigation', (e: any, url: string) => { + if (!url || url === 'about:blank') return; this.startTime = new Date().getTime(); this.loadingState = true; + const loadUrl = this.currentBrowserView.webContents.getURL(); + this.currentUrl = loadUrl; + const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); + if (recordIndex !== -1) { + switch (recordIndex) { + case 0: + this.updateRecordUrl({ youtube: loadUrl }); + break; + case 1: + this.updateRecordUrl({ bilibili: loadUrl }); + break; + case 2: + this.updateRecordUrl({ iqiyi: loadUrl }); + break; + default: + break; + } + } }); - this.$refs.webView.addEventListener('did-stop-loading', () => { + this.currentBrowserView.webContents.addListener('did-stop-loading', () => { const loadingTime: number = new Date().getTime() - this.startTime; if (loadingTime % 3000 === 0) { this.loadingState = false; @@ -431,6 +451,7 @@ export default { }); }, beforeDestroy() { + this.$electron.ipcRenderer.send('remove-browser', this.isPip); asyncStorage.set('browsing', { pipSize: this.pipSize, pipPos: this.pipPos, @@ -438,8 +459,12 @@ export default { browsingPos: this.browsingPos, barrageOpen: this.barrageOpen, }).then(() => { - this.$store.dispatch(this.isPip ? 'updatePipSize' : 'updateBrowsingSize', this.winSize); - this.$store.dispatch(this.isPip ? 'updatePipPos' : 'updateBrowsingPos', this.winPos); + if (this.isPip) { + this.$electron.ipcRenderer.send('store-pip-info'); + } else { + this.$store.dispatch('updateBrowsingSize', this.winSize); + this.$store.dispatch('updateBrowsingPos', this.winPos); + } this.updateIsPip(false); }).finally(() => { if (this.backToLandingView) { @@ -449,7 +474,6 @@ export default { }, methods: { ...mapActions({ - updateInitialUrl: browsingActions.UPDATE_INITIAL_URL, updateRecordUrl: browsingActions.UPDATE_RECORD_URL, updateBarrageOpen: browsingActions.UPDATE_BARRAGE_OPEN, updateIsPip: browsingActions.UPDATE_IS_PIP, @@ -459,7 +483,7 @@ export default { if (this.isPip) { this.updateIsPip(false); } - this.updateInitialUrl(url); + this.currentBrowserView.webContents.loadURL(urlParseLax(url).protocol ? url : `https:${url}`); }, pipAdapter() { const parseUrl = urlParseLax(this.currentUrl); @@ -488,71 +512,94 @@ export default { this.pipBtnsKeepShow = false; }, handleWindowChangeEnterPip() { - const newDisplayId = this.$electron.screen + const newDisplayId = this.$electron.remote.screen .getDisplayNearestPoint({ x: this.winPos[0], y: this.winPos[1] }).id; const useDefaultPosition = !this.pipPos.length || this.oldDisplayId !== newDisplayId; this.oldDisplayId = newDisplayId; - this.$refs.webView.executeJavaScript(this.getVideoStyle, (result: CSSStyleDeclaration) => { - const videoAspectRatio = parseFloat(result.width as string) - / parseFloat(result.height as string); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [videoAspectRatio]); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setMinimumSize', [420, Math.round(420 / videoAspectRatio)]); - if (useDefaultPosition) { - this.$store.dispatch('updatePipPos', [window.screen.availLeft + 70, - window.screen.availTop + window.screen.availHeight - 236 - 70]) - .then(() => { - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', [window.screen.availLeft + 70, - window.screen.availTop + window.screen.availHeight - 236 - 70]); - }); - } else { - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', this.pipPos); - } - const calculateSize = this.pipSize[0] / this.pipSize[1] >= videoAspectRatio - ? [this.pipSize[0], Math.round(this.pipSize[0] / videoAspectRatio)] - : [Math.round(this.pipSize[1] * videoAspectRatio), this.pipSize[1]]; - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setSize', calculateSize); + this.currentPipBrowserView.setBounds({ + x: 0, y: 36, width: this.winSize[0], height: this.winSize[1] - 36, }); + this.currentPipBrowserView.setAutoResize({ width: true, height: true }); + this.currentBrowserView.webContents + .executeJavaScript(this.getVideoStyle, (result: CSSStyleDeclaration) => { + const videoAspectRatio = parseFloat(result.width as string) + / parseFloat(result.height as string); + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setAspectRatio', [videoAspectRatio]); + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setMinimumSize', [420, Math.round(420 / videoAspectRatio)]); + if (useDefaultPosition) { + this.$store.dispatch('updatePipPos', [window.screen.availLeft + 70, + window.screen.availTop + window.screen.availHeight - 236 - 70]) + .then(() => { + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [window.screen.availLeft + 70, + window.screen.availTop + window.screen.availHeight - 236 - 70]); + }); + } else { + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', this.pipPos); + } + const calculateSize = this.pipSize[0] / this.pipSize[1] >= videoAspectRatio + ? [this.pipSize[0], Math.round(this.pipSize[0] / videoAspectRatio)] + : [Math.round(this.pipSize[1] * videoAspectRatio), this.pipSize[1]]; + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setSize', calculateSize); + this.currentBrowserView.setBounds({ + x: 0, y: 0, width: calculateSize[0], height: calculateSize[1], + }); + }); }, handleWindowChangeExitPip() { - const newDisplayId = this.$electron.screen + const newDisplayId = this.$electron.remote.screen .getDisplayNearestPoint({ x: this.winPos[0], y: this.winPos[1] }).id; - if (this.oldDisplayId !== newDisplayId) { - windowRectService.calculateWindowRect( - this.browsingSize, - true, - this.winPos.concat(this.winSize), - ); - } else { - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setSize', this.browsingSize); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', this.browsingPos); - } + this.currentBrowserView.setAutoResize({ width: false, height: false }); + this.currentBrowserView.setBounds({ + x: 0, y: 36, width: this.winSize[0], height: this.winSize[1] - 36, + }); + // if (this.oldDisplayId !== newDisplayId) { + // windowRectService.calculateWindowRect( + // this.browsingSize, + // true, + // this.winPos.concat(this.winSize), + // ); + // } else { + // this.$electron.ipcRenderer.send('callMainWindowMethod', 'setSize', this.browsingSize); + // this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', this.browsingPos); + // } this.oldDisplayId = newDisplayId; - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setMinimumSize', [570, 375]); + // this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); + // this.$electron.ipcRenderer.send('callMainWindowMethod', 'setMinimumSize', [570, 375]); + this.currentBrowserView.setAutoResize({ width: true, height: true }); }, handleDanmuDisplay() { if (this.pipType === 'iqiyi') { this.updateBarrageOpen(!this.barrageOpen); - this.$refs.webView.executeJavaScript(this.iqiyiBarrage); + this.currentBrowserView.webContents.executeJavaScript(this.iqiyiBarrage); } else if (this.pipType === 'bilibili') { this.updateBarrageOpen(!this.barrageOpen); - this.$refs.webView.executeJavaScript(this.bilibiliBarrage); + this.currentBrowserView.webContents.executeJavaScript(this.bilibiliBarrage); } }, handleUrlForward() { - if (this.$refs.webView.canGoForward()) { - this.$refs.webView.goForward(); + if (!this.isPip) { + if (this.currentBrowserView.webContents.canGoForward()) { + this.currentBrowserView.webContents.goForward(); + } + } else if (this.currentPipBrowserView.webContents.canGoForward()) { + this.currentPipBrowserView.webContents.goForward(); } }, handleUrlBack() { - if (this.$refs.webView.canGoBack()) { - this.$refs.webView.goBack(); + if (!this.isPip) { + if (this.currentBrowserView.webContents.canGoBack()) { + this.currentBrowserView.webContents.goBack(); + } + } else if (this.currentPipBrowserView.webContents.canGoBack()) { + this.currentPipBrowserView.webContents.goBack(); } }, handleUrlReload() { - this.$refs.webView.reload(); if (this.isPip) { this.pipRestore = true; + this.currentPipBrowserView.webContents.reload(); + } else { + this.currentBrowserView.webContents.reload(); } }, handleEnterPip() { @@ -567,44 +614,45 @@ export default { }, othersAdapter() { this.handleWindowChangeEnterPip(); - this.$refs.webView.executeJavaScript(this.othersPip.adapter); + this.currentBrowserView.webContents.executeJavaScript(this.othersPip.adapter); }, othersWatcher() { - this.$refs.webView.executeJavaScript(this.othersPip.watcher); + this.currentBrowserView.webContents.executeJavaScript(this.othersPip.watcher); }, othersRecover() { - this.$refs.webView.executeJavaScript(this.othersPip.recover); + this.currentBrowserView.webContents.executeJavaScript(this.othersPip.recover); }, iqiyiAdapter() { this.handleWindowChangeEnterPip(); - this.$refs.webView.executeJavaScript(this.iqiyiPip.adapter); + this.currentBrowserView.webContents.executeJavaScript(this.iqiyiPip.adapter); }, iqiyiWatcher() { - this.$refs.webView.executeJavaScript(this.iqiyiPip.watcher); + this.currentBrowserView.webContents.executeJavaScript(this.iqiyiPip.watcher); }, iqiyiRecover() { - this.$refs.webView.executeJavaScript(this.iqiyiPip.recover); + this.currentBrowserView.webContents.executeJavaScript(this.iqiyiPip.recover); }, youtubeAdapter() { this.handleWindowChangeEnterPip(); - this.$refs.webView.executeJavaScript(youtube.adapter); + this.currentBrowserView.webContents.executeJavaScript(youtube.adapter); }, youtubeRecover() { - this.$refs.webView.executeJavaScript(youtube.recover); + this.currentBrowserView.webContents.executeJavaScript(youtube.recover); }, bilibiliAdapter() { - this.$refs.webView.executeJavaScript(bilibiliFindType, (r: (HTMLElement | null)[]) => { - this.bilibiliType = ['bangumi', 'videoStreaming', 'iframeStreaming', 'video'][r.findIndex(i => i)] || 'others'; - }).then(() => { - this.handleWindowChangeEnterPip(); - this.$refs.webView.executeJavaScript(this.bilibiliPip.adapter); - }); + this.currentBrowserView.webContents + .executeJavaScript(bilibiliFindType, (r: (HTMLElement | null)[]) => { + this.bilibiliType = ['bangumi', 'videoStreaming', 'iframeStreaming', 'video'][r.findIndex(i => i)] || 'others'; + }).then(() => { + this.handleWindowChangeEnterPip(); + this.currentBrowserView.webContents.executeJavaScript(this.bilibiliPip.adapter); + }); }, bilibiliWatcher() { - this.$refs.webView.executeJavaScript(this.bilibiliPip.watcher); + this.currentBrowserView.webContents.executeJavaScript(this.bilibiliPip.watcher); }, bilibiliRecover() { - this.$refs.webView.executeJavaScript(this.bilibiliPip.recover); + this.currentBrowserView.webContents.executeJavaScript(this.bilibiliPip.recover); }, }, }; @@ -616,6 +664,7 @@ export default { width: 100vw; display: flex; flex-direction: column; + background: rgba(255, 255, 255, 1); .web-view { flex: 1; background: rgba(255, 255, 255, 1); diff --git a/src/renderer/components/BrowsingView/BrowsingFavicons.vue b/src/renderer/components/BrowsingView/BrowsingFavicons.vue index 93a54847c9..11df63fbff 100644 --- a/src/renderer/components/BrowsingView/BrowsingFavicons.vue +++ b/src/renderer/components/BrowsingView/BrowsingFavicons.vue @@ -52,10 +52,6 @@ export default { type: Object, required: true, }, - updateInitialUrl: { - type: Function, - required: true, - }, }, data() { return { @@ -96,7 +92,10 @@ export default { this.faviconIndex = -1; }, handleFavOpen(item: { name: string, type: string, url: string }) { - this.updateInitialUrl(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); + this.$electron.remote.BrowserView.getAllViews()[0].webContents + .loadURL(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); + this.$electron.remote.BrowserView.getAllViews()[1].webContents + .loadURL(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); }, handleFavAnimEnd(e: AnimationEvent) { const target = e.target as HTMLElement; diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index 86294ca02f..bd8526cf6f 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -16,7 +16,6 @@ /> diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 148421dc9c..c4e2f49465 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -74,7 +74,6 @@ import youtube from '../../shared/pip/youtube'; import iqiyi, { iqiyiBarrageAdapt } from '../../shared/pip/iqiyi'; import globalPip from '../../shared/pip/others'; import { getValidVideoRegex, getValidSubtitleRegex } from '../../shared/utils'; -import MenuService from '@/services/menu/MenuService'; export default { name: 'BrowsingView', @@ -103,7 +102,6 @@ export default { pipBtnsKeepShow: false, asyncTasksDone: false, headerToShow: true, - menuService: null, pipRestore: false, acceleratorAvailable: true, oldDisplayId: -1, @@ -143,6 +141,9 @@ export default { }, }, watch: { + barrageOpen(val: boolean) { + this.$electron.ipcRenderer.send('update-danmu-state', val); + }, dropFiles(val: string[]) { this.backToLandingView = false; const onlyFolders = val.every((file: fs.PathLike) => fs.statSync(file).isDirectory()); @@ -159,7 +160,6 @@ export default { } }, isPip(val: boolean) { - this.menuService.updatePip(val); if (val) { this.$electron.ipcRenderer.send('enter-pip'); this.$store.dispatch('updateBrowsingSize', this.winSize); @@ -172,6 +172,8 @@ export default { this.timeout = false; }, 3000); this.pipAdapter(); + const opacity = ['youtube', 'others'].includes(this.pipType) || (this.pipType === 'bilibili' && this.bilibiliType === 'others') ? 0.2 : 1; + this.$electron.ipcRenderer.send('init-danmu-state', { opacity, barrageOpen: opacity === 1 ? this.barrageOpen : false }); } else { if (!this.browsingWindowClose) { this.$electron.ipcRenderer.send('exit-pip'); @@ -189,6 +191,109 @@ export default { this.othersRecover(); } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any,complexity + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 + switch (channel) { + case 'open-url': + this.handleOpenUrl(args); + break; + case 'dragover': + case 'dragleave': + this.maskToShow = args.dragover; + break; + case 'drop': + this.maskToShow = false; + if ((args.files as string[]).length) { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ + x: 0, y: 36, width: 0, height: 0, + }); + this.dropFiles = args.files; + } + break; + case 'mousemove': + if (this.isPip) { + this.timeout = true; + if (this.timer) { + clearTimeout(this.timer); + } + this.timer = setTimeout(() => { + this.timeout = false; + }, 3000); + } + break; + case 'left-drag': + if (this.isPip) { + if (args.windowSize) { + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setBounds', [{ + x: args.x, + y: args.y, + width: args.windowSize[0], + height: args.windowSize[1], + }]); + } else { + this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [args.x, args.y]); + } + } + break; + case 'fullscreenchange': + if (!this.isPip) { + this.headerToShow = !args.isFullScreen; + } + break; + case 'keydown': + if (['INPUT', 'TEXTAREA'].includes(args.targetName as string)) { + this.acceleratorAvailable = false; + } + break; + default: + console.warn(`Unhandled ipc-message: ${channel}`, args); + break; + } + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('dom-ready', () => { // for webview test + window.focus(); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.focus(); + if (process.env.NODE_ENV === 'development') this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.openDevTools(); + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { + if (disposition !== 'new-window') { + this.handleOpenUrl({ url }); + } + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', (e: Event, url: string) => { + if (!url || url === 'about:blank') return; + this.startTime = new Date().getTime(); + this.loadingState = true; + const loadUrl = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getURL(); + this.currentUrl = loadUrl; + const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); + if (recordIndex !== -1) { + switch (recordIndex) { + case 0: + this.updateRecordUrl({ youtube: loadUrl }); + break; + case 1: + this.updateRecordUrl({ bilibili: loadUrl }); + break; + case 2: + this.updateRecordUrl({ iqiyi: loadUrl }); + break; + default: + break; + } + } + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-stop-loading', () => { + const loadingTime: number = new Date().getTime() - this.startTime; + if (loadingTime % 3000 === 0) { + this.loadingState = false; + } else { + setTimeout(() => { + this.loadingState = false; + }, 3000 - (loadingTime % 3000)); + } + }); }, pipSize() { if (this.isPip && this.pipType !== 'youtube') { @@ -203,10 +308,10 @@ export default { }, loadingState(val: boolean) { const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.getURL(); + .getBrowserViews()[0].webContents.getURL(); const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); - this.$electron.ipcRenderer.send('update-enabled', 'history.back', this.$electron.remote.getCurrentWindow().getBrowserView().webContents.canGoBack()); - this.$electron.ipcRenderer.send('update-enabled', 'history.forward', this.$electron.remote.getCurrentWindow().getBrowserView().webContents.canGoForward()); + this.$electron.ipcRenderer.send('update-enabled', 'history.back', this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.canGoBack()); + this.$electron.ipcRenderer.send('update-enabled', 'history.forward', this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.canGoForward()); if (val) { this.hasVideo = false; this.$electron.ipcRenderer.send('update-enabled', 'window.pip', false); @@ -214,16 +319,16 @@ export default { hasVideo: this.hasVideo, url: loadUrl, canGoBack: this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.canGoBack(), + .getBrowserViews()[0].webContents.canGoBack(), canGoForward: this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.canGoForward(), + .getBrowserViews()[0].webContents.canGoForward(), }); } else { if (this.pipRestore) { this.pipAdapter(); this.pipRestore = false; } - this.$electron.remote.getCurrentWindow().getBrowserView().webContents + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents .executeJavaScript(this.calculateVideoNum, (r: number) => { this.hasVideo = recordIndex === 0 && !getVideoId(loadUrl).id ? false : !!r; this.$electron.ipcRenderer.send('update-enabled', 'window.pip', this.hasVideo); @@ -231,15 +336,15 @@ export default { hasVideo: this.hasVideo, url: loadUrl, canGoBack: this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.canGoBack(), + .getBrowserViews()[0].webContents.canGoBack(), canGoForward: this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.canGoForward(), + .getBrowserViews()[0].webContents.canGoForward(), }); }); } }, headerToShow(val: boolean) { - const currentView = this.$electron.remote.getCurrentWindow().getBrowserView(); + const currentView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; if (!val) { currentView.setBounds({ x: 0, y: 0, width: window.screen.width, height: window.screen.height, @@ -260,14 +365,13 @@ export default { ); this.$store.dispatch('updateBrowsingPos', this.winPos); this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); - this.$electron.remote.getCurrentWindow().getBrowserView().setBounds({ + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ x: 0, y: 36, width: this.browsingSize[0], height: this.browsingSize[1] - 36, }); this.$electron.remote.getCurrentWindow() - .getBrowserView().setAutoResize({ width: true, height: true }); + .getBrowserViews()[0].setAutoResize({ width: true, height: true }); }, mounted() { - this.menuService = new MenuService(); this.$bus.$on('toggle-reload', this.handleUrlReload); this.$bus.$on('toggle-back', this.handleUrlBack); this.$bus.$on('toggle-forward', this.handleUrlForward); @@ -286,13 +390,13 @@ export default { }); window.addEventListener('focus', () => { this.$electron.ipcRenderer.send('update-focused-window', true); - this.$electron.ipcRenderer.send('update-enabled', 'history.back', this.$electron.remote.getCurrentWindow().getBrowserView().webContents.canGoBack()); - this.$electron.ipcRenderer.send('update-enabled', 'history.forward', this.$electron.remote.getCurrentWindow().getBrowserView().webContents.canGoForward()); + this.$electron.ipcRenderer.send('update-enabled', 'history.back', this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.canGoBack()); + this.$electron.ipcRenderer.send('update-enabled', 'history.forward', this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.canGoForward()); this.$electron.ipcRenderer.send('update-enabled', 'window.keepPlayingWindowFront', false); const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.getURL(); + .getBrowserViews()[0].webContents.getURL(); const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); - this.$electron.remote.getCurrentWindow().getBrowserView().webContents + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents .executeJavaScript(this.calculateVideoNum, (r: number) => { this.hasVideo = recordIndex === 0 && !getVideoId(loadUrl).id ? false : !!r; this.$electron.ipcRenderer.send('update-enabled', 'window.pip', this.hasVideo); @@ -350,11 +454,30 @@ export default { name: 'landing-view', }); }); + this.$electron.ipcRenderer.on('handle-exit-pip', () => { + this.handleExitPip(); + }); + this.$electron.ipcRenderer.on('handle-danmu-display', () => { + this.handleDanmuDisplay(); + }); this.$electron.ipcRenderer.on('store-pip-pos', (e: Event, pos: number[]) => { this.$store.dispatch('updatePipPos', pos); }); + this.$electron.ipcRenderer.on('pip-window-size', (e: Event, size: number[]) => { + this.$store.dispatch('updatePipSize', size); + }); + this.$electron.ipcRenderer.on('quit', () => { + this.quit = true; + }); + this.$electron.ipcRenderer.on('update-header-to-show', (ev: Event, headerToShow: boolean) => { + this.headerToShow = headerToShow; + }); + this.$electron.ipcRenderer.on('update-pip-state', () => { + this.browsingWindowClose = true; + this.updateIsPip(false); + }); // eslint-disable-next-line @typescript-eslint/no-explicit-any,complexity - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 switch (channel) { case 'open-url': this.handleOpenUrl(args); @@ -366,7 +489,7 @@ export default { case 'drop': this.maskToShow = false; if ((args.files as string[]).length) { - this.$electron.remote.getCurrentWindow().getBrowserView().setBounds({ + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ x: 0, y: 36, width: 0, height: 0, }); this.dropFiles = args.files; @@ -412,35 +535,22 @@ export default { break; } }); - this.$electron.ipcRenderer.on('pip-window-size', (e: Event, size: number[]) => { - this.$store.dispatch('updatePipSize', size); - }); - this.$electron.ipcRenderer.on('quit', () => { - this.quit = true; - }); - this.$electron.ipcRenderer.on('update-header-to-show', (ev: Event, headerToShow: boolean) => { - this.headerToShow = headerToShow; - }); - this.$electron.ipcRenderer.on('update-pip-state', () => { - this.browsingWindowClose = true; - this.updateIsPip(false); - }); - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.addListener('dom-ready', () => { // for webview test + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('dom-ready', () => { // for webview test window.focus(); - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.focus(); - if (process.env.NODE_ENV === 'development') this.$electron.remote.getCurrentWindow().getBrowserView().webContents.openDevTools(); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.focus(); + if (process.env.NODE_ENV === 'development') this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.openDevTools(); }); - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { if (disposition !== 'new-window') { this.handleOpenUrl({ url }); } }); - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.addListener('did-start-navigation', (e: Event, url: string) => { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', (e: Event, url: string) => { if (!url || url === 'about:blank') return; this.startTime = new Date().getTime(); this.loadingState = true; const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.getURL(); + .getBrowserViews()[0].webContents.getURL(); this.currentUrl = loadUrl; const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); if (recordIndex !== -1) { @@ -459,7 +569,7 @@ export default { } } }); - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.addListener('did-stop-loading', () => { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-stop-loading', () => { const loadingTime: number = new Date().getTime() - this.startTime; if (loadingTime % 3000 === 0) { this.loadingState = false; @@ -503,7 +613,7 @@ export default { if (this.isPip) { this.updateIsPip(false); } - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.loadURL(urlParseLax(url).protocol ? url : `https:${url}`); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.loadURL(urlParseLax(url).protocol ? url : `https:${url}`); }, pipAdapter() { const parseUrl = urlParseLax(this.currentUrl); @@ -531,10 +641,14 @@ export default { handleMouseleave() { this.pipBtnsKeepShow = false; }, + currentPipBrowserView() { + const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; + return this.$electron.remote.BrowserView.getAllViews().filter( + (view: Electron.BrowserView) => view.id !== currentBrowserView.id && view.id <= 2, + )[0]; + }, handleWindowChangeEnterPip() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; + const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; const newDisplayId = this.$electron.remote.screen .getDisplayNearestPoint({ x: this.winPos[0], y: this.winPos[1] }).id; const useDefaultPosition = !this.pipPos.length @@ -544,7 +658,7 @@ export default { x: 0, y: 36, width: this.winSize[0], height: this.winSize[1] - 36, }); currentBrowserView.setAutoResize({ width: true, height: true }); - currentPipBrowserView.webContents + this.currentPipBrowserView().webContents .executeJavaScript(this.getVideoStyle, (result: CSSStyleDeclaration) => { const videoAspectRatio = parseFloat(result.width as string) / parseFloat(result.height as string); @@ -564,8 +678,14 @@ export default { ? [this.pipSize[0], Math.round(this.pipSize[0] / videoAspectRatio)] : [Math.round(this.pipSize[1] * videoAspectRatio), this.pipSize[1]]; this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setSize', calculateSize); - currentPipBrowserView.setBounds({ - x: 0, y: 0, width: calculateSize[0], height: calculateSize[1], + this.$electron.ipcRenderer.send('set-control-bounds', { + x: Math.round(calculateSize[0] - 65), + y: Math.round(calculateSize[1] / 2 - 54), + width: 50, + height: 104, + }); + this.currentPipBrowserView().setBounds({ + x: 0, y: 0, width: Math.round(calculateSize[0]), height: Math.round(calculateSize[1]), }); }); }, @@ -573,8 +693,8 @@ export default { const newDisplayId = this.$electron.remote.screen .getDisplayNearestPoint({ x: this.winPos[0], y: this.winPos[1] }).id; this.$electron.remote.getCurrentWindow() - .getBrowserView().setAutoResize({ width: false, height: false }); - this.$electron.remote.getCurrentWindow().getBrowserView().setBounds({ + .getBrowserViews()[0].setAutoResize({ width: false, height: false }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ x: 0, y: 36, width: this.winSize[0], height: this.winSize[1] - 36, }); // if (this.oldDisplayId !== newDisplayId) { @@ -591,35 +711,34 @@ export default { // this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); // this.$electron.ipcRenderer.send('callMainWindowMethod', 'setMinimumSize', [570, 375]); this.$electron.remote.getCurrentWindow() - .getBrowserView().setAutoResize({ width: true, height: true }); + .getBrowserViews()[0].setAutoResize({ width: true, height: true }); }, handleDanmuDisplay() { if (this.pipType === 'iqiyi') { this.updateBarrageOpen(!this.barrageOpen); - this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.executeJavaScript(this.iqiyiBarrage); + this.currentPipBrowserView().webContents.executeJavaScript(this.iqiyiBarrage); } else if (this.pipType === 'bilibili') { this.updateBarrageOpen(!this.barrageOpen); - this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.executeJavaScript(this.bilibiliBarrage); + this.currentPipBrowserView().webContents.executeJavaScript(this.bilibiliBarrage); } }, handleUrlForward() { - if (this.$electron.remote.getCurrentWindow().getBrowserView().webContents.canGoForward()) { - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.goForward(); + if (this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.canGoForward()) { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.goForward(); } }, handleUrlBack() { - if (this.$electron.remote.getCurrentWindow().getBrowserView().webContents.canGoBack()) { - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.goBack(); + if (this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.canGoBack()) { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.goBack(); } }, handleUrlReload() { if (this.isPip) { this.pipRestore = true; - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.reload(); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.reload(); } else { - this.$electron.remote.getCurrentWindow().getBrowserView().webContents.reload(); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.reload(); } }, handleEnterPip() { @@ -633,71 +752,50 @@ export default { } }, othersAdapter() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; this.handleWindowChangeEnterPip(); - currentPipBrowserView.webContents.executeJavaScript(this.othersPip.adapter); + this.currentPipBrowserView().webContents.executeJavaScript(this.othersPip.adapter); }, othersWatcher() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; - currentPipBrowserView.webContents.executeJavaScript(this.othersPip.watcher); + this.currentPipBrowserView().webContents.executeJavaScript(this.othersPip.watcher); }, othersRecover() { this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.executeJavaScript(this.othersPip.recover); + .getBrowserViews()[0].webContents.executeJavaScript(this.othersPip.recover); }, iqiyiAdapter() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; this.handleWindowChangeEnterPip(); - currentPipBrowserView.webContents.executeJavaScript(this.iqiyiPip.adapter); + this.currentPipBrowserView().webContents.executeJavaScript(this.iqiyiPip.adapter); }, iqiyiWatcher() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; - currentPipBrowserView.webContents.executeJavaScript(this.iqiyiPip.watcher); + this.currentPipBrowserView().webContents.executeJavaScript(this.iqiyiPip.watcher); }, iqiyiRecover() { this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.executeJavaScript(this.iqiyiPip.recover); + .getBrowserViews()[0].webContents.executeJavaScript(this.iqiyiPip.recover); }, youtubeAdapter() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; this.handleWindowChangeEnterPip(); - currentPipBrowserView.webContents.executeJavaScript(youtube.adapter); + this.currentPipBrowserView().webContents.executeJavaScript(youtube.adapter); }, youtubeRecover() { this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.executeJavaScript(youtube.recover); + .getBrowserViews()[0].webContents.executeJavaScript(youtube.recover); }, bilibiliAdapter() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; - currentPipBrowserView.webContents + this.currentPipBrowserView().webContents .executeJavaScript(bilibiliFindType, (r: (HTMLElement | null)[]) => { this.bilibiliType = ['bangumi', 'videoStreaming', 'iframeStreaming', 'video'][r.findIndex(i => i)] || 'others'; }).then(() => { this.handleWindowChangeEnterPip(); - currentPipBrowserView.webContents.executeJavaScript(this.bilibiliPip.adapter); + this.currentPipBrowserView().webContents.executeJavaScript(this.bilibiliPip.adapter); }); }, bilibiliWatcher() { - const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserView(); - const currentPipBrowserView = this.$electron.remote.BrowserView.getAllViews() - .filter((view: Electron.BrowserView) => view.id !== currentBrowserView.id)[0]; - currentPipBrowserView.webContents.executeJavaScript(this.bilibiliPip.watcher); + this.currentPipBrowserView().webContents.executeJavaScript(this.bilibiliPip.watcher); }, bilibiliRecover() { this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.executeJavaScript(this.bilibiliPip.recover); + .getBrowserViews()[0].webContents.executeJavaScript(this.bilibiliPip.recover); }, }, }; diff --git a/src/renderer/services/menu/MenuService.ts b/src/renderer/services/menu/MenuService.ts index f6ca4ed979..66032aadd3 100644 --- a/src/renderer/services/menu/MenuService.ts +++ b/src/renderer/services/menu/MenuService.ts @@ -36,10 +36,6 @@ export default class MenuService { ipcRenderer.send('update-browsingview-on-top', topOnWindow); } - public updatePip(isPip: boolean) { - ipcRenderer.send('update-pip', isPip); - } - public updateMenuItemChecked(id: string, checked: boolean) { ipcRenderer.send('update-checked', id, checked); } diff --git a/static/pip/pipControl.html b/static/pip/pipControl.html new file mode 100644 index 0000000000..b9e711cf91 --- /dev/null +++ b/static/pip/pipControl.html @@ -0,0 +1,43 @@ + + + + + Title + + + +
+ + +
+ + diff --git a/static/pip/preload.js b/static/pip/preload.js index 2007508269..603815397b 100644 --- a/static/pip/preload.js +++ b/static/pip/preload.js @@ -8,6 +8,7 @@ let mousedown = false; let isDragging = false; let mousedownPos = null; let windowSize = null; +let pipTimer = 0; function sendToHost(channel, message) { ipcRenderer.send(channel, message); } @@ -16,6 +17,39 @@ function getRatio() { } document.addEventListener('DOMContentLoaded', () => { + const danmu = document.querySelector('.danmu'); + const pip = document.querySelector('.pip'); + const pipBtns = document.querySelector('.pip-buttons'); + if (pipBtns) { + pipBtns.style.display = ''; + pipBtns.addEventListener('mouseenter', () => { + if (pipTimer) clearTimeout(pipTimer); + sendToHost('mouseenter', 'mouseenter'); + pipBtns.style.display = ''; + }); + pipTimer = setTimeout(() => { + pipBtns.style.display = 'none'; + }, 3000); + } + if (danmu) { + danmu.addEventListener('mouseup', () => { + sendToHost('danmu', 'danmu'); + }); + } + if (pip) { + pip.addEventListener('mouseup', () => { + sendToHost('pip', 'pip'); + }); + } + window.addEventListener('mouseleave', (evt) => { + if (!pipBtns) { + const winSize = remote.getCurrentWindow().getSize(); + if (evt.pageX <= 0 || evt.pageY <= 0 + || evt.pageX >= winSize[0] || evt.pageY >= winSize[1]) { + sendToHost('mouseleave', 'leave'); + } + } + }, true); window.addEventListener('mousedown', (evt) => { mousedown = true; mousedownPos = [evt.clientX, evt.clientY]; @@ -43,7 +77,10 @@ document.addEventListener('DOMContentLoaded', () => { }); } window.addEventListener('mousemove', (evt) => { - sendToHost('mousemove', 'isMoving'); + if (!pipBtns && remote.getCurrentWindow().getBrowserViews().length > 1) { + if (pipTimer) clearTimeout(pipTimer); + sendToHost('mousemove', 'isMoving'); + } if (mousedown) isDragging = true; }, true); window.addEventListener('click', (evt) => { From 2a794c80a2158d3f58152c1547ceb172619a9f55 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Wed, 14 Aug 2019 18:42:00 +0800 Subject: [PATCH 006/123] feat(browsingview): support to shift pip in two windows --- src/main/index.js | 47 +++++-- src/renderer/components/BrowsingPip.vue | 8 +- src/renderer/components/BrowsingView.vue | 168 +++++++---------------- src/renderer/main.ts | 2 +- 4 files changed, 88 insertions(+), 137 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index e72e0fc839..991c953921 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -69,6 +69,7 @@ let pipControlView = null; let browserViews = []; let tray = null; let pipTimer = 0; +let initialUrl = ''; let needToRestore = false; let inited = false; let hideBrowsingWindow = false; @@ -127,6 +128,7 @@ function handleBossKey() { } function createPipControlView() { + if (pipControlView) pipControlView.destroy(); pipControlView = new BrowserView({ webPreferences: { preload: `${require('path').resolve(__static, 'pip/preload.js')}`, @@ -424,8 +426,17 @@ function registerMainWindowEvent(mainWindow) { ipcMain.on('pip-window-size', (evt, size) => { mainWindow.send('pip-window-size', size); }); + ipcMain.on('pip-window-close', () => { + const views = browsingWindow.getBrowserViews(); + browsingWindow.removeBrowserView(views[0]); + browsingWindow.removeBrowserView(views[1]); + views[0].webContents.loadURL(initialUrl).then(() => { + views[0].webContents.clearHistory(); + }); + }); ipcMain.on('remove-browser', () => { - mainWindow.removeBrowserView(mainWindow.getBrowserView()); + const mainView = mainWindow.getBrowserView(); + mainWindow.removeBrowserView(mainView); if (browsingWindow) { const views = browsingWindow.getBrowserViews(); views.forEach((view) => { @@ -433,17 +444,18 @@ function registerMainWindowEvent(mainWindow) { }); } browserViews.forEach((view) => { - view.destroy(); + view.webContents.loadURL(initialUrl).then(() => { + view.webContents.clearHistory(); + }); }); - pipControlView.destroy(); if (browsingWindow) browsingWindow.hide(); }); ipcMain.on('update-pip-state', () => { mainWindow.send('update-pip-state'); }); ipcMain.on('create-browser-view', (evt, args) => { - const isDestroyed = browserViews.filter(view => view.isDestroyed()).length; - if (!browserViews.length || isDestroyed) { + initialUrl = args.url; + if (!browserViews.length) { browserViews = [ new BrowserView({ webPreferences: { @@ -505,17 +517,34 @@ function registerMainWindowEvent(mainWindow) { pipControlView.webContents.executeJavaScript('document.querySelector(".pip-buttons").style.display = "none";'); } }); + ipcMain.on('shift-pip', () => { + const mainView = mainWindow.getBrowserView(); + mainWindow.removeBrowserView(mainView); + browsingWindow.getBrowserViews().forEach((view) => { + browsingWindow.removeBrowserView(view); + view.webContents.loadURL(initialUrl); + }); + createPipControlView(); + browsingWindow.addBrowserView(mainView); + browsingWindow.addBrowserView(pipControlView); + mainWindow.addBrowserView(browserViews.find(view => view.id !== mainView.id)); + pipControlView.webContents.openDevTools(); + pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); + pipControlView.setBackgroundColor('#00FFFFFF'); + browsingWindow.blur(); + browsingWindow.focus(); + }); ipcMain.on('enter-pip', () => { const mainView = mainWindow.getBrowserView(); createPipControlView(); if (!browsingWindow) { createBrowsingWindow(); browsingWindow.openDevTools(); - const browView = BrowserView.getAllViews().find(view => view.id !== mainView.id); + const browView = browserViews.find(view => view.id !== mainView.id); mainWindow.removeBrowserView(mainView); + mainWindow.addBrowserView(browView); browsingWindow.addBrowserView(mainView); browsingWindow.addBrowserView(pipControlView); - mainWindow.addBrowserView(browView); browsingWindow.show(); } else { const browView = browsingWindow.getBrowserView(); @@ -526,11 +555,11 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.addBrowserView(pipControlView); browsingWindow.show(); } - browsingWindow.blur(); - browsingWindow.focus(); pipControlView.webContents.openDevTools(); pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); pipControlView.setBackgroundColor('#00FFFFFF'); + browsingWindow.blur(); + browsingWindow.focus(); }); ipcMain.on('set-control-bounds', (evt, args) => { if (pipControlView) pipControlView.setBounds(args); diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index 39db8004b1..4966ea7737 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -31,13 +31,7 @@ export default { }, 100)); window.addEventListener('beforeunload', () => { electron.ipcRenderer.send('store-pip-pos'); - const view = electron.remote.getCurrentWindow().getBrowserView(); - if (view) { - if (view.webContents.canGoBack()) { - view.webContents.goBack(); - } - electron.remote.getCurrentWindow().removeBrowserView(view); - } + electron.ipcRenderer.send('pip-window-close'); electron.ipcRenderer.send('update-pip-state'); }); }, diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index c4e2f49465..5e09ad9c47 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -29,29 +29,6 @@ v-show="loadingState && headerToShow" class="loading-state loading-animation" /> -
- - -
@@ -66,7 +43,6 @@ import getVideoId from 'get-video-id'; import { windowRectService } from '@/services/window/WindowRectService'; import { Browsing as browsingActions } from '@/store/actionTypes'; import BrowsingHeader from '@/components/BrowsingView/BrowsingHeader.vue'; -import Icon from '@/components/BaseIconContainer.vue'; import asyncStorage from '@/helpers/asyncStorage'; import NotificationBubble from '@/components/NotificationBubble.vue'; import { bilibili, bilibiliFindType, bilibiliBarrageAdapt } from '../../shared/pip/bilibili'; @@ -79,7 +55,6 @@ export default { name: 'BrowsingView', components: { 'browsing-header': BrowsingHeader, - Icon, NotificationBubble, }, data() { @@ -95,8 +70,6 @@ export default { dropFiles: [], hasVideo: false, currentUrl: '', - timeout: false, - timer: 0, calculateVideoNum: 'var iframe = document.querySelector("iframe");if (iframe && iframe.contentDocument) {document.getElementsByTagName("video").length + iframe.contentDocument.getElementsByTagName("video").length} else {document.getElementsByTagName("video").length}', getVideoStyle: 'getComputedStyle(document.querySelector("video") || document.querySelector("iframe").contentDocument.querySelector("video"))', pipBtnsKeepShow: false, @@ -133,12 +106,6 @@ export default { othersPip() { return globalPip(this.pipSize); }, - danmuType() { - return this.barrageOpen ? 'danmu' : 'noDanmu'; - }, - danmuIconState() { - return ['youtube', 'others'].includes(this.pipType) || (this.pipType === 'bilibili' && this.bilibiliType === 'others') ? 0.2 : 1; - }, }, watch: { barrageOpen(val: boolean) { @@ -159,38 +126,7 @@ export default { this.$electron.ipcRenderer.send('drop-subtitle', val); } }, - isPip(val: boolean) { - if (val) { - this.$electron.ipcRenderer.send('enter-pip'); - this.$store.dispatch('updateBrowsingSize', this.winSize); - this.$store.dispatch('updateBrowsingPos', this.winPos); - this.timeout = true; - if (this.timer) { - clearTimeout(this.timer); - } - this.timer = setTimeout(() => { - this.timeout = false; - }, 3000); - this.pipAdapter(); - const opacity = ['youtube', 'others'].includes(this.pipType) || (this.pipType === 'bilibili' && this.bilibiliType === 'others') ? 0.2 : 1; - this.$electron.ipcRenderer.send('init-danmu-state', { opacity, barrageOpen: opacity === 1 ? this.barrageOpen : false }); - } else { - if (!this.browsingWindowClose) { - this.$electron.ipcRenderer.send('exit-pip'); - this.$electron.ipcRenderer.send('store-pip-pos'); - this.handleWindowChangeExitPip(); - } - this.browsingWindowClose = false; - if (this.pipType === 'youtube') { - this.youtubeRecover(); - } else if (this.pipType === 'bilibili') { - this.bilibiliRecover(); - } else if (this.pipType === 'iqiyi') { - this.iqiyiRecover(); - } else { - this.othersRecover(); - } - } + isPip() { // eslint-disable-next-line @typescript-eslint/no-explicit-any,complexity this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 switch (channel) { @@ -210,17 +146,6 @@ export default { this.dropFiles = args.files; } break; - case 'mousemove': - if (this.isPip) { - this.timeout = true; - if (this.timer) { - clearTimeout(this.timer); - } - this.timer = setTimeout(() => { - this.timeout = false; - }, 3000); - } - break; case 'left-drag': if (this.isPip) { if (args.windowSize) { @@ -380,8 +305,14 @@ export default { if (this.acceleratorAvailable) { if (!state) { this.updateIsPip(false); + this.exitPipOperation(); } else if (this.hasVideo) { - this.updateIsPip(true); + if (this.isPip) { + this.shiftPipOperation(); + } else { + this.updateIsPip(true); + this.enterPipOperation(); + } } } else { this.acceleratorAvailable = true; @@ -426,27 +357,6 @@ export default { this.$electron.remote.app.quit(); } }); - (document.querySelector('#app') as HTMLElement).addEventListener('mouseleave', () => { - setTimeout(() => { - if (this.isPip) { - this.timeout = false; - if (this.timer) { - clearTimeout(this.timer); - } - } - }, 50); - }); - window.addEventListener('mousemove', () => { - if (this.isPip && !this.pipBtnsKeepShow && this.isFocused) { - this.timeout = true; - if (this.timer) { - clearTimeout(this.timer); - } - this.timer = setTimeout(() => { - this.timeout = false; - }, 3000); - } - }); this.$bus.$on('back-to-landingview', () => { this.backToLandingView = true; this.$bus.$off(); @@ -495,17 +405,6 @@ export default { this.dropFiles = args.files; } break; - case 'mousemove': - if (this.isPip) { - this.timeout = true; - if (this.timer) { - clearTimeout(this.timer); - } - this.timer = setTimeout(() => { - this.timeout = false; - }, 3000); - } - break; case 'left-drag': if (this.isPip) { if (args.windowSize) { @@ -631,16 +530,6 @@ export default { this.othersAdapter(); } }, - handleMouseenter() { - this.pipBtnsKeepShow = true; - this.timeout = true; - if (this.timer) { - clearTimeout(this.timer); - } - }, - handleMouseleave() { - this.pipBtnsKeepShow = false; - }, currentPipBrowserView() { const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; return this.$electron.remote.BrowserView.getAllViews().filter( @@ -741,13 +630,52 @@ export default { this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.reload(); } }, + shiftPipOperation() { + this.$electron.ipcRenderer.send('shift-pip'); + this.$store.dispatch('updateBrowsingSize', this.winSize); + this.$store.dispatch('updateBrowsingPos', this.winPos); + this.pipAdapter(); + const opacity = ['youtube', 'others'].includes(this.pipType) || (this.pipType === 'bilibili' && this.bilibiliType === 'others') ? 0.2 : 1; + this.$electron.ipcRenderer.send('init-danmu-state', { opacity, barrageOpen: opacity === 1 ? this.barrageOpen : false }); + }, + enterPipOperation() { + this.$electron.ipcRenderer.send('enter-pip'); + this.$store.dispatch('updateBrowsingSize', this.winSize); + this.$store.dispatch('updateBrowsingPos', this.winPos); + this.pipAdapter(); + const opacity = ['youtube', 'others'].includes(this.pipType) || (this.pipType === 'bilibili' && this.bilibiliType === 'others') ? 0.2 : 1; + this.$electron.ipcRenderer.send('init-danmu-state', { opacity, barrageOpen: opacity === 1 ? this.barrageOpen : false }); + }, + exitPipOperation() { + if (!this.browsingWindowClose) { + this.$electron.ipcRenderer.send('exit-pip'); + this.$electron.ipcRenderer.send('store-pip-pos'); + this.handleWindowChangeExitPip(); + } + this.browsingWindowClose = false; + if (this.pipType === 'youtube') { + this.youtubeRecover(); + } else if (this.pipType === 'bilibili') { + this.bilibiliRecover(); + } else if (this.pipType === 'iqiyi') { + this.iqiyiRecover(); + } else { + this.othersRecover(); + } + }, handleEnterPip() { if (this.hasVideo) { - this.updateIsPip(true); + if (this.isPip) { + this.shiftPipOperation(); + } else { + this.updateIsPip(true); + this.enterPipOperation(); + } } }, handleExitPip() { if (this.isPip) { + this.exitPipOperation(); this.updateIsPip(false); } }, diff --git a/src/renderer/main.ts b/src/renderer/main.ts index e6ce25aba4..0df709b6e4 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -890,7 +890,7 @@ new Vue({ if (this.currentRouteName === 'browsing-view') this.browsingViewTop = !this.browsingViewTop; }); this.menuService.on('window.pip', () => { - this.$bus.$emit('toggle-pip', !this.isPip); + this.$bus.$emit('toggle-pip', this.$electron.remote.BrowserWindow.getFocusedWindow().id === this.$electron.remote.getCurrentWindow().id); }); this.menuService.on('window.fullscreen', () => { if (this.isFullScreen) { From 13cdd0439f17c48873736ae70dbb2d9aaaffcd1e Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Thu, 15 Aug 2019 17:55:20 +0800 Subject: [PATCH 007/123] feat(browsingview): support tabs to change bookmark --- src/main/index.js | 88 ++++- src/renderer/components/BrowsingView.vue | 313 ++++++++---------- .../BrowsingView/BrowsingFavicons.vue | 9 +- .../BrowsingView/BrowsingHeader.vue | 37 ++- src/renderer/main.ts | 2 +- static/pip/macTitlebar.html | 10 + static/pip/winTitlebar.html | 10 + 7 files changed, 268 insertions(+), 201 deletions(-) create mode 100644 static/pip/macTitlebar.html create mode 100644 static/pip/winTitlebar.html diff --git a/src/main/index.js b/src/main/index.js index 073b7d20dc..2373ca15d5 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -9,6 +9,7 @@ import path, { } from 'path'; import fs from 'fs'; import rimraf from 'rimraf'; +import urlParse from 'url-parse-lax'; // import { audioHandler } from './helpers/audioHandler'; import { audioGrabService } from './helpers/AudioGrabService'; import './helpers/electronPrototypes'; @@ -67,15 +68,19 @@ let preferenceWindow = null; let browsingWindow = null; let pipControlView = null; let browserViews = []; +let currentBrowserHostname = ''; +let currentPipHostname = ''; let tray = null; let pipTimer = 0; -let initialUrl = ''; +let initialBrowserUrl = ''; +let lastBrowserUrl = ''; let needToRestore = false; let forceQuit = false; // 大退app 关闭所有windows let needBlockCloseLaborWindow = true; // 标记是否阻塞nsfw窗口关闭 let inited = false; let hideBrowsingWindow = false; let finalVideoToOpen = []; +const tabGroups = []; const tmpVideoToOpen = []; const tmpSubsToOpen = []; const subRegex = getValidSubtitleRegex(); @@ -447,7 +452,7 @@ function registerMainWindowEvent(mainWindow) { const views = browsingWindow.getBrowserViews(); browsingWindow.removeBrowserView(views[0]); browsingWindow.removeBrowserView(views[1]); - views[0].webContents.loadURL(initialUrl).then(() => { + views[0].webContents.loadURL(initialBrowserUrl).then(() => { views[0].webContents.clearHistory(); }); }); @@ -461,7 +466,7 @@ function registerMainWindowEvent(mainWindow) { }); } browserViews.forEach((view) => { - view.webContents.loadURL(initialUrl).then(() => { + view.webContents.loadURL('about:blank').then(() => { view.webContents.clearHistory(); }); }); @@ -470,9 +475,53 @@ function registerMainWindowEvent(mainWindow) { ipcMain.on('update-pip-state', () => { mainWindow.send('update-pip-state'); }); + ipcMain.on('shift-page-tab', (evt, url) => { + const isPip = browsingWindow.isVisible(); + currentPipHostname = isPip ? currentBrowserHostname : urlParse(url).hostname; + currentBrowserHostname = urlParse(url).hostname; + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); + lastBrowserUrl = initialBrowserUrl; + initialBrowserUrl = url; + if (currentBrowserHostname !== 'blank' && index === -1) { + mainWindow.removeBrowserView(mainWindow.getBrowserView()); + browserViews = [ + new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + }, + }), + new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + }, + }), + ]; + tabGroups.push({ [`${currentBrowserHostname}`]: browserViews }); + mainWindow.addBrowserView(browserViews[0]); + if (!isPip) { + browsingWindow.removeBrowserView(browsingWindow.getBrowserViews()[0]); + browsingWindow.addBrowserView(browserViews[1]); + } + browserViews.forEach((view) => { + view.webContents.loadURL(url); + view.webContents.openDevTools(); + }); + } else { + browserViews = tabGroups[index][currentBrowserHostname]; + mainWindow.removeBrowserView(mainWindow.getBrowserView()); + mainWindow.addBrowserView(browserViews[isPip ? 1 : 0]); + if (!isPip) { + browsingWindow.removeBrowserView(browsingWindow.getBrowserViews()[0]); + browsingWindow.addBrowserView(browserViews[1]); + } + } + mainWindow.send('current-browser-id', browserViews.map(v => v.id)); + mainWindow.send('update-browser-view', !(currentBrowserHostname !== 'blank' && index === -1)); + }); ipcMain.on('create-browser-view', (evt, args) => { - initialUrl = args.url; - if (!browserViews.length) { + currentBrowserHostname = currentPipHostname = urlParse(args.url).hostname; + if (currentBrowserHostname !== 'blank' && !Object.keys(tabGroups).includes(currentBrowserHostname)) { + initialBrowserUrl = args.url; browserViews = [ new BrowserView({ webPreferences: { @@ -485,13 +534,15 @@ function registerMainWindowEvent(mainWindow) { }, }), ]; + mainWindow.send('current-browser-id', browserViews.map(v => v.id)); + tabGroups.push({ [`${currentBrowserHostname}`]: browserViews }); + mainWindow.addBrowserView(browserViews[0]); + browsingWindow.addBrowserView(browserViews[1]); + browserViews.forEach((view) => { + view.webContents.loadURL(args.url); + view.webContents.openDevTools(); + }); } - mainWindow.addBrowserView(browserViews[0]); - browsingWindow.addBrowserView(browserViews[1]); - browserViews.forEach((view) => { - view.webContents.loadURL(args.url); - view.webContents.openDevTools(); - }); }); ipcMain.on('update-danmu-state', (evt, val) => { pipControlView.webContents.executeJavaScript(`document.querySelector(".danmu").src = ${val} ? "../../src/renderer/assets/icon/danmu-default-icon.svg" : "../../src/renderer/assets/icon/noDanmu-default-icon.svg"`); @@ -535,16 +586,19 @@ function registerMainWindowEvent(mainWindow) { } }); ipcMain.on('shift-pip', () => { + const isDifferentBrowser = currentPipHostname !== '' && currentBrowserHostname !== currentPipHostname; const mainView = mainWindow.getBrowserView(); mainWindow.removeBrowserView(mainView); - browsingWindow.getBrowserViews().forEach((view) => { - browsingWindow.removeBrowserView(view); - view.webContents.loadURL(initialUrl); - }); + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); + const browserView = browsingWindow.getBrowserViews()[0]; + browsingWindow.removeBrowserView(browserView); + browsingWindow.removeBrowserView(pipControlView); + browserView.webContents.loadURL(isDifferentBrowser ? lastBrowserUrl : initialBrowserUrl); createPipControlView(); browsingWindow.addBrowserView(mainView); browsingWindow.addBrowserView(pipControlView); - mainWindow.addBrowserView(browserViews.find(view => view.id !== mainView.id)); + mainWindow.addBrowserView(isDifferentBrowser ? tabGroups[index][currentBrowserHostname] + .find(tab => tab.id !== mainView.id) : browserView); pipControlView.webContents.openDevTools(); pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); pipControlView.setBackgroundColor('#00FFFFFF'); @@ -572,7 +626,7 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.addBrowserView(pipControlView); browsingWindow.show(); } - pipControlView.webContents.openDevTools(); + pipControlView.webContents.openDevTools({ mode: 'detach' }); pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); pipControlView.setBackgroundColor('#00FFFFFF'); browsingWindow.blur(); diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 5e09ad9c47..25cac685c5 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -12,6 +12,7 @@ :handle-url-reload="handleUrlReload" :handle-url-back="handleUrlBack" :handle-url-forward="handleUrlForward" + :handle-bookmark-open="handleBookmarkOpen" :style="{ webkitAppRegion: 'drag' }" v-show="headerToShow" /> @@ -69,7 +70,6 @@ export default { maskToShow: false, dropFiles: [], hasVideo: false, - currentUrl: '', calculateVideoNum: 'var iframe = document.querySelector("iframe");if (iframe && iframe.contentDocument) {document.getElementsByTagName("video").length + iframe.contentDocument.getElementsByTagName("video").length} else {document.getElementsByTagName("video").length}', getVideoStyle: 'getComputedStyle(document.querySelector("video") || document.querySelector("iframe").contentDocument.querySelector("video"))', pipBtnsKeepShow: false, @@ -80,6 +80,7 @@ export default { oldDisplayId: -1, backToLandingView: false, browsingWindowClose: false, + browserIds: [1, 2], }; }, computed: { @@ -127,98 +128,7 @@ export default { } }, isPip() { - // eslint-disable-next-line @typescript-eslint/no-explicit-any,complexity - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 - switch (channel) { - case 'open-url': - this.handleOpenUrl(args); - break; - case 'dragover': - case 'dragleave': - this.maskToShow = args.dragover; - break; - case 'drop': - this.maskToShow = false; - if ((args.files as string[]).length) { - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ - x: 0, y: 36, width: 0, height: 0, - }); - this.dropFiles = args.files; - } - break; - case 'left-drag': - if (this.isPip) { - if (args.windowSize) { - this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setBounds', [{ - x: args.x, - y: args.y, - width: args.windowSize[0], - height: args.windowSize[1], - }]); - } else { - this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [args.x, args.y]); - } - } - break; - case 'fullscreenchange': - if (!this.isPip) { - this.headerToShow = !args.isFullScreen; - } - break; - case 'keydown': - if (['INPUT', 'TEXTAREA'].includes(args.targetName as string)) { - this.acceleratorAvailable = false; - } - break; - default: - console.warn(`Unhandled ipc-message: ${channel}`, args); - break; - } - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('dom-ready', () => { // for webview test - window.focus(); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.focus(); - if (process.env.NODE_ENV === 'development') this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.openDevTools(); - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { - if (disposition !== 'new-window') { - this.handleOpenUrl({ url }); - } - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', (e: Event, url: string) => { - if (!url || url === 'about:blank') return; - this.startTime = new Date().getTime(); - this.loadingState = true; - const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL(); - this.currentUrl = loadUrl; - const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); - if (recordIndex !== -1) { - switch (recordIndex) { - case 0: - this.updateRecordUrl({ youtube: loadUrl }); - break; - case 1: - this.updateRecordUrl({ bilibili: loadUrl }); - break; - case 2: - this.updateRecordUrl({ iqiyi: loadUrl }); - break; - default: - break; - } - } - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-stop-loading', () => { - const loadingTime: number = new Date().getTime() - this.startTime; - if (loadingTime % 3000 === 0) { - this.loadingState = false; - } else { - setTimeout(() => { - this.loadingState = false; - }, 3000 - (loadingTime % 3000)); - } - }); + this.addListenerToBrowser(); }, pipSize() { if (this.isPip && this.pipType !== 'youtube') { @@ -290,20 +200,17 @@ export default { ); this.$store.dispatch('updateBrowsingPos', this.winPos); this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ - x: 0, y: 36, width: this.browsingSize[0], height: this.browsingSize[1] - 36, - }); - this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].setAutoResize({ width: true, height: true }); + this.initialBrowserViewRect(); }, mounted() { this.$bus.$on('toggle-reload', this.handleUrlReload); this.$bus.$on('toggle-back', this.handleUrlBack); this.$bus.$on('toggle-forward', this.handleUrlForward); - this.$bus.$on('toggle-pip', (state: boolean) => { + this.$bus.$on('toggle-pip', () => { + const focusedOnMainWindow = this.$electron.remote.getCurrentWindow().isFocused(); setTimeout(() => { if (this.acceleratorAvailable) { - if (!state) { + if (!focusedOnMainWindow) { this.updateIsPip(false); this.exitPipOperation(); } else if (this.hasVideo) { @@ -335,6 +242,7 @@ export default { }); window.addEventListener('beforeunload', (e: BeforeUnloadEvent) => { this.$electron.ipcRenderer.send('remove-browser'); + this.removeListener(); if (!this.asyncTasksDone) { e.returnValue = false; if (this.isPip) { @@ -364,6 +272,31 @@ export default { name: 'landing-view', }); }); + this.$electron.ipcRenderer.on('current-browser-id', (e: Event, ids: number[]) => { + this.browserIds = ids; + }); + this.$electron.ipcRenderer.on('update-browser-view', (e: Event, isShift: boolean) => { + this.initialBrowserViewRect(); + this.addListenerToBrowser(); + if (isShift) { + const loadUrl = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getURL(); + const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents + .executeJavaScript(this.calculateVideoNum, (r: number) => { + this.hasVideo = recordIndex === 0 && !getVideoId(loadUrl).id ? false : !!r; + this.$electron.ipcRenderer.send('update-enabled', 'window.pip', this.hasVideo); + this.$refs.browsingHeader.updateWebInfo({ + hasVideo: this.hasVideo, + url: loadUrl, + canGoBack: this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.canGoBack(), + canGoForward: this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.canGoForward(), + }); + }); + } + }); this.$electron.ipcRenderer.on('handle-exit-pip', () => { this.handleExitPip(); }); @@ -386,8 +319,105 @@ export default { this.browsingWindowClose = true; this.updateIsPip(false); }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any,complexity - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { // https://github.com/electron/typescript-definitions/issues/27 fixed in 6.0.0 + this.addListenerToBrowser(); + }, + beforeDestroy() { + this.$electron.ipcRenderer.send('remove-browser'); + asyncStorage.set('browsing', { + pipSize: this.pipSize, + pipPos: this.pipPos, + browsingSize: this.browsingSize, + browsingPos: this.browsingPos, + barrageOpen: this.barrageOpen, + }).then(() => { + if (this.isPip) { + this.$electron.ipcRenderer.send('store-pip-pos'); + } else { + this.$store.dispatch('updateBrowsingSize', this.winSize); + this.$store.dispatch('updateBrowsingPos', this.winPos); + } + this.updateIsPip(false); + }).finally(() => { + if (this.backToLandingView) { + windowRectService.uploadWindowBy(false, 'landing-view'); + } + }); + }, + methods: { + ...mapActions({ + updateRecordUrl: browsingActions.UPDATE_RECORD_URL, + updateBarrageOpen: browsingActions.UPDATE_BARRAGE_OPEN, + updateIsPip: browsingActions.UPDATE_IS_PIP, + }), + handleBookmarkOpen(url: string) { + this.removeListener(); + this.hasVideo = false; + this.$electron.ipcRenderer.send('update-enabled', 'window.pip', false); + this.$refs.browsingHeader.updateWebInfo({ + hasVideo: this.hasVideo, + }); + this.$electron.ipcRenderer.send('shift-page-tab', url); + }, + addListenerToBrowser() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('ipc-message', (evt: Event, channel: string, args: any) => { + this.ipcMessage(channel, args); + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('dom-ready', this.domReady); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { + this.newWindow(url, disposition); + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', (e: Event, url: string) => { + this.didStartNavigation(url); + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-stop-loading', this.didStopLoading); + }, + removeListener() { + const currentWebContents = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents; + currentWebContents.removeListener('did-stop-loading', this.didStopLoading); + currentWebContents.removeListener('dom-ready', this.domReady); + currentWebContents.removeListener('ipc-message', this.ipcMessage); + currentWebContents.removeListener('did-start-navigation', this.didStartNavigation); + currentWebContents.removeListener('new-window', this.newWindow); + }, + newWindow(url: string, disposition: string) { + if (disposition !== 'new-window') { + this.handleOpenUrl({ url }); + } + }, + initialBrowserViewRect() { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].setBounds({ + x: 0, y: 36, width: this.browsingSize[0], height: this.browsingSize[1] - 36, + }); + this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].setAutoResize({ width: true, height: true }); + }, + didStartNavigation(url: string) { + if (!url || url === 'about:blank') return; + this.startTime = new Date().getTime(); + this.loadingState = true; + const loadUrl = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getURL(); + const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); + if (recordIndex !== -1) { + switch (recordIndex) { + case 0: + this.updateRecordUrl({ youtube: loadUrl }); + break; + case 1: + this.updateRecordUrl({ bilibili: loadUrl }); + break; + case 2: + this.updateRecordUrl({ iqiyi: loadUrl }); + break; + default: + break; + } + } + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ipcMessage(channel: string, args: any) { switch (channel) { case 'open-url': this.handleOpenUrl(args); @@ -433,42 +463,13 @@ export default { console.warn(`Unhandled ipc-message: ${channel}`, args); break; } - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('dom-ready', () => { // for webview test + }, + domReady() { window.focus(); this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.focus(); if (process.env.NODE_ENV === 'development') this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.openDevTools(); - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { - if (disposition !== 'new-window') { - this.handleOpenUrl({ url }); - } - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', (e: Event, url: string) => { - if (!url || url === 'about:blank') return; - this.startTime = new Date().getTime(); - this.loadingState = true; - const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL(); - this.currentUrl = loadUrl; - const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); - if (recordIndex !== -1) { - switch (recordIndex) { - case 0: - this.updateRecordUrl({ youtube: loadUrl }); - break; - case 1: - this.updateRecordUrl({ bilibili: loadUrl }); - break; - case 2: - this.updateRecordUrl({ iqiyi: loadUrl }); - break; - default: - break; - } - } - }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-stop-loading', () => { + }, + didStopLoading() { const loadingTime: number = new Date().getTime() - this.startTime; if (loadingTime % 3000 === 0) { this.loadingState = false; @@ -477,45 +478,14 @@ export default { this.loadingState = false; }, 3000 - (loadingTime % 3000)); } - }); - }, - beforeDestroy() { - this.$electron.ipcRenderer.send('remove-browser'); - asyncStorage.set('browsing', { - pipSize: this.pipSize, - pipPos: this.pipPos, - browsingSize: this.browsingSize, - browsingPos: this.browsingPos, - barrageOpen: this.barrageOpen, - }).then(() => { - if (this.isPip) { - this.$electron.ipcRenderer.send('store-pip-pos'); - } else { - this.$store.dispatch('updateBrowsingSize', this.winSize); - this.$store.dispatch('updateBrowsingPos', this.winPos); - } - this.updateIsPip(false); - }).finally(() => { - if (this.backToLandingView) { - windowRectService.uploadWindowBy(false, 'landing-view'); - } - }); - }, - methods: { - ...mapActions({ - updateRecordUrl: browsingActions.UPDATE_RECORD_URL, - updateBarrageOpen: browsingActions.UPDATE_BARRAGE_OPEN, - updateIsPip: browsingActions.UPDATE_IS_PIP, - }), + }, handleOpenUrl({ url }: { url: string }) { if (!url || url === 'about:blank') return; - if (this.isPip) { - this.updateIsPip(false); - } this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.loadURL(urlParseLax(url).protocol ? url : `https:${url}`); }, pipAdapter() { - const parseUrl = urlParseLax(this.currentUrl); + const parseUrl = urlParseLax(this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getURL()); if (parseUrl.host.includes('youtube')) { this.pipType = 'youtube'; this.youtubeAdapter(); @@ -532,9 +502,8 @@ export default { }, currentPipBrowserView() { const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; - return this.$electron.remote.BrowserView.getAllViews().filter( - (view: Electron.BrowserView) => view.id !== currentBrowserView.id && view.id <= 2, - )[0]; + return this.$electron.remote.BrowserView + .fromId(this.browserIds.find((id: number) => id !== currentBrowserView.id)); }, handleWindowChangeEnterPip() { const currentBrowserView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; diff --git a/src/renderer/components/BrowsingView/BrowsingFavicons.vue b/src/renderer/components/BrowsingView/BrowsingFavicons.vue index 11df63fbff..f7ab88f7d2 100644 --- a/src/renderer/components/BrowsingView/BrowsingFavicons.vue +++ b/src/renderer/components/BrowsingView/BrowsingFavicons.vue @@ -52,6 +52,10 @@ export default { type: Object, required: true, }, + handleBookmarkOpen: { + type: Function, + required: true, + }, }, data() { return { @@ -92,10 +96,7 @@ export default { this.faviconIndex = -1; }, handleFavOpen(item: { name: string, type: string, url: string }) { - this.$electron.remote.BrowserView.getAllViews()[0].webContents - .loadURL(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); - this.$electron.remote.BrowserView.getAllViews()[1].webContents - .loadURL(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); + this.handleBookmarkOpen(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); }, handleFavAnimEnd(e: AnimationEvent) { const target = e.target as HTMLElement; diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index bd8526cf6f..fdd396387e 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -16,6 +16,7 @@ /> { + this[key] = info[key]; + }); }, }, }; diff --git a/src/renderer/main.ts b/src/renderer/main.ts index 4349ca3ae5..8efe30becf 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -907,7 +907,7 @@ new Vue({ if (this.currentRouteName === 'browsing-view') this.browsingViewTop = !this.browsingViewTop; }); this.menuService.on('window.pip', () => { - this.$bus.$emit('toggle-pip', this.$electron.remote.BrowserWindow.getFocusedWindow().id === this.$electron.remote.getCurrentWindow().id); + this.$bus.$emit('toggle-pip'); }); this.menuService.on('window.fullscreen', () => { if (this.isFullScreen) { diff --git a/static/pip/macTitlebar.html b/static/pip/macTitlebar.html new file mode 100644 index 0000000000..81d41bd8d6 --- /dev/null +++ b/static/pip/macTitlebar.html @@ -0,0 +1,10 @@ + + + + + pipTitlebar + + + + + diff --git a/static/pip/winTitlebar.html b/static/pip/winTitlebar.html new file mode 100644 index 0000000000..6b6499c3e0 --- /dev/null +++ b/static/pip/winTitlebar.html @@ -0,0 +1,10 @@ + + + + + $Title$ + + +$END$ + + From c2095dedef0c5b573c153fdc426fd7256ae2d8d5 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Fri, 16 Aug 2019 15:14:12 +0800 Subject: [PATCH 008/123] feat(browsingview): add titlebar to pip window --- src/main/index.js | 80 +++++++++++++-- src/renderer/components/BrowsingPip.vue | 14 +++ .../pip/assets}/danmu-default-icon.svg | 0 .../pip/assets}/noDanmu-default-icon.svg | 0 .../pip/assets}/pipBack-default-icon.svg | 0 .../pip/assets/titleBarClose-active-icon.svg | 8 ++ .../pip/assets/titleBarClose-default-icon.svg | 7 ++ .../pip/assets/titleBarClose-hover-icon.svg | 8 ++ .../assets/titleBarExitFull-active-icon.svg | 8 ++ .../assets/titleBarExitFull-default-icon.svg | 7 ++ .../assets/titleBarExitFull-hover-icon.svg | 8 ++ .../pip/assets/titleBarFull-active-icon.svg | 8 ++ .../pip/assets/titleBarFull-default-icon.svg | 7 ++ static/pip/assets/titleBarFull-hover-icon.svg | 8 ++ .../assets/titleBarRecover-active-icon.svg | 8 ++ .../assets/titleBarRecover-default-icon.svg | 7 ++ .../pip/assets/titleBarRecover-hover-icon.svg | 8 ++ static/pip/macTitlebar.html | 97 ++++++++++++++++++- static/pip/pipControl.html | 11 ++- static/pip/titlebarPreload.js | 23 +++++ static/pip/winTitlebar.html | 4 +- 21 files changed, 305 insertions(+), 16 deletions(-) rename {src/renderer/assets/icon => static/pip/assets}/danmu-default-icon.svg (100%) rename {src/renderer/assets/icon => static/pip/assets}/noDanmu-default-icon.svg (100%) rename {src/renderer/assets/icon => static/pip/assets}/pipBack-default-icon.svg (100%) create mode 100644 static/pip/assets/titleBarClose-active-icon.svg create mode 100644 static/pip/assets/titleBarClose-default-icon.svg create mode 100644 static/pip/assets/titleBarClose-hover-icon.svg create mode 100644 static/pip/assets/titleBarExitFull-active-icon.svg create mode 100644 static/pip/assets/titleBarExitFull-default-icon.svg create mode 100644 static/pip/assets/titleBarExitFull-hover-icon.svg create mode 100644 static/pip/assets/titleBarFull-active-icon.svg create mode 100644 static/pip/assets/titleBarFull-default-icon.svg create mode 100644 static/pip/assets/titleBarFull-hover-icon.svg create mode 100644 static/pip/assets/titleBarRecover-active-icon.svg create mode 100644 static/pip/assets/titleBarRecover-default-icon.svg create mode 100644 static/pip/assets/titleBarRecover-hover-icon.svg create mode 100644 static/pip/titlebarPreload.js diff --git a/src/main/index.js b/src/main/index.js index 2373ca15d5..af66ff96de 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -5,7 +5,7 @@ import { app, BrowserWindow, session, Tray, ipcMain, globalShortcut, nativeImage import { throttle, debounce, uniq } from 'lodash'; import os from 'os'; import path, { - basename, dirname, extname, join, + basename, dirname, extname, join, resolve, } from 'path'; import fs from 'fs'; import rimraf from 'rimraf'; @@ -67,6 +67,7 @@ let aboutWindow = null; let preferenceWindow = null; let browsingWindow = null; let pipControlView = null; +let titlebarView = null; let browserViews = []; let currentBrowserHostname = ''; let currentPipHostname = ''; @@ -84,6 +85,7 @@ const tabGroups = []; const tmpVideoToOpen = []; const tmpSubsToOpen = []; const subRegex = getValidSubtitleRegex(); +const titlebarUrl = process.platform === 'darwin' ? `file:${resolve(__static, 'pip/macTitlebar.html')}` : `file:${resolve(__static, 'pip/winTitlebar.html')}`; const mainURL = process.env.NODE_ENV === 'development' ? 'http://localhost:9080' : `file://${__dirname}/index.html`; @@ -143,6 +145,15 @@ function createPipControlView() { }); } +function createTitlebarView() { + if (titlebarView) titlebarView.destroy(); + titlebarView = new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/titlebarPreload.js')}`, + }, + }); +} + function markNeedToRestore() { fs.closeSync(fs.openSync(path.join(app.getPath('userData'), 'NEED_TO_RESTORE_MARK'), 'w')); } @@ -504,7 +515,6 @@ function registerMainWindowEvent(mainWindow) { } browserViews.forEach((view) => { view.webContents.loadURL(url); - view.webContents.openDevTools(); }); } else { browserViews = tabGroups[index][currentBrowserHostname]; @@ -540,17 +550,16 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.addBrowserView(browserViews[1]); browserViews.forEach((view) => { view.webContents.loadURL(args.url); - view.webContents.openDevTools(); }); } }); ipcMain.on('update-danmu-state', (evt, val) => { - pipControlView.webContents.executeJavaScript(`document.querySelector(".danmu").src = ${val} ? "../../src/renderer/assets/icon/danmu-default-icon.svg" : "../../src/renderer/assets/icon/noDanmu-default-icon.svg"`); + pipControlView.webContents.executeJavaScript(`document.querySelector(".danmu").src = ${val} ? "assets/danmu-default-icon.svg" : "assets/noDanmu-default-icon.svg"`); }); ipcMain.on('init-danmu-state', (evt, args) => { pipControlView.webContents.executeJavaScript( `const danmu = document.querySelector(".danmu"); - danmu.src = ${args.barrageOpen} ? "../../src/renderer/assets/icon/danmu-default-icon.svg" : "../../src/renderer/assets/icon/noDanmu-default-icon.svg"; + danmu.src = ${args.barrageOpen} ? "assets/danmu-default-icon.svg" : "assets/noDanmu-default-icon.svg"; danmu.style.opacity = ${args.opacity}; danmu.style.cursor = ${args.opacity} === 1 ? "cursor" : "default"`, ); @@ -585,6 +594,44 @@ function registerMainWindowEvent(mainWindow) { pipControlView.webContents.executeJavaScript('document.querySelector(".pip-buttons").style.display = "none";'); } }); + ipcMain.on('maximizable', (evt, val) => { + console.log(val); + if (val) { + titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarMax").style.display = mouseenter ? "block" : "none";' + + 'document.querySelector(".titlebarFull").style.display = mouseenter ? "none" : "block";'); + } else { + titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarMax").style.display = "none";' + + 'document.querySelector(".titlebarFull").style.display = "block";'); + } + }); + ipcMain.on('mouseup', (evt, type) => { + switch (type) { + case 'close': + browsingWindow.close(); + break; + case 'min': + browsingWindow.minimize(); + break; + case 'full': + browsingWindow.setFullScreen(true); + titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarFull").src = "assets/titleBarRecover-default-icon.svg";' + + 'document.querySelector(".titlebarMin").style.pointerEvents = "none";' + + 'document.querySelector(".titlebarMin").style.opacity = "0.25";' + + 'document.querySelector(".titlebarFull").style.display = "none";' + + 'document.querySelector(".titlebarRecover").style.display = "block";'); + break; + case 'recover': + browsingWindow.setFullScreen(false); + titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarFull").src = "assets/titleBarFull-default-icon.svg";' + + 'document.querySelector(".titlebarMin").style.pointerEvents = "";' + + 'document.querySelector(".titlebarMin").style.opacity = "1";' + + 'document.querySelector(".titlebarFull").style.display = "";' + + 'document.querySelector(".titlebarRecover").style.display = "none";'); + break; + default: + break; + } + }); ipcMain.on('shift-pip', () => { const isDifferentBrowser = currentPipHostname !== '' && currentBrowserHostname !== currentPipHostname; const mainView = mainWindow.getBrowserView(); @@ -593,21 +640,30 @@ function registerMainWindowEvent(mainWindow) { const browserView = browsingWindow.getBrowserViews()[0]; browsingWindow.removeBrowserView(browserView); browsingWindow.removeBrowserView(pipControlView); + browsingWindow.removeBrowserView(titlebarView); browserView.webContents.loadURL(isDifferentBrowser ? lastBrowserUrl : initialBrowserUrl); createPipControlView(); + createTitlebarView(); browsingWindow.addBrowserView(mainView); browsingWindow.addBrowserView(pipControlView); + browsingWindow.addBrowserView(titlebarView); mainWindow.addBrowserView(isDifferentBrowser ? tabGroups[index][currentBrowserHostname] .find(tab => tab.id !== mainView.id) : browserView); - pipControlView.webContents.openDevTools(); pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); pipControlView.setBackgroundColor('#00FFFFFF'); + titlebarView.webContents.loadURL(titlebarUrl); + titlebarView.setBackgroundColor('#00FFFFFF'); + titlebarView.setBounds({ + x: 0, y: 0, width: browsingWindow.getSize()[0], height: 36, + }); browsingWindow.blur(); browsingWindow.focus(); }); ipcMain.on('enter-pip', () => { const mainView = mainWindow.getBrowserView(); createPipControlView(); + createTitlebarView(); + titlebarView.webContents.openDevTools({ mode: 'detach' }); if (!browsingWindow) { createBrowsingWindow(); browsingWindow.openDevTools(); @@ -616,6 +672,7 @@ function registerMainWindowEvent(mainWindow) { mainWindow.addBrowserView(browView); browsingWindow.addBrowserView(mainView); browsingWindow.addBrowserView(pipControlView); + browsingWindow.addBrowserView(titlebarView); browsingWindow.show(); } else { const browView = browsingWindow.getBrowserView(); @@ -624,17 +681,25 @@ function registerMainWindowEvent(mainWindow) { mainWindow.addBrowserView(browView); browsingWindow.addBrowserView(mainView); browsingWindow.addBrowserView(pipControlView); + browsingWindow.addBrowserView(titlebarView); browsingWindow.show(); } - pipControlView.webContents.openDevTools({ mode: 'detach' }); pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); pipControlView.setBackgroundColor('#00FFFFFF'); + titlebarView.webContents.loadURL(titlebarUrl); + titlebarView.setBackgroundColor('#00FFFFFF'); + titlebarView.setBounds({ + x: 0, y: 0, width: browsingWindow.getSize()[0], height: 36, + }); browsingWindow.blur(); browsingWindow.focus(); }); ipcMain.on('set-control-bounds', (evt, args) => { if (pipControlView) pipControlView.setBounds(args); }); + ipcMain.on('set-titlebar-bounds', (evt, args) => { + if (titlebarView) titlebarView.setBounds(args); + }); ipcMain.on('exit-pip', () => { const mainView = mainWindow.getBrowserView(); const browViews = browsingWindow.getBrowserViews(); @@ -642,6 +707,7 @@ function registerMainWindowEvent(mainWindow) { if (browsingWindow) { browsingWindow.removeBrowserView(browViews[0]); browsingWindow.removeBrowserView(pipControlView); + browsingWindow.removeBrowserView(titlebarView); } if (mainView.webContents.canGoBack()) { mainView.webContents.goBack(); diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index 4966ea7737..b4200f3cb4 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -14,6 +14,14 @@ export default { }; }, mounted() { + window.addEventListener('keydown', (e) => { + if (e.keyCode === 18) { + electron.ipcRenderer.send('maximizable', true); + } + }); + window.addEventListener('keyup', () => { + electron.ipcRenderer.send('maximizable', false); + }); window.addEventListener('focus', () => { electron.ipcRenderer.send('update-focused-window', false); electron.ipcRenderer.send('update-enabled', 'window.pip', true); @@ -22,6 +30,12 @@ export default { window.addEventListener('resize', throttle(() => { const size = electron.remote.getCurrentWindow().getSize(); electron.ipcRenderer.send('pip-window-size', size); + electron.ipcRenderer.send('set-titlebar-bounds', { + x: 0, + y: 0, + width: size[0], + height: 36, + }); electron.ipcRenderer.send('set-control-bounds', { x: Math.round(size[0] - 65), y: Math.round(size[1] / 2 - 54), diff --git a/src/renderer/assets/icon/danmu-default-icon.svg b/static/pip/assets/danmu-default-icon.svg similarity index 100% rename from src/renderer/assets/icon/danmu-default-icon.svg rename to static/pip/assets/danmu-default-icon.svg diff --git a/src/renderer/assets/icon/noDanmu-default-icon.svg b/static/pip/assets/noDanmu-default-icon.svg similarity index 100% rename from src/renderer/assets/icon/noDanmu-default-icon.svg rename to static/pip/assets/noDanmu-default-icon.svg diff --git a/src/renderer/assets/icon/pipBack-default-icon.svg b/static/pip/assets/pipBack-default-icon.svg similarity index 100% rename from src/renderer/assets/icon/pipBack-default-icon.svg rename to static/pip/assets/pipBack-default-icon.svg diff --git a/static/pip/assets/titleBarClose-active-icon.svg b/static/pip/assets/titleBarClose-active-icon.svg new file mode 100644 index 0000000000..5bef90a069 --- /dev/null +++ b/static/pip/assets/titleBarClose-active-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarClose-default-icon.svg b/static/pip/assets/titleBarClose-default-icon.svg new file mode 100644 index 0000000000..a96b9f2f11 --- /dev/null +++ b/static/pip/assets/titleBarClose-default-icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarClose-hover-icon.svg b/static/pip/assets/titleBarClose-hover-icon.svg new file mode 100644 index 0000000000..418e969514 --- /dev/null +++ b/static/pip/assets/titleBarClose-hover-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarExitFull-active-icon.svg b/static/pip/assets/titleBarExitFull-active-icon.svg new file mode 100644 index 0000000000..808882bf9c --- /dev/null +++ b/static/pip/assets/titleBarExitFull-active-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarExitFull-default-icon.svg b/static/pip/assets/titleBarExitFull-default-icon.svg new file mode 100644 index 0000000000..a96b9f2f11 --- /dev/null +++ b/static/pip/assets/titleBarExitFull-default-icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarExitFull-hover-icon.svg b/static/pip/assets/titleBarExitFull-hover-icon.svg new file mode 100644 index 0000000000..9887a28ab7 --- /dev/null +++ b/static/pip/assets/titleBarExitFull-hover-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarFull-active-icon.svg b/static/pip/assets/titleBarFull-active-icon.svg new file mode 100644 index 0000000000..2803013ab8 --- /dev/null +++ b/static/pip/assets/titleBarFull-active-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarFull-default-icon.svg b/static/pip/assets/titleBarFull-default-icon.svg new file mode 100644 index 0000000000..a96b9f2f11 --- /dev/null +++ b/static/pip/assets/titleBarFull-default-icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarFull-hover-icon.svg b/static/pip/assets/titleBarFull-hover-icon.svg new file mode 100644 index 0000000000..8995b2abeb --- /dev/null +++ b/static/pip/assets/titleBarFull-hover-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarRecover-active-icon.svg b/static/pip/assets/titleBarRecover-active-icon.svg new file mode 100644 index 0000000000..f77287cf11 --- /dev/null +++ b/static/pip/assets/titleBarRecover-active-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarRecover-default-icon.svg b/static/pip/assets/titleBarRecover-default-icon.svg new file mode 100644 index 0000000000..a96b9f2f11 --- /dev/null +++ b/static/pip/assets/titleBarRecover-default-icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/static/pip/assets/titleBarRecover-hover-icon.svg b/static/pip/assets/titleBarRecover-hover-icon.svg new file mode 100644 index 0000000000..1e5d492ab6 --- /dev/null +++ b/static/pip/assets/titleBarRecover-hover-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/static/pip/macTitlebar.html b/static/pip/macTitlebar.html index 81d41bd8d6..204b490b6f 100644 --- a/static/pip/macTitlebar.html +++ b/static/pip/macTitlebar.html @@ -2,9 +2,102 @@ - pipTitlebar + macTitlebar + - +
+
+ + + + + +
+
+ + diff --git a/static/pip/pipControl.html b/static/pip/pipControl.html index b9e711cf91..7889236101 100644 --- a/static/pip/pipControl.html +++ b/static/pip/pipControl.html @@ -2,12 +2,13 @@ - Title + pipControl -
- - +
+ +
diff --git a/static/pip/titlebarPreload.js b/static/pip/titlebarPreload.js new file mode 100644 index 0000000000..bc6f4c6272 --- /dev/null +++ b/static/pip/titlebarPreload.js @@ -0,0 +1,23 @@ +// eslint-disable-next-line no-console +console.log('titlebar-preloaded~~~~~~~'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { ipcRenderer, remote } = require('electron'); + +document.addEventListener('DOMContentLoaded', () => { + const close = document.querySelector('.titlebarClose'); + const min = document.querySelector('.titlebarMin'); + const full = document.querySelector('.titlebarFull'); + const recover = document.querySelector('.titlebarRecover'); + if (close) { + close.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'close')); + } + if (min) { + min.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'min')); + } + if (full) { + full.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'full')); + } + if (recover) { + recover.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'recover')); + } +}); diff --git a/static/pip/winTitlebar.html b/static/pip/winTitlebar.html index 6b6499c3e0..04e75bc585 100644 --- a/static/pip/winTitlebar.html +++ b/static/pip/winTitlebar.html @@ -2,9 +2,9 @@ - $Title$ + Title -$END$ + From 5f7ace51794cd424dc24a4b5a449011487d25a26 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Fri, 16 Aug 2019 15:28:33 +0800 Subject: [PATCH 009/123] fix(browsingview): add browserView before remove when change tab --- src/main/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/index.js b/src/main/index.js index af66ff96de..2ee51f433b 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -518,8 +518,9 @@ function registerMainWindowEvent(mainWindow) { }); } else { browserViews = tabGroups[index][currentBrowserHostname]; - mainWindow.removeBrowserView(mainWindow.getBrowserView()); + const id = mainWindow.getBrowserView().id; mainWindow.addBrowserView(browserViews[isPip ? 1 : 0]); + mainWindow.removeBrowserView(mainWindow.getBrowserViews().find(view => view.id === id)); if (!isPip) { browsingWindow.removeBrowserView(browsingWindow.getBrowserViews()[0]); browsingWindow.addBrowserView(browserViews[1]); From 0f5b42be7dc41c2fc948acf6edaaf9dc92828409 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Mon, 19 Aug 2019 12:11:28 +0800 Subject: [PATCH 010/123] feat(browsingview): add functions to titlebar --- src/main/index.js | 107 +++++++++++------- src/renderer/components/BrowsingPip.vue | 8 -- src/renderer/components/BrowsingView.vue | 3 +- .../BrowsingView/BrowsingFavicons.vue | 2 +- static/pip/macTitlebar.html | 31 ----- static/pip/titlebarPreload.js | 53 +++++++++ 6 files changed, 121 insertions(+), 83 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index 2ee51f433b..3bf12dc269 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -461,11 +461,14 @@ function registerMainWindowEvent(mainWindow) { }); ipcMain.on('pip-window-close', () => { const views = browsingWindow.getBrowserViews(); - browsingWindow.removeBrowserView(views[0]); - browsingWindow.removeBrowserView(views[1]); + views.forEach((view) => { + browsingWindow.removeBrowserView(view); + }); views[0].webContents.loadURL(initialBrowserUrl).then(() => { views[0].webContents.clearHistory(); }); + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentPipHostname); + tabGroups[index][currentPipHostname].reverse(); }); ipcMain.on('remove-browser', () => { const mainView = mainWindow.getBrowserView(); @@ -487,8 +490,9 @@ function registerMainWindowEvent(mainWindow) { mainWindow.send('update-pip-state'); }); ipcMain.on('shift-page-tab', (evt, url) => { - const isPip = browsingWindow.isVisible(); - currentPipHostname = isPip ? currentBrowserHostname : urlParse(url).hostname; + if (!browsingWindow) createBrowsingWindow(); + const isPip = browsingWindow && browsingWindow.isVisible(); + if (!isPip) currentPipHostname = urlParse(url).hostname; currentBrowserHostname = urlParse(url).hostname; const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); lastBrowserUrl = initialBrowserUrl; @@ -519,14 +523,21 @@ function registerMainWindowEvent(mainWindow) { } else { browserViews = tabGroups[index][currentBrowserHostname]; const id = mainWindow.getBrowserView().id; - mainWindow.addBrowserView(browserViews[isPip ? 1 : 0]); - mainWindow.removeBrowserView(mainWindow.getBrowserViews().find(view => view.id === id)); - if (!isPip) { - browsingWindow.removeBrowserView(browsingWindow.getBrowserViews()[0]); + if (isPip) { + mainWindow.addBrowserView( + browserViews[currentPipHostname === currentBrowserHostname ? 1 : 0], + ); + mainWindow.removeBrowserView(mainWindow.getBrowserViews().find(view => view.id === id)); + } else { + mainWindow.addBrowserView(browserViews[0]); + mainWindow.removeBrowserView(mainWindow.getBrowserViews().find(view => view.id === id)); + browsingWindow.removeBrowserView(browsingWindow.getBrowserView()); browsingWindow.addBrowserView(browserViews[1]); } } - mainWindow.send('current-browser-id', browserViews.map(v => v.id)); + const mainId = mainWindow.getBrowserViews()[0].id; + const browserId = browsingWindow.getBrowserViews()[0].id; + mainWindow.send('current-browser-id', [mainId, browserId]); mainWindow.send('update-browser-view', !(currentBrowserHostname !== 'blank' && index === -1)); }); ipcMain.on('create-browser-view', (evt, args) => { @@ -596,10 +607,9 @@ function registerMainWindowEvent(mainWindow) { } }); ipcMain.on('maximizable', (evt, val) => { - console.log(val); if (val) { - titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarMax").style.display = mouseenter ? "block" : "none";' - + 'document.querySelector(".titlebarFull").style.display = mouseenter ? "none" : "block";'); + titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarMax").style.display = "block";' + + 'document.querySelector(".titlebarFull").style.display = "none"'); } else { titlebarView.webContents.executeJavaScript('document.querySelector(".titlebarMax").style.display = "none";' + 'document.querySelector(".titlebarFull").style.display = "block";'); @@ -629,6 +639,13 @@ function registerMainWindowEvent(mainWindow) { + 'document.querySelector(".titlebarFull").style.display = "";' + 'document.querySelector(".titlebarRecover").style.display = "none";'); break; + case 'max': + if (browsingWindow.isMaximized()) { + browsingWindow.unmaximize(); + } else { + browsingWindow.maximize(); + } + break; default: break; } @@ -637,19 +654,30 @@ function registerMainWindowEvent(mainWindow) { const isDifferentBrowser = currentPipHostname !== '' && currentBrowserHostname !== currentPipHostname; const mainView = mainWindow.getBrowserView(); mainWindow.removeBrowserView(mainView); - const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); - const browserView = browsingWindow.getBrowserViews()[0]; - browsingWindow.removeBrowserView(browserView); - browsingWindow.removeBrowserView(pipControlView); - browsingWindow.removeBrowserView(titlebarView); - browserView.webContents.loadURL(isDifferentBrowser ? lastBrowserUrl : initialBrowserUrl); + const browViews = browsingWindow.getBrowserViews(); + browViews.forEach((view) => { + browsingWindow.removeBrowserView(view); + }); createPipControlView(); createTitlebarView(); - browsingWindow.addBrowserView(mainView); - browsingWindow.addBrowserView(pipControlView); - browsingWindow.addBrowserView(titlebarView); - mainWindow.addBrowserView(isDifferentBrowser ? tabGroups[index][currentBrowserHostname] - .find(tab => tab.id !== mainView.id) : browserView); + if (isDifferentBrowser) { + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); + mainWindow.addBrowserView(tabGroups[index][currentBrowserHostname][1]); + browsingWindow.addBrowserView(mainView); + browsingWindow.addBrowserView(pipControlView); + browsingWindow.addBrowserView(titlebarView); + browViews[0].webContents.loadURL(lastBrowserUrl); + } else { + mainWindow.addBrowserView(browViews[0]); + browsingWindow.addBrowserView(mainView); + browViews[0].webContents.loadURL(initialBrowserUrl); + browsingWindow.addBrowserView(pipControlView); + browsingWindow.addBrowserView(titlebarView); + } + currentPipHostname = urlParse(browsingWindow.getBrowserViews()[0] + .webContents.getURL()).hostname; + currentBrowserHostname = urlParse(mainWindow.getBrowserView() + .webContents.getURL()).hostname; pipControlView.webContents.loadURL(`file:${require('path').resolve(__static, 'pip/pipControl.html')}`); pipControlView.setBackgroundColor('#00FFFFFF'); titlebarView.webContents.loadURL(titlebarUrl); @@ -661,26 +689,25 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.focus(); }); ipcMain.on('enter-pip', () => { - const mainView = mainWindow.getBrowserView(); createPipControlView(); createTitlebarView(); titlebarView.webContents.openDevTools({ mode: 'detach' }); if (!browsingWindow) { createBrowsingWindow(); + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); + browserViews = tabGroups[index][currentBrowserHostname]; browsingWindow.openDevTools(); - const browView = browserViews.find(view => view.id !== mainView.id); - mainWindow.removeBrowserView(mainView); - mainWindow.addBrowserView(browView); - browsingWindow.addBrowserView(mainView); + mainWindow.removeBrowserView(browserViews[0]); + mainWindow.addBrowserView(browserViews[1]); + browsingWindow.addBrowserView(browserViews[0]); browsingWindow.addBrowserView(pipControlView); browsingWindow.addBrowserView(titlebarView); browsingWindow.show(); } else { - const browView = browsingWindow.getBrowserView(); - mainWindow.removeBrowserView(mainView); - browsingWindow.removeBrowserView(browView); - mainWindow.addBrowserView(browView); - browsingWindow.addBrowserView(mainView); + mainWindow.removeBrowserView(browserViews[0]); + browsingWindow.removeBrowserView(browserViews[1]); + mainWindow.addBrowserView(browserViews[1]); + browsingWindow.addBrowserView(browserViews[0]); browsingWindow.addBrowserView(pipControlView); browsingWindow.addBrowserView(titlebarView); browsingWindow.show(); @@ -702,19 +729,17 @@ function registerMainWindowEvent(mainWindow) { if (titlebarView) titlebarView.setBounds(args); }); ipcMain.on('exit-pip', () => { - const mainView = mainWindow.getBrowserView(); - const browViews = browsingWindow.getBrowserViews(); - mainWindow.removeBrowserView(mainView); + mainWindow.removeBrowserView(browserViews[1]); if (browsingWindow) { - browsingWindow.removeBrowserView(browViews[0]); + browsingWindow.removeBrowserView(browserViews[0]); browsingWindow.removeBrowserView(pipControlView); browsingWindow.removeBrowserView(titlebarView); } - if (mainView.webContents.canGoBack()) { - mainView.webContents.goBack(); + if (browserViews[1].webContents.canGoBack()) { + browserViews[1].webContents.goBack(); } - mainWindow.addBrowserView(browViews[0]); - if (browsingWindow) browsingWindow.addBrowserView(mainView); + mainWindow.addBrowserView(browserViews[0]); + if (browsingWindow) browsingWindow.addBrowserView(browserViews[1]); if (browsingWindow) { if (browsingWindow.isFullScreen()) { hideBrowsingWindow = true; diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index b4200f3cb4..5f83e6ae74 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -14,14 +14,6 @@ export default { }; }, mounted() { - window.addEventListener('keydown', (e) => { - if (e.keyCode === 18) { - electron.ipcRenderer.send('maximizable', true); - } - }); - window.addEventListener('keyup', () => { - electron.ipcRenderer.send('maximizable', false); - }); window.addEventListener('focus', () => { electron.ipcRenderer.send('update-focused-window', false); electron.ipcRenderer.send('update-enabled', 'window.pip', true); diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 25cac685c5..95f6b7874a 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -484,8 +484,7 @@ export default { this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.loadURL(urlParseLax(url).protocol ? url : `https:${url}`); }, pipAdapter() { - const parseUrl = urlParseLax(this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL()); + const parseUrl = urlParseLax(this.currentPipBrowserView().webContents.getURL()); if (parseUrl.host.includes('youtube')) { this.pipType = 'youtube'; this.youtubeAdapter(); diff --git a/src/renderer/components/BrowsingView/BrowsingFavicons.vue b/src/renderer/components/BrowsingView/BrowsingFavicons.vue index f7ab88f7d2..7a4ce4ce2a 100644 --- a/src/renderer/components/BrowsingView/BrowsingFavicons.vue +++ b/src/renderer/components/BrowsingView/BrowsingFavicons.vue @@ -96,7 +96,7 @@ export default { this.faviconIndex = -1; }, handleFavOpen(item: { name: string, type: string, url: string }) { - this.handleBookmarkOpen(this.recordUrl[item.type] ? this.recordUrl[item.type] : item.url); + this.handleBookmarkOpen(item.url); }, handleFavAnimEnd(e: AnimationEvent) { const target = e.target as HTMLElement; diff --git a/static/pip/macTitlebar.html b/static/pip/macTitlebar.html index 204b490b6f..7710521b3a 100644 --- a/static/pip/macTitlebar.html +++ b/static/pip/macTitlebar.html @@ -67,37 +67,6 @@
- diff --git a/static/pip/titlebarPreload.js b/static/pip/titlebarPreload.js index bc6f4c6272..838913695a 100644 --- a/static/pip/titlebarPreload.js +++ b/static/pip/titlebarPreload.js @@ -4,10 +4,60 @@ console.log('titlebar-preloaded~~~~~~~'); const { ipcRenderer, remote } = require('electron'); document.addEventListener('DOMContentLoaded', () => { + const titlebar = document.querySelector('.titlebar'); + const content = document.querySelector('.content'); const close = document.querySelector('.titlebarClose'); const min = document.querySelector('.titlebarMin'); const full = document.querySelector('.titlebarFull'); const recover = document.querySelector('.titlebarRecover'); + const max = document.querySelector('.titlebarMax'); + let mouseenter = false; + if (titlebar) { + titlebar.addEventListener('dblclick', () => ipcRenderer.send('mouseup', 'max')); + } + if (content) { + document.addEventListener('keydown', (e) => { + const fullScreen = recover.style.display === 'block'; + if (e.keyCode === 18 && mouseenter && !fullScreen) { + ipcRenderer.send('maximizable', true); + } + }); + document.addEventListener('keyup', () => { + const fullScreen = recover.style.display === 'block'; + if (!fullScreen) { + ipcRenderer.send('maximizable', false); + } + }); + content.addEventListener('mouseenter', (e) => { + mouseenter = true; + remote.getCurrentWindow().getBrowserViews()[2].webContents.focus(); + const fullScreen = recover.style.display === 'block'; + close.src = 'assets/titleBarClose-hover-icon.svg'; + if (!fullScreen) { + if (e.altKey) { + full.style.display = 'none'; + max.style.display = 'block'; + } + min.src = 'assets/titleBarExitFull-hover-icon.svg'; + full.src = 'assets/titleBarFull-hover-icon.svg'; + } else { + recover.src = 'assets/titlebarRecover-hover-icon.svg'; + } + }); + content.addEventListener('mouseleave', () => { + mouseenter = false; + const fullScreen = recover.style.display === 'block'; + close.src = 'assets/titleBarClose-default-icon.svg'; + min.src = 'assets/titleBarExitFull-default-icon.svg'; + if (!fullScreen) { + full.src = 'assets/titleBarFull-default-icon.svg'; + full.style.display = 'block'; + max.style.display = 'none'; + } else { + recover.src = 'assets/titlebarRecover-default-icon.svg'; + } + }); + } if (close) { close.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'close')); } @@ -20,4 +70,7 @@ document.addEventListener('DOMContentLoaded', () => { if (recover) { recover.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'recover')); } + if (max) { + max.addEventListener('mouseup', () => ipcRenderer.send('mouseup', 'max')); + } }); From b8b84128455c946624ccab76cd2c059637851b1d Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Mon, 19 Aug 2019 18:41:45 +0800 Subject: [PATCH 011/123] feat(browsingview): support basic cache --- src/main/index.js | 88 ++++++++++++++---------- src/renderer/components/BrowsingPip.vue | 5 +- src/renderer/components/BrowsingView.vue | 16 ++--- src/shared/pip/youtube.js | 6 ++ static/pip/macTitlebar.html | 12 ++-- static/pip/winTitlebar.html | 16 ++++- 6 files changed, 91 insertions(+), 52 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index 3bf12dc269..bb554c8c53 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -461,30 +461,31 @@ function registerMainWindowEvent(mainWindow) { }); ipcMain.on('pip-window-close', () => { const views = browsingWindow.getBrowserViews(); + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentPipHostname); + const url = tabGroups[index][currentPipHostname] + .find(view => view.id !== views[0].id).webContents.getURL(); + views[0].webContents.loadURL(url); views.forEach((view) => { browsingWindow.removeBrowserView(view); }); - views[0].webContents.loadURL(initialBrowserUrl).then(() => { - views[0].webContents.clearHistory(); - }); - const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentPipHostname); tabGroups[index][currentPipHostname].reverse(); }); ipcMain.on('remove-browser', () => { const mainView = mainWindow.getBrowserView(); - mainWindow.removeBrowserView(mainView); - if (browsingWindow) { - const views = browsingWindow.getBrowserViews(); - views.forEach((view) => { - browsingWindow.removeBrowserView(view); + if (mainView) { + lastBrowserUrl = mainView.webContents.getURL(); + mainWindow.removeBrowserView(mainView); + if (browsingWindow) { + const views = browsingWindow.getBrowserViews(); + views.forEach((view) => { + browsingWindow.removeBrowserView(view); + }); + } + browserViews.forEach((view) => { + view.destroy(); }); + if (browsingWindow) browsingWindow.hide(); } - browserViews.forEach((view) => { - view.webContents.loadURL('about:blank').then(() => { - view.webContents.clearHistory(); - }); - }); - if (browsingWindow) browsingWindow.hide(); }); ipcMain.on('update-pip-state', () => { mainWindow.send('update-pip-state'); @@ -495,7 +496,6 @@ function registerMainWindowEvent(mainWindow) { if (!isPip) currentPipHostname = urlParse(url).hostname; currentBrowserHostname = urlParse(url).hostname; const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); - lastBrowserUrl = initialBrowserUrl; initialBrowserUrl = url; if (currentBrowserHostname !== 'blank' && index === -1) { mainWindow.removeBrowserView(mainWindow.getBrowserView()); @@ -540,30 +540,41 @@ function registerMainWindowEvent(mainWindow) { mainWindow.send('current-browser-id', [mainId, browserId]); mainWindow.send('update-browser-view', !(currentBrowserHostname !== 'blank' && index === -1)); }); + ipcMain.on('keep-browsers-cache', (evt, url) => { + if (browsingWindow) { + browsingWindow.getBrowserViews()[0].webContents.loadURL(url); + } + }); ipcMain.on('create-browser-view', (evt, args) => { currentBrowserHostname = currentPipHostname = urlParse(args.url).hostname; - if (currentBrowserHostname !== 'blank' && !Object.keys(tabGroups).includes(currentBrowserHostname)) { - initialBrowserUrl = args.url; - browserViews = [ - new BrowserView({ - webPreferences: { - preload: `${require('path').resolve(__static, 'pip/preload.js')}`, - }, - }), - new BrowserView({ - webPreferences: { - preload: `${require('path').resolve(__static, 'pip/preload.js')}`, - }, - }), - ]; - mainWindow.send('current-browser-id', browserViews.map(v => v.id)); + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); + initialBrowserUrl = args.url; + browserViews = [ + new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + }, + }), + new BrowserView({ + webPreferences: { + preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + }, + }), + ]; + if (index === -1) { tabGroups.push({ [`${currentBrowserHostname}`]: browserViews }); - mainWindow.addBrowserView(browserViews[0]); - browsingWindow.addBrowserView(browserViews[1]); browserViews.forEach((view) => { view.webContents.loadURL(args.url); }); + } else if (tabGroups[index][currentBrowserHostname][0].isDestroyed()) { + tabGroups[index][currentBrowserHostname].splice(0, 2, ...browserViews); + browserViews.forEach((view) => { + view.webContents.loadURL(lastBrowserUrl); + }); } + mainWindow.send('current-browser-id', browserViews.map(v => v.id)); + mainWindow.addBrowserView(browserViews[0]); + browsingWindow.addBrowserView(browserViews[1]); }); ipcMain.on('update-danmu-state', (evt, val) => { pipControlView.webContents.executeJavaScript(`document.querySelector(".danmu").src = ${val} ? "assets/danmu-default-icon.svg" : "assets/noDanmu-default-icon.svg"`); @@ -666,11 +677,15 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.addBrowserView(mainView); browsingWindow.addBrowserView(pipControlView); browsingWindow.addBrowserView(titlebarView); - browViews[0].webContents.loadURL(lastBrowserUrl); + const pipIndex = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentPipHostname); + const url = tabGroups[pipIndex][currentPipHostname] + .find(view => view.id !== browViews[0].id).webContents.getURL(); + browViews[0].webContents.loadURL(url); } else { mainWindow.addBrowserView(browViews[0]); browsingWindow.addBrowserView(mainView); - browViews[0].webContents.loadURL(initialBrowserUrl); + browViews[0].webContents + .loadURL(mainView.webContents.history[mainView.webContents.history.length - 2]); browsingWindow.addBrowserView(pipControlView); browsingWindow.addBrowserView(titlebarView); } @@ -692,6 +707,9 @@ function registerMainWindowEvent(mainWindow) { createPipControlView(); createTitlebarView(); titlebarView.webContents.openDevTools({ mode: 'detach' }); + if (browsingWindow.getBrowserViews()[0].webContents.canGoBack()) { + browsingWindow.getBrowserViews()[0].webContents.goBack(); + } if (!browsingWindow) { createBrowsingWindow(); const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index 5f83e6ae74..ce7312aa54 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -14,6 +14,9 @@ export default { }; }, mounted() { + electron.remote.getCurrentWindow().addListener('enter-html-full-screen', () => { + electron.ipcRenderer.send('mouseup', 'full'); + }); window.addEventListener('focus', () => { electron.ipcRenderer.send('update-focused-window', false); electron.ipcRenderer.send('update-enabled', 'window.pip', true); @@ -47,7 +50,7 @@ export default { +
+
+
+
From 78a67af069ec43f39810f6835749cae65fca43fd Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Mon, 19 Aug 2019 19:23:42 +0800 Subject: [PATCH 012/123] fix(browsingview): fix exit pip --- src/main/index.js | 47 ++++++++++++++++++++----------------- static/pip/winTitlebar.html | 9 +++++++ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index bb554c8c53..df4d42c486 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -73,7 +73,6 @@ let currentBrowserHostname = ''; let currentPipHostname = ''; let tray = null; let pipTimer = 0; -let initialBrowserUrl = ''; let lastBrowserUrl = ''; let needToRestore = false; let forceQuit = false; // 大退app 关闭所有windows @@ -496,7 +495,6 @@ function registerMainWindowEvent(mainWindow) { if (!isPip) currentPipHostname = urlParse(url).hostname; currentBrowserHostname = urlParse(url).hostname; const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); - initialBrowserUrl = url; if (currentBrowserHostname !== 'blank' && index === -1) { mainWindow.removeBrowserView(mainWindow.getBrowserView()); browserViews = [ @@ -548,7 +546,6 @@ function registerMainWindowEvent(mainWindow) { ipcMain.on('create-browser-view', (evt, args) => { currentBrowserHostname = currentPipHostname = urlParse(args.url).hostname; const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); - initialBrowserUrl = args.url; browserViews = [ new BrowserView({ webPreferences: { @@ -714,7 +711,6 @@ function registerMainWindowEvent(mainWindow) { createBrowsingWindow(); const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentBrowserHostname); browserViews = tabGroups[index][currentBrowserHostname]; - browsingWindow.openDevTools(); mainWindow.removeBrowserView(browserViews[0]); mainWindow.addBrowserView(browserViews[1]); browsingWindow.addBrowserView(browserViews[0]); @@ -722,8 +718,10 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.addBrowserView(titlebarView); browsingWindow.show(); } else { + browsingWindow.getBrowserViews().forEach((view) => { + browsingWindow.removeBrowserView(view); + }); mainWindow.removeBrowserView(browserViews[0]); - browsingWindow.removeBrowserView(browserViews[1]); mainWindow.addBrowserView(browserViews[1]); browsingWindow.addBrowserView(browserViews[0]); browsingWindow.addBrowserView(pipControlView); @@ -747,24 +745,29 @@ function registerMainWindowEvent(mainWindow) { if (titlebarView) titlebarView.setBounds(args); }); ipcMain.on('exit-pip', () => { - mainWindow.removeBrowserView(browserViews[1]); - if (browsingWindow) { - browsingWindow.removeBrowserView(browserViews[0]); - browsingWindow.removeBrowserView(pipControlView); - browsingWindow.removeBrowserView(titlebarView); - } - if (browserViews[1].webContents.canGoBack()) { - browserViews[1].webContents.goBack(); + const isDifferentBrowser = currentPipHostname !== '' && currentBrowserHostname !== currentPipHostname; + const index = tabGroups.findIndex(tab => Object.keys(tab)[0] === currentPipHostname); + const mainView = mainWindow.getBrowserView(); + mainWindow.removeBrowserView(mainView); + const browViews = browsingWindow.getBrowserViews(); + browViews.forEach((view) => { + browsingWindow.removeBrowserView(view); + }); + if (isDifferentBrowser) { + mainWindow.addBrowserView(browViews[0]); + browsingWindow.addBrowserView(tabGroups[index][currentPipHostname] + .find(view => view.id !== browViews[0].id)); + } else { + mainView.webContents + .loadURL(mainView.webContents.history[mainView.webContents.history.length - 2]); + mainWindow.addBrowserView(browViews[0]); + browsingWindow.addBrowserView(mainView); } - mainWindow.addBrowserView(browserViews[0]); - if (browsingWindow) browsingWindow.addBrowserView(browserViews[1]); - if (browsingWindow) { - if (browsingWindow.isFullScreen()) { - hideBrowsingWindow = true; - browsingWindow.setFullScreen(false); - } else { - browsingWindow.hide(); - } + if (browsingWindow.isFullScreen()) { + hideBrowsingWindow = true; + browsingWindow.setFullScreen(false); + } else { + browsingWindow.hide(); } }); ipcMain.on('update-header-to-show', (e, headerToShow) => { diff --git a/static/pip/winTitlebar.html b/static/pip/winTitlebar.html index 1c8670c34f..503b683004 100644 --- a/static/pip/winTitlebar.html +++ b/static/pip/winTitlebar.html @@ -4,6 +4,12 @@ winTitlebar From e2ef1580e221eeccd0ce564bacdd03501a548425 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Tue, 20 Aug 2019 14:42:39 +0800 Subject: [PATCH 013/123] fix(browsingview): fix browsing control display error --- src/main/index.js | 24 +++++++-------- src/renderer/components/BrowsingPip.vue | 28 ++++++++++-------- src/renderer/components/BrowsingView.vue | 37 +++++++++++++++++------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index df4d42c486..ca27a71015 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -318,6 +318,7 @@ function createBrowsingWindow() { }); } browsingWindow.once('ready-to-show', () => { + browsingWindow.webContents.openDevTools(); // browsingWindow.show(); }); browsingWindow.on('leave-full-screen', () => { @@ -468,6 +469,7 @@ function registerMainWindowEvent(mainWindow) { browsingWindow.removeBrowserView(view); }); tabGroups[index][currentPipHostname].reverse(); + mainWindow.send('update-pip-state'); }); ipcMain.on('remove-browser', () => { const mainView = mainWindow.getBrowserView(); @@ -486,9 +488,6 @@ function registerMainWindowEvent(mainWindow) { if (browsingWindow) browsingWindow.hide(); } }); - ipcMain.on('update-pip-state', () => { - mainWindow.send('update-pip-state'); - }); ipcMain.on('shift-page-tab', (evt, url) => { if (!browsingWindow) createBrowsingWindow(); const isPip = browsingWindow && browsingWindow.isVisible(); @@ -539,7 +538,7 @@ function registerMainWindowEvent(mainWindow) { mainWindow.send('update-browser-view', !(currentBrowserHostname !== 'blank' && index === -1)); }); ipcMain.on('keep-browsers-cache', (evt, url) => { - if (browsingWindow) { + if (browsingWindow && !browsingWindow.getBrowserViews()[0].webContents.history.includes(url)) { browsingWindow.getBrowserViews()[0].webContents.loadURL(url); } }); @@ -679,10 +678,12 @@ function registerMainWindowEvent(mainWindow) { .find(view => view.id !== browViews[0].id).webContents.getURL(); browViews[0].webContents.loadURL(url); } else { + const index = browViews[0].webContents.history + .findIndex(url => url === mainView.webContents.getURL()); + browViews[0].webContents + .loadURL(mainView.webContents.history[index - 1]); mainWindow.addBrowserView(browViews[0]); browsingWindow.addBrowserView(mainView); - browViews[0].webContents - .loadURL(mainView.webContents.history[mainView.webContents.history.length - 2]); browsingWindow.addBrowserView(pipControlView); browsingWindow.addBrowserView(titlebarView); } @@ -698,12 +699,12 @@ function registerMainWindowEvent(mainWindow) { x: 0, y: 0, width: browsingWindow.getSize()[0], height: 36, }); browsingWindow.blur(); - browsingWindow.focus(); }); ipcMain.on('enter-pip', () => { createPipControlView(); createTitlebarView(); titlebarView.webContents.openDevTools({ mode: 'detach' }); + pipControlView.webContents.openDevTools({ mode: 'detach' }); if (browsingWindow.getBrowserViews()[0].webContents.canGoBack()) { browsingWindow.getBrowserViews()[0].webContents.goBack(); } @@ -736,13 +737,10 @@ function registerMainWindowEvent(mainWindow) { x: 0, y: 0, width: browsingWindow.getSize()[0], height: 36, }); browsingWindow.blur(); - browsingWindow.focus(); - }); - ipcMain.on('set-control-bounds', (evt, args) => { - if (pipControlView) pipControlView.setBounds(args); }); - ipcMain.on('set-titlebar-bounds', (evt, args) => { - if (titlebarView) titlebarView.setBounds(args); + ipcMain.on('set-bounds', (evt, args) => { + if (pipControlView) pipControlView.setBounds(args.control); + if (titlebarView) titlebarView.setBounds(args.titlebar); }); ipcMain.on('exit-pip', () => { const isDifferentBrowser = currentPipHostname !== '' && currentBrowserHostname !== currentPipHostname; diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index ce7312aa54..ddbf66711b 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -21,27 +21,31 @@ export default { electron.ipcRenderer.send('update-focused-window', false); electron.ipcRenderer.send('update-enabled', 'window.pip', true); electron.ipcRenderer.send('update-enabled', 'window.keepPlayingWindowFront', true); + electron.ipcRenderer.send('update-enabled', 'history.back', false); + electron.ipcRenderer.send('update-enabled', 'history.forward', false); + electron.ipcRenderer.send('update-enabled', 'history.reload', false); }); window.addEventListener('resize', throttle(() => { const size = electron.remote.getCurrentWindow().getSize(); electron.ipcRenderer.send('pip-window-size', size); - electron.ipcRenderer.send('set-titlebar-bounds', { - x: 0, - y: 0, - width: size[0], - height: 36, - }); - electron.ipcRenderer.send('set-control-bounds', { - x: Math.round(size[0] - 65), - y: Math.round(size[1] / 2 - 54), - width: 50, - height: 104, + electron.ipcRenderer.send('set-bounds', { + titlebar: { + x: 0, + y: 0, + width: size[0], + height: 36, + }, + control: { + x: Math.round(size[0] - 65), + y: Math.round(size[1] / 2 - 54), + width: 50, + height: 104, + }, }); }, 100)); window.addEventListener('beforeunload', () => { electron.ipcRenderer.send('store-pip-pos'); electron.ipcRenderer.send('pip-window-close'); - electron.ipcRenderer.send('update-pip-state'); }); }, }; diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index ae468273ed..eff59639e1 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -210,15 +210,9 @@ export default { setTimeout(() => { if (this.acceleratorAvailable) { if (!focusedOnMainWindow) { - this.updateIsPip(false); - this.exitPipOperation(); - } else if (this.hasVideo) { - if (this.isPip) { - this.shiftPipOperation(); - } else { - this.updateIsPip(true); - this.enterPipOperation(); - } + this.handleExitPip(); + } else { + this.handleEnterPip(); } } else { this.acceleratorAvailable = true; @@ -365,7 +359,9 @@ export default { this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('new-window', (e: Event, url: string, disposition: string) => { this.newWindow(url, disposition); }); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', (e: Event, url: string) => { + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.addListener('did-start-navigation', () => { + const url = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getURL(); if (url !== 'about:blank' && !this.isPip) { this.$electron.ipcRenderer.send('keep-browsers-cache', url); } @@ -461,7 +457,6 @@ export default { } break; default: - console.warn(`Unhandled ipc-message: ${channel}`, args); break; } }, @@ -631,12 +626,32 @@ export default { }, handleEnterPip() { if (this.hasVideo) { + const loadUrl = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getURL(); + const recordIndex = this.supportedRecordHost.indexOf(urlParseLax(loadUrl).hostname); if (this.isPip) { this.shiftPipOperation(); } else { this.updateIsPip(true); this.enterPipOperation(); } + this.$electron.ipcRenderer.send('update-enabled', 'window.pip', false); + this.$refs.browsingHeader.updateWebInfo({ + hasVideo: false, + url: loadUrl, + canGoBack: this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.canGoBack(), + canGoForward: this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.canGoForward(), + }); + this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents + .executeJavaScript(this.calculateVideoNum, (r: number) => { + this.hasVideo = recordIndex === 0 && !getVideoId(loadUrl).id ? false : !!r; + this.$electron.ipcRenderer.send('update-enabled', 'window.pip', this.hasVideo); + this.$refs.browsingHeader.updateWebInfo({ + hasVideo: this.hasVideo, + }); + }); } }, handleExitPip() { From e37a3dfadc66a815e49971452e0c237e847edb9b Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Tue, 20 Aug 2019 16:13:57 +0800 Subject: [PATCH 014/123] style(browsingview): add icons to win titlebar --- src/renderer/components/BrowsingPip.vue | 13 ++- .../assets/titleBarWinClose-default-icon.svg | 17 +++ .../titleBarWinExitFull-default-icon.svg | 17 +++ .../assets/titleBarWinFull-default-icon.svg | 17 +++ .../assets/titleBarWinResize-default-icon.svg | 17 +++ .../titleBarWinRestore-default-icon.svg | 17 +++ static/pip/titlebarPreload.js | 105 ++++++++++-------- static/pip/winTitlebar.html | 21 +++- 8 files changed, 174 insertions(+), 50 deletions(-) create mode 100755 static/pip/assets/titleBarWinClose-default-icon.svg create mode 100755 static/pip/assets/titleBarWinExitFull-default-icon.svg create mode 100755 static/pip/assets/titleBarWinFull-default-icon.svg create mode 100644 static/pip/assets/titleBarWinResize-default-icon.svg create mode 100644 static/pip/assets/titleBarWinRestore-default-icon.svg diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index ddbf66711b..f178fbd5b3 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -1,5 +1,10 @@ + diff --git a/src/renderer/containers/LandingView.vue b/src/renderer/containers/LandingView.vue index 9c52624573..627b2bbb81 100644 --- a/src/renderer/containers/LandingView.vue +++ b/src/renderer/containers/LandingView.vue @@ -1,29 +1,5 @@ @@ -162,9 +137,6 @@ import { filePathToUrl } from '@/helpers/path'; import { playInfoStorageService } from '@/services/storage/PlayInfoStorageService'; import { recentPlayService } from '@/services/media/RecentPlayService'; import Icon from '@/components/BaseIconContainer.vue'; -import BilibiliSidebarIcon from '@/components/LandingView/BilibiliSidebarIcon.vue'; -import iQiyiSidebarIcon from '@/components/LandingView/iQiyiSidebarIcon.vue'; -import YoutubeSidebarIcon from '@/components/LandingView/YoutubeSidebarIcon.vue'; import OpenUrl from '@/components/LandingView/OpenUrl.vue'; import NotificationBubble from '@/components/NotificationBubble.vue'; import PlaylistItem from '@/components/LandingView/PlaylistItem.vue'; @@ -173,7 +145,6 @@ import { log } from '@/libs/Log'; import Sagi from '@/libs/sagi'; import { findNsfwFistFilter } from '@/libs/utils'; import { Browsing as browsingActions } from '@/store/actionTypes'; -import asyncStorage from '@/helpers/asyncStorage'; Vue.component('PlaylistItem', PlaylistItem); Vue.component('VideoItem', VideoItem); @@ -182,9 +153,6 @@ export default { name: 'LandingView', components: { Icon, - BilibiliSidebarIcon, - iQiyiSidebarIcon, - YoutubeSidebarIcon, NotificationBubble, 'open-url': OpenUrl, }, @@ -376,18 +344,6 @@ export default { move(steps: number) { return steps * (this.thumbnailWidth + this.marginRight); }, - handleSidebarIcon(site: string) { - asyncStorage.get('browsingPip').then((data) => { - this.$store.dispatch('updatePipSize', data.pipSize || this.pipSize); - this.$store.dispatch('updatePipPos', data.pipPos || this.pipPos); - this.$electron.ipcRenderer.send('add-browsing', { size: data.pipSize || this.pipSize, position: data.pipPos || this.pipPos }); - }); - const url = `https://www.${site}.com`; - this.$electron.ipcRenderer.send('change-channel', { url }); - this.$router.push({ - name: 'browsing-view', - }); - }, handleBrowsingOpen(url: string) { this.updateInitialUrl(url); this.$router.push({ @@ -472,33 +428,6 @@ export default { $themeColor-Light: white; .landing-view { - height: 100vh; - width: 100vw; -} -.side-bar { - position: absolute; - background-color: #39383F; - z-index: 0; - left: 0; - height: 100%; - transition: width 100ms ease-out; - will-change: width; - - .icon-box { - width: 40px; - margin-top: 46px; - margin-left: 18px; - margin-right: 18px; - display: flex; - flex-direction: column; - div { - width: 40px; - height: 40px; - margin-bottom: 16px; - } - } -} -.wrapper { overflow: hidden; will-change: width; transition-property: width; From ff30131a52f397c8d08a5fede08a69158c2a6eca Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Mon, 2 Sep 2019 19:37:14 +0800 Subject: [PATCH 034/123] feat(sidebar): browsingview --- src/renderer/components/Sidebar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/Sidebar.vue b/src/renderer/components/Sidebar.vue index 14ad30958d..c58511d032 100644 --- a/src/renderer/components/Sidebar.vue +++ b/src/renderer/components/Sidebar.vue @@ -42,7 +42,7 @@ export default { this.$electron.ipcRenderer.send('add-browsing', { size: data.pipSize || this.pipSize, position: data.pipPos || this.pipPos }); }); const url = `https://www.${site}.com`; - this.$electron.ipcRenderer.send('change-channel', { url }); + this.$electron.ipcRenderer.send('change-channel', { url, sidebar: this.showSidebar }); if (this.$router.currentRoute.name !== 'browsing-view') this.$router.push({ name: 'browsing-view' }); }, From fb914365e964bb9f6c1c2250aa2ff37a2e39564a Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Tue, 3 Sep 2019 12:37:34 +0800 Subject: [PATCH 035/123] feat(browsingview): support dragging pip window with osx-mouse-cocoa --- package.json | 2 ++ scripts/post-install.js | 4 ++- src/main/helpers/mouse.ts | 14 ++------ src/main/index.js | 11 ++++++- src/renderer/components/BrowsingPip.vue | 44 +++++++++++++++++++++---- static/pip/preload.js | 27 +++++---------- static/pip/titlebarPreload.js | 18 +++++++++- 7 files changed, 81 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index 66b376d231..74fc6008ac 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", "rebuild:win-mouse": "electron-rebuild -f -e node_modules/@chiflix/electron -w win-mouse", + "rebuild:osx-mouse-cocoa": "electron-rebuild -f -e node_modules/@chiflix/electron -w osx-mouse-cocoa", "test": "npm run unit && npm run e2e", "unit": "karma start test/unit/karma.conf.js", "postinstall": "node ./scripts/post-install.js" @@ -89,6 +90,7 @@ "winston-daily-rotate-file": "^3.9.0" }, "optionalDependencies": { + "osx-mouse-cocoa": "github:Pat1enceLos/osx-mouse-cocoa", "win-mouse": "1.3.0" }, "devDependencies": { diff --git a/scripts/post-install.js b/scripts/post-install.js index c146bc4612..a728cce16b 100644 --- a/scripts/post-install.js +++ b/scripts/post-install.js @@ -7,11 +7,13 @@ require('events').EventEmitter.prototype._maxListeners = 10000; const commands = [ 'npx rimraf node_modules/**/.git', 'npm run lint:fix', + 'npm run install-app-deps', ]; if (process.platform === 'win32') { - commands.push('npm run install-app-deps'); commands.push('npm run rebuild:win-mouse'); +} else { + commands.push('npm run rebuild:osx-mouse-cocoa'); } exec(commands.join('&&'), (error, stdout) => { diff --git a/src/main/helpers/mouse.ts b/src/main/helpers/mouse.ts index 422b2afb02..bbede8f3a1 100644 --- a/src/main/helpers/mouse.ts +++ b/src/main/helpers/mouse.ts @@ -4,7 +4,7 @@ interface IMouse { dispose(): void, } -class WinMouse implements IMouse { +class Mouse implements IMouse { private mouse: { on(channel: string, callback: (x: number, y: number) => void): void, off(channel: string, callback?: (x: number, y: number) => void): void, @@ -13,7 +13,7 @@ class WinMouse implements IMouse { public constructor() { try { - const mouseConstructor = require('win-mouse'); //eslint-disable-line + const mouseConstructor = process.platform === 'win32' ? require('win-mouse') : require('osx-mouse-cocoa'); //eslint-disable-line this.mouse = mouseConstructor(); } catch (ex) { console.error(ex); @@ -34,14 +34,4 @@ class WinMouse implements IMouse { } } -class FakeMouse implements IMouse { - public on() {} //eslint-disable-line - - public off() {} //eslint-disable-line - - public dispose() {} //eslint-disable-line -} - -const Mouse = process.platform === 'win32' ? WinMouse : FakeMouse; - export const mouse: IMouse = new Mouse(); diff --git a/src/main/index.js b/src/main/index.js index 205ec1b0d6..6f8190c052 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -608,6 +608,11 @@ function registerMainWindowEvent(mainWindow) { + 'document.querySelector(".titlebarFull").style.display = "block";'); } }); + ipcMain.on('update-mouse-info', (evt, args) => { + if (browsingWindow && browsingWindow.isFocused()) { + browsingWindow.send('update-mouse-info', args); + } + }); ipcMain.on('mouseup', (evt, type) => { switch (type) { case 'close': @@ -989,7 +994,11 @@ function createMainWindow(openDialog, playlistId) { ['left-drag', 'left-up'].forEach((channel) => { mouse.on(channel, (...args) => { if (!mainWindow || mainWindow.webContents.isDestroyed()) return; - mainWindow.webContents.send(`mouse-${channel}`, ...args); + if (process.platform === 'win32') { + BrowserWindow.getFocusedWindow().webContents.send(`mouse-${channel}`, ...args); + } else if (browsingWindow && browsingWindow.isFocused()) { + browsingWindow.webContents.send(`mouse-${channel}`, ...args); + } }); }); diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index a14f34ad03..fdd8fcd838 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -1,10 +1,5 @@ @@ -79,5 +110,6 @@ export default { height: 36px; position: absolute; top: 0; + -webkit-app-region: no-drag; } diff --git a/static/pip/preload.js b/static/pip/preload.js index 61ccb8fbe1..5b5a68ae2d 100644 --- a/static/pip/preload.js +++ b/static/pip/preload.js @@ -2,11 +2,10 @@ // eslint-disable-next-line no-console console.log('preloaded~~~~~~~'); const { ipcRenderer, remote } = require('electron'); -const mouse = process.platform === 'win32' ? require('win-mouse')() : null; let mousedown = false; let isDragging = false; -let mousedownPos = null; +let offset = null; let windowSize = null; let pipTimer = 0; function sendToHost(channel, message) { @@ -54,30 +53,21 @@ document.addEventListener('DOMContentLoaded', () => { }, true); window.addEventListener('mousedown', (evt) => { mousedown = true; - mousedownPos = [evt.clientX, evt.clientY]; + offset = [evt.clientX, evt.clientY]; if (getRatio() !== 1) { windowSize = remote.getCurrentWindow().getSize(); } + if (!pipBtns) { + sendToHost('update-mouse-info', { offset, windowSize }); + } }, true); window.addEventListener('mouseup', (evt) => { if (isDragging && !pipBtns) evt.stopImmediatePropagation(); mousedown = false; - mousedownPos = null; + offset = null; windowSize = null; + if (!pipBtns) sendToHost('update-mouse-info', { offset, windowSize }); }, true); - if (mouse) { - mouse.on('left-drag', (x, y) => { - sendToHost('mousemove', 'isMoving'); - isDragging = true; - if (mousedownPos) { - sendToHost('left-drag', { - windowSize, - x: Math.round(x / getRatio() - mousedownPos[0]), - y: Math.round(y / getRatio() - mousedownPos[1]), - }); - } - }); - } window.addEventListener('mousemove', (evt) => { if (!pipBtns && remote.getCurrentWindow() && remote.getCurrentWindow().getBrowserViews().length > 1) { @@ -89,8 +79,9 @@ document.addEventListener('DOMContentLoaded', () => { window.addEventListener('click', (evt) => { if (isDragging) evt.stopImmediatePropagation(); isDragging = false; - mousedownPos = null; + offset = null; windowSize = null; + if (!pipBtns) sendToHost('update-mouse-info', { offset, windowSize }); }, true); window.addEventListener('drop', (evt) => { evt.preventDefault(); diff --git a/static/pip/titlebarPreload.js b/static/pip/titlebarPreload.js index a039257a1a..0503f0a3bf 100644 --- a/static/pip/titlebarPreload.js +++ b/static/pip/titlebarPreload.js @@ -3,7 +3,11 @@ console.log('titlebar-preloaded~~~~~~~'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { ipcRenderer, remote } = require('electron'); const isDarwin = process.platform === 'darwin'; - +let offset = null; +let windowSize = null; +function getRatio() { + return window.devicePixelRatio || 1; +} document.addEventListener('DOMContentLoaded', () => { const titlebar = document.querySelector('.titlebar'); const content = document.querySelector('.content'); @@ -63,6 +67,18 @@ document.addEventListener('DOMContentLoaded', () => { }); } } + window.addEventListener('mousedown', (evt) => { + offset = [evt.clientX, evt.clientY]; + if (getRatio() !== 1) { + windowSize = remote.getCurrentWindow().getSize(); + } + if ([content, titlebar].includes(evt.target)) ipcRenderer.send('update-mouse-info', { offset, windowSize }); + }, true); + window.addEventListener('mouseup', () => { + offset = null; + windowSize = null; + ipcRenderer.send('update-mouse-info', { offset, windowSize }); + }); window.addEventListener('keydown', (e) => { ipcRenderer.send('key-events', e.keyCode); }); From 19f8b5b51c1b17598b64eb91ae3499ba6f63f425 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Tue, 3 Sep 2019 14:09:41 +0800 Subject: [PATCH 036/123] style(browsing): eslint warning --- src/renderer/components/BrowsingView.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 5072ecdc89..1c0c7f4d3c 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -215,9 +215,6 @@ export default { this.$electron.ipcRenderer.send('callMainWindowMethod', 'setSize', this.browsingSize); this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', this.browsingPos); this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); - if (this.showSidebar) { - const browserWindow = this.$electron.remote.getCurrentWindow().getBrowserViews(); - } }, mounted() { this.menuService = new MenuService(); From c55d2cb2a2608bca5799108db5167ac11c2bf178 Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Tue, 3 Sep 2019 14:55:49 +0800 Subject: [PATCH 037/123] fix(browsingview): fix some error about windows drag --- src/main/index.js | 4 ++-- src/renderer/components/BrowsingPip.vue | 21 +++++++++++---------- static/pip/preload.js | 3 ++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index 61e9789ea1..e9e285e7ab 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -996,9 +996,9 @@ function createMainWindow(openDialog, playlistId) { ['left-drag', 'left-up'].forEach((channel) => { mouse.on(channel, (...args) => { - if (!mainWindow || mainWindow.webContents.isDestroyed()) return; if (process.platform === 'win32') { - BrowserWindow.getFocusedWindow().webContents.send(`mouse-${channel}`, ...args); + const focusedWindow = BrowserWindow.getFocusedWindow(); + if (focusedWindow) focusedWindow.send(`mouse-${channel}`, ...args); } else if (browsingWindow && browsingWindow.isFocused()) { browsingWindow.webContents.send(`mouse-${channel}`, ...args); } diff --git a/src/renderer/components/BrowsingPip.vue b/src/renderer/components/BrowsingPip.vue index fdd8fcd838..5f314fca0e 100644 --- a/src/renderer/components/BrowsingPip.vue +++ b/src/renderer/components/BrowsingPip.vue @@ -26,14 +26,6 @@ export default { }, }, mounted() { - window.addEventListener('focus', () => { - const cursorPoint = electron.screen.getCursorScreenPoint(); - const windowPos = electron.remote.getCurrentWindow().getPosition(); - this.offset = [cursorPoint.x - windowPos[0], cursorPoint.y - windowPos[1]]; - if (this.getRatio() !== 1) { - this.windowSize = electron.remote.getCurrentWindow().getSize(); - } - }); electron.ipcRenderer.on('update-mouse-info', (evt: Event, args: { windowSize: number[] | null, offset: number[]}) => { this.offset = args.offset; this.windowSize = args.windowSize; @@ -43,10 +35,19 @@ export default { this.windowSize = null; }); electron.ipcRenderer.on('mouse-left-drag', (evt: Event, x: number, y: number) => { - if (!this.offset) return; + if (!this.offset) { + const cursorPoint = electron.screen.getCursorScreenPoint(); + const windowPos = electron.remote.getCurrentWindow().getPosition(); + this.offset = [cursorPoint.x - windowPos[0], cursorPoint.y - windowPos[1]]; + if (this.getRatio() !== 1) { + this.windowSize = electron.remote.getCurrentWindow().getSize(); + } + } x = Math.round((x / this.getRatio()) - this.offset[0]); y = Math.round((y / this.getRatio()) - this.offset[1]); - if (this.windowSize) { + if (this.isDarwin) { + electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [x, y]); + } else if (this.windowSize) { electron.ipcRenderer.send('callBrowsingWindowMethod', 'setBounds', [{ x, y, width: this.windowSize[0], height: this.windowSize[1], }]); diff --git a/static/pip/preload.js b/static/pip/preload.js index 5b5a68ae2d..942fa9d379 100644 --- a/static/pip/preload.js +++ b/static/pip/preload.js @@ -57,7 +57,8 @@ document.addEventListener('DOMContentLoaded', () => { if (getRatio() !== 1) { windowSize = remote.getCurrentWindow().getSize(); } - if (!pipBtns) { + if (!pipBtns && remote.getCurrentWindow() + && remote.getCurrentWindow().getBrowserViews().length > 1) { sendToHost('update-mouse-info', { offset, windowSize }); } }, true); From 3fc71b2beb4d09106eae4db9606e92673de8b28b Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Tue, 3 Sep 2019 16:03:15 +0800 Subject: [PATCH 038/123] feat(browsing): ui structure adjustment --- src/main/index.js | 4 +- .../assets/icon/back-default-icon.svg | 2 +- .../assets/icon/backDisabled-default-icon.svg | 2 +- .../assets/icon/forward-default-icon.svg | 2 +- .../icon/forwardDisabled-default-icon.svg | 2 +- .../assets/icon/pageRefresh-default-icon.svg | 2 +- src/renderer/assets/icon/pip-default-icon.svg | 2 +- .../assets/icon/pipDisabled-default-icon.svg | 2 +- src/renderer/components/BrowsingView.vue | 1 + .../BrowsingView/BrowsingControl.vue | 41 ++-- .../BrowsingView/BrowsingHeader.vue | 53 ++--- .../components/BrowsingView/BrowsingInput.vue | 33 ++- .../components/BrowsingView/BrowsingPip.vue | 49 ++++ .../components/LandingView/SidebarIcon.vue | 6 +- src/renderer/components/Titlebar.vue | 6 + src/renderer/containers/LandingView.vue | 218 +++++++++--------- 16 files changed, 230 insertions(+), 195 deletions(-) create mode 100644 src/renderer/components/BrowsingView/BrowsingPip.vue diff --git a/src/main/index.js b/src/main/index.js index 87cf6d07d5..43575675e9 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -526,9 +526,9 @@ function registerMainWindowEvent(mainWindow) { }, 150); view.setBounds({ x: args.sidebar ? 76 : 0, - y: 36, + y: 40, width: args.sidebar ? mainWindow.getSize()[0] - 76 : mainWindow.getSize()[0], - height: mainWindow.getSize()[1] - 36, + height: mainWindow.getSize()[1] - 40, }); view.setAutoResize({ width: true, height: true, diff --git a/src/renderer/assets/icon/back-default-icon.svg b/src/renderer/assets/icon/back-default-icon.svg index 191e088410..01a306ca22 100644 --- a/src/renderer/assets/icon/back-default-icon.svg +++ b/src/renderer/assets/icon/back-default-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/assets/icon/backDisabled-default-icon.svg b/src/renderer/assets/icon/backDisabled-default-icon.svg index c1a62a778e..dd008368aa 100644 --- a/src/renderer/assets/icon/backDisabled-default-icon.svg +++ b/src/renderer/assets/icon/backDisabled-default-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/assets/icon/forward-default-icon.svg b/src/renderer/assets/icon/forward-default-icon.svg index 55732794ea..ba36a7e021 100644 --- a/src/renderer/assets/icon/forward-default-icon.svg +++ b/src/renderer/assets/icon/forward-default-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/assets/icon/forwardDisabled-default-icon.svg b/src/renderer/assets/icon/forwardDisabled-default-icon.svg index 81c3a5b2d9..97e349cfad 100644 --- a/src/renderer/assets/icon/forwardDisabled-default-icon.svg +++ b/src/renderer/assets/icon/forwardDisabled-default-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/assets/icon/pageRefresh-default-icon.svg b/src/renderer/assets/icon/pageRefresh-default-icon.svg index be5b674741..88dd7cc223 100644 --- a/src/renderer/assets/icon/pageRefresh-default-icon.svg +++ b/src/renderer/assets/icon/pageRefresh-default-icon.svg @@ -3,7 +3,7 @@ Created with Sketch. - + diff --git a/src/renderer/assets/icon/pip-default-icon.svg b/src/renderer/assets/icon/pip-default-icon.svg index 77da00f98e..643a634875 100644 --- a/src/renderer/assets/icon/pip-default-icon.svg +++ b/src/renderer/assets/icon/pip-default-icon.svg @@ -3,7 +3,7 @@ Created with Sketch. - + diff --git a/src/renderer/assets/icon/pipDisabled-default-icon.svg b/src/renderer/assets/icon/pipDisabled-default-icon.svg index 7db10cc8c8..44400f2d8f 100644 --- a/src/renderer/assets/icon/pipDisabled-default-icon.svg +++ b/src/renderer/assets/icon/pipDisabled-default-icon.svg @@ -3,7 +3,7 @@ Created with Sketch. - + diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 1c0c7f4d3c..f25ba8f001 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -699,6 +699,7 @@ export default { .browsing { position: absolute; right: 0; + border-top-left-radius: 4px; height: 100vh; width: 100vw; display: flex; diff --git a/src/renderer/components/BrowsingView/BrowsingControl.vue b/src/renderer/components/BrowsingView/BrowsingControl.vue index 737c44cccd..051120e7a0 100644 --- a/src/renderer/components/BrowsingView/BrowsingControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingControl.vue @@ -1,11 +1,17 @@
+ -
+ \ No newline at end of file diff --git a/src/renderer/components/LandingView/SidebarIcon.vue b/src/renderer/components/LandingView/SidebarIcon.vue index b2da54d802..5ded19fbd0 100644 --- a/src/renderer/components/LandingView/SidebarIcon.vue +++ b/src/renderer/components/LandingView/SidebarIcon.vue @@ -24,7 +24,7 @@ @@ -34,6 +34,10 @@ \ No newline at end of file From 7c35dbdbb9c485833bc1d916eff33cecbb125e54 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Tue, 3 Sep 2019 18:09:48 +0800 Subject: [PATCH 042/123] feat(browsing): sidebar --- src/main/helpers/BrowserViewManager.ts | 22 +- src/renderer/App.vue | 3 + src/renderer/components/BrowsingView.vue | 636 +++++++++++++----- .../BrowsingView/BrowsingControl.vue | 7 +- .../BrowsingView/BrowsingHeader.vue | 26 +- .../components/BrowsingView/BrowsingInput.vue | 2 +- .../BrowsingView/BrowsingPipControl.vue | 2 +- .../components/LandingView/SidebarIcon.vue | 9 +- src/renderer/components/Titlebar.vue | 5 - src/renderer/containers/LandingView.vue | 11 +- 10 files changed, 495 insertions(+), 228 deletions(-) diff --git a/src/main/helpers/BrowserViewManager.ts b/src/main/helpers/BrowserViewManager.ts index 676e94c4db..05d59bf7fe 100644 --- a/src/main/helpers/BrowserViewManager.ts +++ b/src/main/helpers/BrowserViewManager.ts @@ -93,19 +93,17 @@ export class BrowserViewManager implements IBrowserViewManager { const currentIndex = this.history[this.currentChannel].currentIndex; const view = this.history[this.currentChannel].list[currentIndex].view; this.pauseVideo(view); - } else { + } else if (this.backPage.length) { // 清除后退的记录 - if (this.backPage.length) { - remove(this.history[this.currentChannel].list, - (list: BrowserViewHistory) => { - if (this.backPage.includes(list)) { - list.view.destroy(); - return true; - } - return false; - }); - this.backPage = []; - } + remove(this.history[this.currentChannel].list, + (list: BrowserViewHistory) => { + if (this.backPage.includes(list)) { + list.view.destroy(); + return true; + } + return false; + }); + this.backPage = []; } } diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 3b1829fd1e..cb70dad7eb 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -63,6 +63,9 @@ export default { }, }, mounted() { + this.$event.on('side-bar-mouseup', () => { + this.showSidebar = !this.showSidebar; + }); ipcRenderer.on('open-file', (event: Event, args: { onlySubtitle: boolean, files: string[] }) => { this.openFileArgs = args; }); diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 73e75b8294..d8b273bcfc 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -8,6 +8,7 @@ > fs.statSync(file).isDirectory()); - if (onlyFolders || val.every((file: fs.PathLike) => getValidVideoRegex() - .test(file) && !getValidSubtitleRegex().test(file))) { + if ( + onlyFolders + || val.every( + (file: fs.PathLike) => getValidVideoRegex().test(file) + && !getValidSubtitleRegex().test(file), + ) + ) { val.forEach((file: fs.PathLike) => this.$electron.remote.app.addRecentDocument(file)); if (onlyFolders) { this.openFolder(...val); @@ -174,17 +202,27 @@ export default { } }, loadingState(val: boolean) { - const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL(); + const loadUrl = this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.getURL(); const hostname = urlParseLax(loadUrl).hostname; let channel = hostname.slice(hostname.indexOf('.') + 1, hostname.length); if (loadUrl.includes('youtube')) { channel = 'youtube.com'; } - this.updateCanGoBack(this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.canGoBack()); - this.updateCanGoForward(this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.canGoForward()); + this.updateCanGoBack( + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.canGoBack(), + ); + this.updateCanGoForward( + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.canGoForward(), + ); if (val) { this.hasVideo = false; } else { @@ -192,30 +230,58 @@ export default { this.pipAdapter(); this.pipRestore = false; } - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents - .executeJavaScript(this.calculateVideoNum, (r: number) => { - this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id ? false : !!r; - }); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript( + this.calculateVideoNum, + (r: number) => { + this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id + ? false + : !!r; + }, + ); } }, headerToShow(val: boolean) { - const currentView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; + const currentView = this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0]; if (!val) { currentView.setBounds({ - x: 0, y: 0, width: window.screen.width, height: window.screen.height, + x: 0, + y: 0, + width: window.screen.width, + height: window.screen.height, }); } else { currentView.setBounds({ - x: 0, y: 36, width: this.winSize[0], height: this.winSize[1] - 36, + x: 0, + y: 36, + width: this.winSize[0], + height: this.winSize[1] - 36, }); } }, }, created() { - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setMinimumSize', [570, 375]); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setSize', this.browsingSize); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setPosition', this.browsingPos); - this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [0]); + this.$electron.ipcRenderer.send('callMainWindowMethod', 'setMinimumSize', [ + 570, + 375, + ]); + this.$electron.ipcRenderer.send( + 'callMainWindowMethod', + 'setSize', + this.browsingSize, + ); + this.$electron.ipcRenderer.send( + 'callMainWindowMethod', + 'setPosition', + this.browsingPos, + ); + this.$electron.ipcRenderer.send('callMainWindowMethod', 'setAspectRatio', [ + 0, + ]); }, mounted() { this.menuService = new MenuService(); @@ -257,55 +323,81 @@ export default { this.$electron.ipcRenderer.on('quit', () => { this.quit = true; }); - this.$electron.ipcRenderer.on('update-pip-size', (e: Event, args: number[]) => { - this.$store.dispatch('updatePipSize', args); - }); - this.$electron.ipcRenderer.on('update-pip-state', (e: Event, info: { size: number[], position: number[] }) => { - this.$store.dispatch('updatePipPos', info.position); - this.$store.dispatch('updatePipSize', info.size); - this.updateIsPip(false); - }); - this.$electron.ipcRenderer.on('update-browser-state', (e: Event, state: { url: string, canGoBack: boolean, canGoForward: boolean }) => { - this.currentUrl = urlParseLax(state.url).href; - this.removeListener(); - this.addListenerToBrowser(); - this.canGoBack = state.canGoBack; - this.canGoForward = state.canGoForward; - const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL(); - const hostname = urlParseLax(loadUrl).hostname; - let channel = hostname.slice(hostname.indexOf('.') + 1, hostname.length); - if (loadUrl.includes('youtube')) { - channel = 'youtube.com'; - } - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents - .executeJavaScript(this.calculateVideoNum, (r: number) => { - this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id ? false : !!r; + this.$electron.ipcRenderer.on( + 'update-pip-size', + (e: Event, args: number[]) => { + this.$store.dispatch('updatePipSize', args); + }, + ); + this.$electron.ipcRenderer.on( + 'update-pip-state', + (e: Event, info: { size: number[]; position: number[] }) => { + this.$store.dispatch('updatePipPos', info.position); + this.$store.dispatch('updatePipSize', info.size); + this.updateIsPip(false); + }, + ); + this.$electron.ipcRenderer.on( + 'update-browser-state', + ( + e: Event, + state: { url: string; canGoBack: boolean; canGoForward: boolean }, + ) => { + this.currentUrl = urlParseLax(state.url).href; + this.removeListener(); + this.addListenerToBrowser(); + this.canGoBack = state.canGoBack; + this.canGoForward = state.canGoForward; + const loadUrl = this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.getURL(); + const hostname = urlParseLax(loadUrl).hostname; + let channel = hostname.slice( + hostname.indexOf('.') + 1, + hostname.length, + ); + if (loadUrl.includes('youtube')) { + channel = 'youtube.com'; + } + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript( + this.calculateVideoNum, + (r: number) => { + this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id + ? false + : !!r; + }, + ); + this.$bus.$emit('update-web-info', { + canGoBack: state.canGoBack, + canGoForward: state.canGoForward, }); - this.$bus.$emit('update-web-info', { - canGoBack: state.canGoBack, - canGoForward: state.canGoForward, - }); - }); + }, + ); }, beforeDestroy() { this.removeListener(); this.$store.dispatch('updateBrowsingSize', this.winSize); this.$store.dispatch('updateBrowsingPos', this.winPos); this.updateIsPip(false); - asyncStorage.set('browsing', { - browsingSize: this.browsingSize, - browsingPos: this.browsingPos, - barrageOpen: this.barrageOpen, - }).finally(() => { - this.menuService.updateMenuItemEnabled('file.open', true); - window.removeEventListener('beforeunload', this.beforeUnloadHandler); - window.removeEventListener('focus', this.focusHandler); - if (this.backToLandingView) { - this.$electron.ipcRenderer.send('remove-browser'); - windowRectService.uploadWindowBy(false, 'landing-view'); - } - }); + asyncStorage + .set('browsing', { + browsingSize: this.browsingSize, + browsingPos: this.browsingPos, + barrageOpen: this.barrageOpen, + }) + .finally(() => { + this.menuService.updateMenuItemEnabled('file.open', true); + window.removeEventListener('beforeunload', this.beforeUnloadHandler); + window.removeEventListener('focus', this.focusHandler); + if (this.backToLandingView) { + this.$electron.ipcRenderer.send('remove-browser'); + windowRectService.uploadWindowBy(false, 'landing-view'); + } + }); }, methods: { ...mapActions({ @@ -320,20 +412,32 @@ export default { focusHandler() { this.menuService.updateFocusedWindow(true); this.updatePipState(this.hasVideo); - this.updateCanGoBack(this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.canGoBack()); - this.updateCanGoForward(this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.canGoForward()); + this.updateCanGoBack( + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.canGoBack(), + ); + this.updateCanGoForward( + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.canGoForward(), + ); this.updateReload(true); - const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL(); + const loadUrl = this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.getURL(); const hostname = urlParseLax(loadUrl).hostname; let channel = hostname.slice(hostname.indexOf('.') + 1, hostname.length); if (loadUrl.includes('youtube')) { channel = 'youtube.com'; } - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents - .executeJavaScript(this.calculateVideoNum, (r: number) => { + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript(this.calculateVideoNum, (r: number) => { this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id ? false : !!r; }); }, @@ -343,31 +447,40 @@ export default { e.returnValue = false; this.$store.dispatch('updateBrowsingSize', this.winSize); this.$store.dispatch('updateBrowsingPos', this.winPos); - asyncStorage.set('browsing', { - browsingSize: this.browsingSize, - browsingPos: this.browsingPos, - barrageOpen: this.barrageOpen, - }).finally(() => { - this.asyncTasksDone = true; - if (!this.isPip) { - window.close(); - } else { - this.isGlobal = true; - this.$electron.ipcRenderer.send('remove-main-window'); - } - }); + asyncStorage + .set('browsing', { + browsingSize: this.browsingSize, + browsingPos: this.browsingPos, + barrageOpen: this.barrageOpen, + }) + .finally(() => { + this.asyncTasksDone = true; + if (!this.isPip) { + window.close(); + } else { + this.isGlobal = true; + this.$electron.ipcRenderer.send('remove-main-window'); + } + }); } else if (this.quit) { this.$electron.remote.app.quit(); } }, updateReload(val: boolean) { if (this.$electron.remote.getCurrentWindow().isFocused()) { - this.$electron.ipcRenderer.send('update-enabled', 'history.reload', val); + this.$electron.ipcRenderer.send( + 'update-enabled', + 'history.reload', + val, + ); } }, updatePipState(available: boolean) { if (this.$electron.remote.getCurrentWindow().isFocused()) { - this.menuService.updateMenuItemEnabled('browsing.window.pip', available); + this.menuService.updateMenuItemEnabled( + 'browsing.window.pip', + available, + ); } }, updateCanGoBack(val: boolean) { @@ -377,15 +490,29 @@ export default { }, updateCanGoForward(val: boolean) { if (this.$electron.remote.getCurrentWindow().isFocused()) { - this.$electron.ipcRenderer.send('update-enabled', 'history.forward', val); + this.$electron.ipcRenderer.send( + 'update-enabled', + 'history.forward', + val, + ); } }, handleBookmarkOpen(url: string) { - const supportedPage = ['https://www.youtube.com/', 'https://www.bilibili.com/', 'https://www.iqiyi.com/']; + const supportedPage = [ + 'https://www.youtube.com/', + 'https://www.bilibili.com/', + 'https://www.iqiyi.com/', + ]; const newHostname = urlParseLax(url).hostname; const oldHostname = urlParseLax(this.currentUrl).hostname; - let newChannel = newHostname.slice(newHostname.indexOf('.') + 1, newHostname.length); - let oldChannel = oldHostname.slice(oldHostname.indexOf('.') + 1, oldHostname.length); + let newChannel = newHostname.slice( + newHostname.indexOf('.') + 1, + newHostname.length, + ); + let oldChannel = oldHostname.slice( + oldHostname.indexOf('.') + 1, + oldHostname.length, + ); if (url.includes('youtube')) { newChannel = 'youtube.com'; } @@ -396,15 +523,22 @@ export default { if (newChannel !== oldChannel) { this.removeListener(); this.$electron.ipcRenderer.send('change-channel', { url }); - } else if (this.currentUrl === url && supportedPage.includes(this.currentUrl)) { + } else if ( + this.currentUrl === url + && supportedPage.includes(this.currentUrl) + ) { this.currentMainBrowserView().webContents.reload(); } else { const homePage = urlParseLax(`https://www.${newChannel}`).href; - this.$electron.ipcRenderer.send('create-browser-view', { url: homePage }); + this.$electron.ipcRenderer.send('create-browser-view', { + url: homePage, + }); } }, addListenerToBrowser() { - const view = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; + const view = this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0]; // eslint-disable-next-line @typescript-eslint/no-explicit-any view.webContents.addListener('ipc-message', this.ipcMessage); view.webContents.addListener('dom-ready', this.domReady); @@ -414,13 +548,21 @@ export default { view.webContents.addListener('will-navigate', this.willNavigate); }, removeListener() { - const currentBrowserViews = this.$electron.remote.getCurrentWindow().getBrowserViews(); + const currentBrowserViews = this.$electron.remote + .getCurrentWindow() + .getBrowserViews(); if (currentBrowserViews.length) { const currentWebContents = currentBrowserViews[0].webContents; - currentWebContents.removeListener('did-stop-loading', this.didStopLoading); + currentWebContents.removeListener( + 'did-stop-loading', + this.didStopLoading, + ); currentWebContents.removeListener('dom-ready', this.domReady); currentWebContents.removeListener('ipc-message', this.ipcMessage); - currentWebContents.removeListener('did-start-loading', this.didStartLoading); + currentWebContents.removeListener( + 'did-start-loading', + this.didStartLoading, + ); currentWebContents.removeListener('new-window', this.newWindow); currentWebContents.removeListener('will-navigate', this.willNavigate); } @@ -433,7 +575,11 @@ export default { willNavigate(e: Event, url: string) { if (!this.startLoading) { this.startLoading = true; - if (!url || url === 'about:blank' || urlParseLax(this.currentUrl).href === urlParseLax(url).href) return; + if ( + !url + || url === 'about:blank' + || urlParseLax(this.currentUrl).href === urlParseLax(url).href + ) return; this.currentUrl = urlParseLax(url).href; this.startTime = new Date().getTime(); this.loadingState = true; @@ -443,9 +589,15 @@ export default { didStartLoading() { if (!this.startLoading) { this.startLoading = true; - const url = this.$electron.remote.getCurrentWindow() - .getBrowserView().webContents.getURL(); - if (!url || url === 'about:blank' || urlParseLax(this.currentUrl).href === urlParseLax(url).href) return; + const url = this.$electron.remote + .getCurrentWindow() + .getBrowserView() + .webContents.getURL(); + if ( + !url + || url === 'about:blank' + || urlParseLax(this.currentUrl).href === urlParseLax(url).href + ) return; this.currentUrl = urlParseLax(url).href; this.startTime = new Date().getTime(); this.loadingState = true; @@ -476,14 +628,24 @@ export default { case 'left-drag': if (this.isPip) { if (args.windowSize) { - this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setBounds', [{ - x: args.x, - y: args.y, - width: args.windowSize[0], - height: args.windowSize[1], - }]); + this.$electron.ipcRenderer.send( + 'callBrowsingWindowMethod', + 'setBounds', + [ + { + x: args.x, + y: args.y, + width: args.windowSize[0], + height: args.windowSize[1], + }, + ], + ); } else { - this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [args.x, args.y]); + this.$electron.ipcRenderer.send( + 'callBrowsingWindowMethod', + 'setPosition', + [args.x, args.y], + ); } } break; @@ -503,7 +665,10 @@ export default { }, domReady() { window.focus(); - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.focus(); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.focus(); }, didStopLoading() { this.startLoading = false; @@ -519,15 +684,24 @@ export default { handleOpenUrl({ url }: { url: string }) { if (!this.startLoading) { this.startLoading = true; - if (!url || url === 'about:blank' || urlParseLax(url).href === urlParseLax(this.currentUrl).href) return; + if ( + !url + || url === 'about:blank' + || urlParseLax(url).href === urlParseLax(this.currentUrl).href + ) return; this.loadingState = true; this.currentUrl = urlParseLax(url).href; const protocol = urlParseLax(url).protocol; - this.$electron.ipcRenderer.send('create-browser-view', { url: protocol ? this.currentUrl : `https:${this.currentUrl}`, isNewWindow: true }); + this.$electron.ipcRenderer.send('create-browser-view', { + url: protocol ? this.currentUrl : `https:${this.currentUrl}`, + isNewWindow: true, + }); } }, pipAdapter() { - const parseUrl = urlParseLax(this.currentMainBrowserView().webContents.getURL()); + const parseUrl = urlParseLax( + this.currentMainBrowserView().webContents.getURL(), + ); if (parseUrl.hostname.includes('youtube')) { this.pipType = 'youtube'; this.youtubeAdapter(); @@ -546,26 +720,47 @@ export default { return this.$electron.remote.getCurrentWindow().getBrowserView(); }, handleWindowChangeEnterPip() { - const newDisplayId = this.$electron.remote.screen - .getDisplayNearestPoint({ x: this.winPos[0], y: this.winPos[1] }).id; + const newDisplayId = this.$electron.remote.screen.getDisplayNearestPoint({ + x: this.winPos[0], + y: this.winPos[1], + }).id; const useDefaultPosition = !this.pipPos.length || (this.oldDisplayId !== newDisplayId && this.oldDisplayId !== -1); this.oldDisplayId = newDisplayId; - this.currentMainBrowserView().webContents - .executeJavaScript(this.getVideoStyle).then((result: CSSStyleDeclaration) => { + this.currentMainBrowserView() + .webContents.executeJavaScript(this.getVideoStyle) + .then((result: CSSStyleDeclaration) => { const videoAspectRatio = parseFloat(result.width as string) / parseFloat(result.height as string); if (useDefaultPosition) { - this.$store.dispatch('updatePipPos', [window.screen.availLeft + 70, - window.screen.availTop + window.screen.availHeight - 236 - 70]) + this.$store + .dispatch('updatePipPos', [ + window.screen.availLeft + 70, + window.screen.availTop + window.screen.availHeight - 236 - 70, + ]) .then(() => { - this.$electron.ipcRenderer.send('callBrowsingWindowMethod', 'setPosition', [window.screen.availLeft + 70, - window.screen.availTop + window.screen.availHeight - 236 - 70]); + this.$electron.ipcRenderer.send( + 'callBrowsingWindowMethod', + 'setPosition', + [ + window.screen.availLeft + 70, + window.screen.availTop + + window.screen.availHeight + - 236 + - 70, + ], + ); }); } const calculateSize = this.pipSize[0] / this.pipSize[1] >= videoAspectRatio - ? [this.pipSize[0], Math.round(this.pipSize[0] / videoAspectRatio)] - : [Math.round(this.pipSize[1] * videoAspectRatio), this.pipSize[1]]; + ? [ + this.pipSize[0], + Math.round(this.pipSize[0] / videoAspectRatio), + ] + : [ + Math.round(this.pipSize[1] * videoAspectRatio), + this.pipSize[1], + ]; this.pipInfo = { aspectRatio: videoAspectRatio, minimumSize: [420, Math.round(420 / videoAspectRatio)], @@ -575,17 +770,25 @@ export default { }); }, handleWindowChangeExitPip() { - const newDisplayId = this.$electron.remote.screen - .getDisplayNearestPoint({ x: this.winPos[0], y: this.winPos[1] }).id; + const newDisplayId = this.$electron.remote.screen.getDisplayNearestPoint({ + x: this.winPos[0], + y: this.winPos[1], + }).id; this.oldDisplayId = newDisplayId; }, handleDanmuDisplay() { if (this.pipType === 'iqiyi') { this.updateBarrageOpen(!this.barrageOpen); - this.$electron.ipcRenderer.send('handle-danmu-display', this.iqiyiBarrage); + this.$electron.ipcRenderer.send( + 'handle-danmu-display', + this.iqiyiBarrage, + ); } else if (this.pipType === 'bilibili') { this.updateBarrageOpen(!this.barrageOpen); - this.$electron.ipcRenderer.send('handle-danmu-display', this.bilibiliBarrage); + this.$electron.ipcRenderer.send( + 'handle-danmu-display', + this.bilibiliBarrage, + ); } }, handleUrlForward() { @@ -603,9 +806,15 @@ export default { handleUrlReload() { if (this.isPip) { this.pipRestore = true; - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.reload(); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.reload(); } else { - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.reload(); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.reload(); } }, enterPipOperation() { @@ -642,21 +851,34 @@ export default { if (this.isPip) { this.exitPipOperation(); this.updateIsPip(false); - const loadUrl = this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.getURL(); + const loadUrl = this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.getURL(); const hostname = urlParseLax(loadUrl).hostname; - let channel = hostname.slice(hostname.indexOf('.') + 1, hostname.length); + let channel = hostname.slice( + hostname.indexOf('.') + 1, + hostname.length, + ); if (loadUrl.includes('youtube')) { channel = 'youtube.com'; } - this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents - .executeJavaScript(this.calculateVideoNum, (r: number) => { - this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id ? false : !!r; - }); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript( + this.calculateVideoNum, + (r: number) => { + this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id + ? false + : !!r; + }, + ); } }, othersAdapter() { - this.currentMainBrowserView().webContents.executeJavaScript(this.othersPip.adapter) + this.currentMainBrowserView() + .webContents.executeJavaScript(this.othersPip.adapter) .then(() => { this.adaptFinished = true; }); @@ -665,12 +887,15 @@ export default { this.$electron.ipcRenderer.send('pip-watcher', this.othersPip.watcher); }, othersRecover() { - this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.executeJavaScript(this.othersPip.recover); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript(this.othersPip.recover); }, iqiyiAdapter() { - this.currentMainBrowserView().webContents - .executeJavaScript(this.iqiyiPip.adapter).then(() => { + this.currentMainBrowserView() + .webContents.executeJavaScript(this.iqiyiPip.adapter) + .then(() => { this.adaptFinished = true; }); }, @@ -678,25 +903,42 @@ export default { this.$electron.ipcRenderer.send('pip-watcher', this.iqiyiPip.watcher); }, iqiyiRecover() { - this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.executeJavaScript(this.iqiyiPip.recover); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript(this.iqiyiPip.recover); }, youtubeAdapter() { - this.currentMainBrowserView().webContents.executeJavaScript(youtube.adapter).then(() => { - this.adaptFinished = true; - }); + this.currentMainBrowserView() + .webContents.executeJavaScript(youtube.adapter) + .then(() => { + this.adaptFinished = true; + }); }, youtubeRecover() { - this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.executeJavaScript(youtube.recover); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript(youtube.recover); }, bilibiliAdapter() { - this.currentMainBrowserView().webContents - .executeJavaScript(bilibiliFindType).then((r: (HTMLElement | null)[]) => { - this.bilibiliType = ['bangumi', 'videoStreaming', 'iframeStreaming', 'iframeStreaming', 'video'][r.findIndex(i => i)] || 'others'; - }).then(() => { - this.currentMainBrowserView().webContents.executeJavaScript(this.bilibiliPip.adapter); - }).then(() => { + this.currentMainBrowserView() + .webContents.executeJavaScript(bilibiliFindType) + .then((r: (HTMLElement | null)[]) => { + this.bilibiliType = [ + 'bangumi', + 'videoStreaming', + 'iframeStreaming', + 'iframeStreaming', + 'video', + ][r.findIndex(i => i)] || 'others'; + }) + .then(() => { + this.currentMainBrowserView().webContents.executeJavaScript( + this.bilibiliPip.adapter, + ); + }) + .then(() => { this.adaptFinished = true; }); }, @@ -704,8 +946,10 @@ export default { this.$electron.ipcRenderer.send('pip-watcher', this.bilibiliPip.watcher); }, bilibiliRecover() { - this.$electron.remote.getCurrentWindow() - .getBrowserViews()[0].webContents.executeJavaScript(this.bilibiliPip.recover); + this.$electron.remote + .getCurrentWindow() + .getBrowserViews()[0] + .webContents.executeJavaScript(this.bilibiliPip.recover); }, }, }; @@ -713,6 +957,7 @@ export default { diff --git a/src/renderer/components/BrowsingView/BrowsingControl.vue b/src/renderer/components/BrowsingView/BrowsingControl.vue index 39aa5335fe..23b6bbf646 100644 --- a/src/renderer/components/BrowsingView/BrowsingControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingControl.vue @@ -6,9 +6,8 @@ class="browsing-control" > @@ -74,6 +73,8 @@ export default { return process.platform === 'darwin'; }, }, + methods: { + }, }; diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index ad108b4f71..24d89e1f49 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -1,6 +1,5 @@ @@ -31,22 +24,22 @@ From 0813641c35b859589d48d253ae55b635e1cae7f5 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Wed, 4 Sep 2019 17:16:01 +0800 Subject: [PATCH 051/123] feat(browsing): refresh --- src/renderer/components/BrowsingView/BrowsingControl.vue | 4 ---- src/renderer/components/BrowsingView/BrowsingHeader.vue | 2 +- src/renderer/components/BrowsingView/BrowsingInput.vue | 4 ++++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderer/components/BrowsingView/BrowsingControl.vue b/src/renderer/components/BrowsingView/BrowsingControl.vue index 5c027c1e6a..8dd82edc1c 100644 --- a/src/renderer/components/BrowsingView/BrowsingControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingControl.vue @@ -49,10 +49,6 @@ export default { Icon, }, props: { - handleUrlReload: { - type: Function, - required: true, - }, handleUrlBack: { type: Function, required: true, diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index 24d89e1f49..61e7872299 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -3,7 +3,6 @@ class="header" > diff --git a/src/renderer/components/BrowsingView/BrowsingInput.vue b/src/renderer/components/BrowsingView/BrowsingInput.vue index 322617f847..2c92f5f82c 100644 --- a/src/renderer/components/BrowsingView/BrowsingInput.vue +++ b/src/renderer/components/BrowsingView/BrowsingInput.vue @@ -27,6 +27,10 @@ export default { Icon, }, props: { + handleUrlReload: { + type: Function, + required: true, + }, closeUrlInput: { type: Function, required: true, From 07d43b35baa788a50208180cc2b5723090ab321a Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 17:33:49 +0800 Subject: [PATCH 052/123] chore(splayerx): update package.json and typings.d.ts Update ass-compiler and @chiflix/electron. Add typings for new subtitle interface. --- package.json | 4 ++-- src/renderer/typings.d.ts | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index dc7b31aa02..7661f4409c 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@types/fs-extra": "^7.0.0", "@types/lodash": "^4.14.134", "@types/lolex": "^3.1.1", - "ass-compiler": "0.0.10", + "ass-compiler": "github:YvonTre/ass-compiler", "axios": "^0.19.0", "chardet": "^0.7.0", "configcat-js": "^1.1.19", @@ -112,7 +112,7 @@ "@babel/plugin-syntax-import-meta": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/register": "^7.0.0", - "@chiflix/electron": "5.0.10", + "@chiflix/electron": "5.0.7-dev.1", "@sentry/webpack-plugin": "^1.6.2", "@types/chai": "^4.1.7", "@types/mkdirp": "^0.5.2", diff --git a/src/renderer/typings.d.ts b/src/renderer/typings.d.ts index 9fb12973ac..3fc082e174 100644 --- a/src/renderer/typings.d.ts +++ b/src/renderer/typings.d.ts @@ -95,19 +95,29 @@ declare module 'electron' { err: string, ) => void, ): void; - /** extract one subtitle from a video file given stream index and format */ + /** extract subtitle fragment from a video file (@cliflix/electron@4.0.7-dev.1) */ extractSubtitles( /** source video path to extract subtitle from */ srcVideoPath: string, - /** subtitle path to save */ - subtitlePath: string, - /** stream index string in the form of 0:s:0 */ - streamIndex?: string, + /** subtitle stream index to extract, if -1 will auto select the default track */ + streamIndex: number, + /** position of the last subtitle extracted or seconds of the video */ + posOrSeconds: number, + /** whether posOrSeconds param is seconds of the video */ + isSeconds: boolean, + /** subtitle dialogues count */ + dialogueCount: number, callback: ( - /** if not '0', an error occurred */ - err: string, + /** if not '', an error occurred */ + error: string, + /** video position of the last extracted dialogue */ + position: number, + /** buffer of the extracted dialogues */ + data: Buffer | null, ) => void, ): void; + /** stop the subtitle extraction */ + stopExtractSubtitles(): void; /** generate thumbnails in the order of left-to-right and top-to-bottom */ generateThumbnails( /** source video path to extract thumbnails from */ From 80f80711f7c619008533d70da3b388690b1ff30b Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 17:38:43 +0800 Subject: [PATCH 053/123] refactor(subtitle): subtitle interfaces tweak Refactor subtitle interfaces into more consistent ones. 1. Remove duplicated properties from Entity. 2. Add ILoader and tweak IParser to adjust to new stream subtitles. 3. Add new ITimeSegments interfaces. --- src/renderer/interfaces/ISubtitle.ts | 110 +++++++++++++-------------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/src/renderer/interfaces/ISubtitle.ts b/src/renderer/interfaces/ISubtitle.ts index 2419b31b6c..14c791c13c 100644 --- a/src/renderer/interfaces/ISubtitle.ts +++ b/src/renderer/interfaces/ISubtitle.ts @@ -21,50 +21,66 @@ export interface IOrigin { type: Type; source: unknown; } -export type Entity = { - source: IOrigin; - type: Type; +export interface IEntity { + displaySource: IOrigin; + realSource: IOrigin; + hash: string; format: Format; language: LanguageCode; - payload: unknown; - hash: string; - metadata: IMetadata; delay: number; } -export const defaultEntity: Entity = { - source: { - type: Type.Local, - source: '', - }, - type: Type.Local, - format: Format.Unknown, - language: LanguageCode.Default, - payload: '', - hash: '', - metadata: {}, - delay: 0, -}; -export type SubtitleControlListItem = { +export interface IEntityGenerator { + getDisplaySource(): Promise; + getRealSource(): Promise; + getHash(): Promise + getFormat(): Promise + getLanguage(): Promise + getDelay(): Promise + getVideoSegments?: () => Promise + getAutoUploaded?: () => Promise +} +export interface ISubtitleControlListItem { id: string; hash: string; type: Type; language: LanguageCode; - source: unknown; + source: IOrigin; name?: string; - delay: number; -}; - -export interface IEntityGenerator { - /** get real source to fetch subtitle from */ - getSource(): Promise - /** get fake source for display use */ - getStoredSource?: () => Promise - getDelay?: () => Promise - getType(): Promise - getFormat(): Promise - getLanguage(): Promise - getPayload(): Promise - getHash(): Promise +} +export interface ILoader { + readonly source: IOrigin; + readonly canPreload: boolean; + readonly canCache: boolean; + readonly canUpload: boolean; + readonly fullyRead: boolean; + getPayload(time?: number): Promise; + pause(): void | Promise; + cache(): Promise; + on(event: 'cache' | 'read' | 'upload', callback: (result: boolean) => void): void; + once(event: 'cache' | 'read' | 'upload', callback: (result: boolean) => void): void; + destroy(): Promise +} +export interface IParser { + readonly format: Format; + readonly loader: ILoader; + readonly videoSegments: IVideoSegments; + getMetadata(): Promise; + getDialogues(time?: number): Promise; +} +export interface ITimeSegments { + insert(start: number, end: number): void; + check(time: number): boolean; +} +export interface IVideoSegments extends ITimeSegments { + updatePlayed(timeStamp: number, lastTimeStamp?: number): void; + playedTime: number; + export(): IRawVideoSegment[]; + restore(videoSegments: IRawVideoSegment[]): void; +} +export interface IRawVideoSegment { + start: number; + end: number; + played: boolean; } export interface IMetadata { @@ -99,30 +115,6 @@ export type Cue = { format: string, tags: ITags, } -export interface IDialogue { - start: number; - end: number; - text?: string; - tags?: ITags; - fragments?: { - text: string; - tags: ITags; - }[]; -} -export interface IVideoSegment { - start: number; - end: number; - played: boolean; -} - -export interface IParser { - parse(): void; - readonly payload: unknown; - getMetadata(): Promise; - getDialogues(time?: number): Promise; - getVideoSegments(duration: number): Promise; - updateVideoSegments(lastTime: number, currentTime: number): number; -} export type Subtitle = { id: string From abd002a5c5cb69cffd14be01cba383f88066b016 Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 17:45:48 +0800 Subject: [PATCH 054/123] feat(lib): implement time segments and add tests Implement Stream and Subtitle TimeSegments. --- src/renderer/libs/TimeSegments.ts | 148 +++++++++++++++++ test/unit/specs/libs/TimeSegments.spec.js | 194 ++++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 src/renderer/libs/TimeSegments.ts create mode 100644 test/unit/specs/libs/TimeSegments.spec.js diff --git a/src/renderer/libs/TimeSegments.ts b/src/renderer/libs/TimeSegments.ts new file mode 100644 index 0000000000..3dfd0f7f15 --- /dev/null +++ b/src/renderer/libs/TimeSegments.ts @@ -0,0 +1,148 @@ +import { ITimeSegments, IVideoSegments } from '@/interfaces/ISubtitle'; + +export class StreamTimeSegments implements ITimeSegments { + private startTimestamps: number[] = []; + + private endTimestamps: number[] = []; + + private isValidNumber(num: number) { + return typeof num === 'number' && Number.isFinite(num) && !Number.isNaN(num) && num >= 0; + } + + private checkWithIndex(time: number) { + const index = this.startTimestamps.findIndex((timestamp, index, arr) => ( + timestamp <= time && (!arr[index + 1] || arr[index + 1] >= time) + )); + return { + in: index !== -1 && this.endTimestamps[index] >= time, + index, + }; + } + + public check(time: number) { return this.checkWithIndex(time).in; } + + public insert(start: number, end: number) { + if (this.isValidNumber(start) && this.isValidNumber(end) && start !== end) { + const [startTime, endTime] = [start, end].sort(); + const { in: startIn, index: startIndex } = this.checkWithIndex(startTime); + const { in: endIn, index: endIndex } = this.checkWithIndex(endTime); + + if (!startIn && !endIn && startIndex === endIndex) { + this.startTimestamps.splice(startIndex + 1, 0, startTime); + this.endTimestamps.splice(endIndex + 1, 0, endTime); + } else { + if (!startIn) this.startTimestamps[startIndex + 1] = startTime; + this.endTimestamps[startIn ? startIndex : endIndex] = endIn + ? this.endTimestamps[endIndex] : end; + + const deleteIndex = startIndex === endIndex ? 0 : startIndex + 1; + const deleteCount = endIndex - startIndex - (startIn ? 0 : 1); + this.startTimestamps.splice(deleteIndex, deleteCount); + this.endTimestamps.splice(deleteIndex, deleteCount); + } + } + } + + public bulkInsert(segments: [number, number][], time: number) { + const allTimeStamps = segments.flatMap(nums => nums) + .concat([time]) + .filter(num => this.isValidNumber(num)); + this.insert(Math.min(...allTimeStamps), Math.max(...allTimeStamps)); + } +} + +export class VideoTimeSegments implements IVideoSegments { + private duration: number = 0; + + private timestamps: number[] = []; + + private played: boolean[] = []; + + public constructor(duration: number) { + if (!this.isValidNumber(duration)) throw new Error('Duration should be a valid number.'); + this.duration = duration; + this.timestamps.push(0, this.duration); + this.played.push(false); + } + + private isValidNumber(num: number) { + return typeof num === 'number' && Number.isFinite(num) && !Number.isNaN(num) && num > 0 + && (!this.duration || (this.duration && num <= this.duration)); + } + + public insert(start: number, end: number) { + if (this.isValidNumber(start) && this.isValidNumber(end)) { + start = Math.min(start, end); + const result = this.checkWithIndex(start); + if (result.in && this.timestamps[result.index] !== start) { + this.timestamps.splice(result.index + 1, 0, start); + this.played.splice(result.index + 1, 0, false); + } + } + } + + private checkWithIndex(time: number) { + const index = this.timestamps.findIndex((timestamp, index, arr) => ( + timestamp <= time && (!arr[index + 1] || arr[index + 1] > time) + )); + const lastTimeStamp = this.timestamps[this.timestamps.length - 1]; + return { + in: index !== -1 && lastTimeStamp >= time, + index, + }; + } + + public check(time: number) { return this.checkWithIndex(time).in; } + + private lastIndex: number = 0; + + private lastPlayedTime: number = 0; + + public updatePlayed(timeStamp: number, lastTimeStamp: number = 0) { + if (this.isValidNumber(timeStamp) && this.isValidNumber(lastTimeStamp)) { + [timeStamp, lastTimeStamp] = [ + Math.max(timeStamp, lastTimeStamp), + Math.min(timeStamp, lastTimeStamp), + ]; + const { in: currentIn, index: currentIndex } = this.checkWithIndex(timeStamp); + const { in: lastIn, index: lastIndex } = this.checkWithIndex(lastTimeStamp); + if (currentIn && lastIn && !this.played[currentIndex]) { + let playedTime = timeStamp - lastTimeStamp; + if (lastIndex !== currentIndex) playedTime = timeStamp - this.timestamps[currentIndex]; + if (this.lastIndex !== currentIndex) { + this.lastIndex = currentIndex; + this.lastPlayedTime = playedTime; + } else this.lastPlayedTime += playedTime; + } + if (this.lastIndex !== -1) { + const segmentTime = this.timestamps[this.lastIndex + 1] - this.timestamps[this.lastIndex]; + if (this.lastPlayedTime / segmentTime >= 0.9) this.played[this.lastIndex] = true; + } + } + } + + public get playedTime() { + return this.played + .map((played, index) => ({ played, index })) + .filter(({ played }) => played) + .reduce((playedTime, { index }) => { + playedTime += this.timestamps[index + 1] - this.timestamps[index]; + return playedTime; + }, 0); + } + + public export() { + return this.played + .map((played, index) => ({ + start: this.timestamps[index], + end: this.timestamps[index + 1], + played, + })); + } + + public restore(timeSegments: { start: number, end: number, played: boolean }[]) { + this.timestamps = timeSegments.map(({ start }) => start); + this.timestamps.push(timeSegments[timeSegments.length - 1].end); + this.played = timeSegments.map(({ played }) => played); + } +} diff --git a/test/unit/specs/libs/TimeSegments.spec.js b/test/unit/specs/libs/TimeSegments.spec.js new file mode 100644 index 0000000000..b456f69db2 --- /dev/null +++ b/test/unit/specs/libs/TimeSegments.spec.js @@ -0,0 +1,194 @@ +import { StreamTimeSegments, VideoTimeSegments } from '@/libs/TimeSegments'; + +describe('StreamTimeSegments', () => { + let timeSeg = new StreamTimeSegments(); + beforeEach(() => { + timeSeg = new StreamTimeSegments(); + timeSeg.startTimestamps = [1, 3, 5]; + timeSeg.endTimestamps = [2, 4, 6]; + }); + + describe('checkWithIndex', () => { + it('should return false and -1 when time is lower than the minimum', () => { + const result = timeSeg.checkWithIndex(0.5); + + expect(result).to.deep.equal({ in: false, index: -1 }); + }); + + it('should return true and index when time in range', () => { + const result = timeSeg.checkWithIndex(1.5); + + expect(result).to.deep.equal({ in: true, index: 0 }); + }); + + it('should return false and index when time is not in range', () => { + const result = timeSeg.checkWithIndex(4.5); + + expect(result).to.deep.equal({ in: false, index: 1 }); + }); + }); + + describe('insert', () => { + it('start in range, end in range, start index and end index are equal', () => { + timeSeg.insert(1.25, 1.75); + + expect(timeSeg.startTimestamps).to.deep.equal([1, 3, 5]); + expect(timeSeg.endTimestamps).to.deep.equal([2, 4, 6]); + }); + it('start in range, end not in range, start index and end index are equal', () => { + timeSeg.insert(1.5, 2.5); + + expect(timeSeg.startTimestamps).to.deep.equal([1, 3, 5]); + expect(timeSeg.endTimestamps).to.deep.equal([2.5, 4, 6]); + }); + it('should ignore two same numbers', () => { + timeSeg.insert(1, 1); + + expect(timeSeg.startTimestamps).to.deep.equal([1, 3, 5]); + expect(timeSeg.endTimestamps).to.deep.equal([2, 4, 6]); + }); + }); +}); +describe('VideoTimeSegments', () => { + let timeSeg = new VideoTimeSegments(10); + beforeEach(() => { + timeSeg = new VideoTimeSegments(10); + timeSeg.timestamps = [0, 2, 4, 6, 8, 10]; + timeSeg.played = [false, false, false, false, false]; + }); + + describe('checkWithIndex', () => { + it('should return false and -1 when time is lower than the minimum', () => { + const result = timeSeg.checkWithIndex(-1); + + expect(result).to.deep.equal({ in: false, index: -1 }); + }); + + it('should return true and index when time in range', () => { + const result = timeSeg.checkWithIndex(4); + + expect(result).to.deep.equal({ in: true, index: 2 }); + }); + + it('should return false and index when time is larger than the maximum', () => { + const result = timeSeg.checkWithIndex(11); + + expect(result).to.deep.equal({ in: false, index: 5 }); + }); + }); + + describe('insert', () => { + it('should insert a new entry in timestamps', () => { + timeSeg.insert(5, 5.5); + + expect(timeSeg.timestamps).to.deep.equal([0, 2, 4, 5, 6, 8, 10]); + }); + it('should insert the minimum as a new entry', () => { + timeSeg.insert(5.5, 5); + + expect(timeSeg.timestamps).to.deep.equal([0, 2, 4, 5, 6, 8, 10]); + }); + it('should not insert an existed entry', () => { + timeSeg.insert(4, 7); + + expect(timeSeg.timestamps).to.deep.equal([0, 2, 4, 6, 8, 10]); + }); + it('should also insert a false entry in played', () => { + timeSeg.insert(5, 5.5); + + expect(timeSeg.played).to.deep.equal([false, false, false, false, false, false]); + }); + it('should keep previous segment played status when break entries', () => { + timeSeg.played = [false, false, true, false, false]; + timeSeg.insert(5, 5.5); + + expect(timeSeg.played).to.deep.equal([false, false, true, false, false, false]); + }); + }); + + describe('updatePlayed', () => { + describe('update last index and last played time', () => { + describe('when current and last timestamp are in the same time segment', () => { + it('should update last index to the segment index', () => { + timeSeg.updatePlayed(5, 4); + + expect(timeSeg.lastIndex).to.equal(2); + }); + it('should update last played time += the contraction of two timestamps', () => { + timeSeg.updatePlayed(5, 4); + + expect(timeSeg.lastPlayedTime).to.equal(1); + }); + }); + describe('when current and last timestamp are not in the same time segment', () => { + it('should update last index to the timestamp segment index', () => { + timeSeg.updatePlayed(7.5, 4); + + expect(timeSeg.lastIndex).to.equal(3); + }); + it('should update last played += the contraction of the timestamp and the segment start', () => { + timeSeg.updatePlayed(7.5, 4); + + expect(timeSeg.lastPlayedTime).to.equal(1.5); + }); + }); + }); + + it('should not update played when time played not reaching 90% of the segment time', () => { + timeSeg.updatePlayed(5, 4); + + expect(timeSeg.played).to.deep.equal([false, false, false, false, false]); + }); + + it('should update played to true when time played reaching 90% of the segment time', () => { + timeSeg.updatePlayed(5.9, 4); + + expect(timeSeg.played).to.deep.equal([false, false, true, false, false]); + }); + }); + + it('should played time return proper played time', () => { + timeSeg.played = [true, false, true, false, true]; + + expect(timeSeg.playedTime).to.deep.equal(6); + }); + + it('should export return raw time segments', () => { + timeSeg.played[2] = true; + const result = timeSeg.export(); + const expectedResult = [ + { start: 0, end: 2, played: false }, + { start: 2, end: 4, played: false }, + { start: 4, end: 6, played: true }, + { start: 6, end: 8, played: false }, + { start: 8, end: 10, played: false }, + ]; + + expect(result).to.deep.equal(expectedResult); + }); + + describe('restore', () => { + const rawTimeSegments = [ + { start: 0, end: 2, played: false }, + { start: 2, end: 4, played: false }, + { start: 4, end: 6, played: true }, + { start: 6, end: 8, played: false }, + { start: 8, end: 10, played: true }, + { start: 10, end: 12, played: true }, + ]; + it('should update time segments', () => { + timeSeg = new VideoTimeSegments(12); + timeSeg.restore(rawTimeSegments); + const expectedResult = [0, 2, 4, 6, 8, 10, 12]; + + expect(timeSeg.timestamps).to.deep.equal(expectedResult); + }); + it('should update played', () => { + timeSeg = new VideoTimeSegments(12); + timeSeg.restore(rawTimeSegments); + const expectedResult = [false, false, true, false, true, true]; + + expect(timeSeg.played).to.deep.equal(expectedResult); + }); + }); +}); From 41a49978ec78b13d306f40d5848a774f5df10970 Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 17:51:33 +0800 Subject: [PATCH 055/123] feat(mediatask): implement task cancel ability 1. Cancel task using taskQueue.cancelTask(id). 2. MediaTask can provide a cancel method to cancel gracefully. 3. Task will be cancelled right away, regardless of its original piority --- .../plugins/mediaTasks/baseMediaTaskQueue.ts | 59 ++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/renderer/plugins/mediaTasks/baseMediaTaskQueue.ts b/src/renderer/plugins/mediaTasks/baseMediaTaskQueue.ts index d2b0bb2201..986b21adaa 100644 --- a/src/renderer/plugins/mediaTasks/baseMediaTaskQueue.ts +++ b/src/renderer/plugins/mediaTasks/baseMediaTaskQueue.ts @@ -1,13 +1,16 @@ import { log } from '@/libs/Log'; -export interface IMediaTask { +export interface IMediaTask { getId(): string; - execute(): Promise; + execute(): ResultType | Promise; + cancel?: () => (CancelType | Promise); } type TaskInfo = { id: string; run: () => Promise; + cancel: () => Promise; piority: number; + needCancel: boolean; } interface IAddTaskOptions { piority?: number; @@ -54,18 +57,59 @@ export default class BaseMediaTaskQueue { this.processTasks(); } }; - this.enqueue(run, id, piority || 0); + const cancel = async (): Promise => { + try { + const result = task.cancel ? await Promise.race([ + new Promise((resolve, reject) => setTimeout( + () => reject(new Error(`Timeout: ${task.constructor.name}`)), + timeout || defaultOptions.timeout, + )), + task.cancel(), + ]) : () => {}; + resolve(result as T); + } catch (error) { + reject(error); + } finally { + this.executing = false; + this.processTasks(); + } + }; + this.enqueue(run, cancel, id, piority || 0); if (!this.executing) this.processTasks(); }); } - private enqueue(run: () => Promise, id: string, piority: number) { + public cancelTask(id: string) { + const taskIndex = this.pendingTasks.findIndex(task => task.id === id); + if (taskIndex !== -1) { + const task = this.pendingTasks.splice(taskIndex, 1)[0]; + task.needCancel = true; + this.pendingTasks.unshift(task); + } + } + + private enqueue( + run: () => Promise, cancel: () => Promise, + id: string, piority: number, + ) { if (!this.pendingTasks.length || this.pendingTasks[this.pendingTasks.length - 1].piority >= piority) { - this.pendingTasks.push({ id, run, piority }); + this.pendingTasks.push({ + id, + run, + cancel, + piority, + needCancel: false, + }); } else { const index = this.pendingTasks.findIndex(task => task.piority < piority); - this.pendingTasks.splice(index, 0, { id, run, piority }); + this.pendingTasks.splice(index, 0, { + id, + run, + cancel, + piority, + needCancel: false, + }); } } @@ -75,7 +119,8 @@ export default class BaseMediaTaskQueue { if (task) { this.executing = true; try { - task.run(); + if (task.needCancel) task.cancel(); + else task.run(); } catch (ex) { log.error('BaseMediaTask', ex); } From e424bb45b3a1a7ec470da2b57ae682d8b83f6352 Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 18:01:34 +0800 Subject: [PATCH 056/123] refactor(mediatask): refactor subtitle tasks Seperate subtitle tasks to four different tasks for streaming support. 1. Add subtitle-metadata, -cache, -stream and -destroy tasks. 2. Seperate subtitle queue from snapshot queue. 3. Rename subtitle tasks in mediaTasks. --- src/main/helpers/mediaTasksPlugin.ts | 119 +++++++++++--- src/renderer/plugins/mediaTasks/index.ts | 43 ++++- .../mediaTasks/snapshotSubtitleQueue.ts | 64 +------- .../plugins/mediaTasks/subtitleQueue.ts | 152 ++++++++++++++++++ src/renderer/typings.d.ts | 45 ++++-- 5 files changed, 326 insertions(+), 97 deletions(-) create mode 100644 src/renderer/plugins/mediaTasks/subtitleQueue.ts diff --git a/src/main/helpers/mediaTasksPlugin.ts b/src/main/helpers/mediaTasksPlugin.ts index aa0c703f4e..acf4ec2ed3 100644 --- a/src/main/helpers/mediaTasksPlugin.ts +++ b/src/main/helpers/mediaTasksPlugin.ts @@ -61,25 +61,108 @@ export default function registerMediaTasks() { reply(event, 'snapshot-reply', 'File does not exist.'); } }); - ipcMain.on('subtitle-request', (event, videoPath, subtitlePath, streamIndex) => { - if (existsSync(subtitlePath)) { - reply(event, 'subtitle-reply', null, subtitlePath); - } else if (existsSync(videoPath)) { - splayerxProxy.extractSubtitles( - videoPath, subtitlePath, - streamIndex, - (err) => { - if (err === '0' && existsSync(subtitlePath)) { - reply(event, 'subtitle-reply', null, subtitlePath); - } else { - if (typeof err !== 'string') err = `${err}, type: ${typeof err}`; - reply(event, 'subtitle-reply', `subtitle-reply: ${err}`); - } - }, - ); - } else { - reply(event, 'subtitle-reply', 'File does not exist.'); + let lastVideoPath = ''; + let lastStreamIndex = -1; + const videoSubtitlesMap: Map> = new Map(); + ipcMain.on('subtitle-metadata-request', async (event: Event, videoPath: string, streamIndex: number, subtitlePath: string) => { + if ((lastVideoPath || lastStreamIndex !== -1) + && (lastVideoPath !== videoPath || lastStreamIndex !== streamIndex)) { + await splayerxProxy.stopExtractSubtitles(); + } + lastVideoPath = videoPath; + lastStreamIndex = streamIndex; + if (!videoSubtitlesMap.has(videoPath)) videoSubtitlesMap.set(videoPath, new Map()); + const streamSubtitlesMap = videoSubtitlesMap.get(videoPath); + if (streamSubtitlesMap) { + const subtitle = streamSubtitlesMap.get(streamIndex) || { + path: subtitlePath, + metadata: '', + payload: '', + position: 0, + finished: false, + lastLines: [], + }; + if (existsSync(subtitlePath) || subtitle.finished) { + subtitle.finished = true; + reply(event, 'subtitle-metadata-reply', undefined, subtitle.finished); + } else if (!subtitle.metadata) { + splayerxProxy.extractSubtitles(videoPath, streamIndex, 0, false, 1, + (error, pos, data) => { + if (error || !data) reply(event, 'subtitle-metadata-reply', new Error(error)); + else { + subtitle.position = pos; + subtitle.payload = subtitle.metadata = data.toString('utf8') + .replace(/\n(Dialogue|Comment)[\s\S]*/g, '') + .split(/\r?\n/) + .join('\n'); + } + reply(event, 'subtitle-metadata-reply', undefined, subtitle.finished, subtitle.metadata); + }); + } else reply(event, 'subtitle-metadata-reply', undefined, subtitle.finished, subtitle.metadata); + streamSubtitlesMap.set(streamIndex, subtitle); + } + }); + ipcMain.on('subtitle-cache-request', async (event: Event, videoPath: string, streamIndex: number) => { + if ((lastVideoPath || lastStreamIndex !== -1) + && (lastVideoPath !== videoPath || lastStreamIndex !== streamIndex)) { + await splayerxProxy.stopExtractSubtitles(); + } + lastVideoPath = videoPath; + lastStreamIndex = streamIndex; + const streamSubtitlesMap = videoSubtitlesMap.get(videoPath); + if (streamSubtitlesMap) { + const subtitle = streamSubtitlesMap.get(streamIndex); + if (subtitle) { + splayerxProxy.extractSubtitles(videoPath, streamIndex, subtitle.position, false, 20, + (error, pos, data) => { + if (pos) subtitle.position = pos; + if (data) { + const newLines = data.toString('utf8').split(/\r?\n/); + const finalPayload = newLines.filter(line => !subtitle.lastLines.includes(line)).join('\n'); + subtitle.lastLines = newLines; + subtitle.payload += `\n${finalPayload}`; + } + if (error === 'EOF') { + subtitle.finished = true; + reply(event, 'subtitle-cache-reply', undefined, subtitle.finished, subtitle.payload); + } else if (error) reply(event, 'subtitle-cache-reply', new Error(error)); + else reply(event, 'subtitle-cache-reply', undefined, subtitle.finished); + }); + streamSubtitlesMap.set(streamIndex, subtitle); + } else reply(event, 'subtitle-cache-reply', new Error('Missing subtitle entry, should request metadata first.')); + } else reply(event, 'subtitle-cache-reply', new Error('Missing videoPath entry, should request metadata first.')); + }); + ipcMain.on('subtitle-stream-request', (event: Event, videoPath: string, streamIndex: number, time: number) => { + const streamSubtitlesMap = videoSubtitlesMap.get(videoPath); + if (streamSubtitlesMap) { + const subtitle = streamSubtitlesMap.get(streamIndex); + if (subtitle) { + splayerxProxy.extractSubtitles(videoPath, streamIndex, time, true, 20, + (error, pos, data) => { + if (data) reply(event, 'subtitle-stream-reply', undefined, data.toString('utf8')); + else reply(event, 'subtitle-stream-reply', new Error(!error || !data ? 'Missing data' : error)); + }); + } else reply(event, 'subtitle-stream-reply', new Error('Missing subtitle entry, should request metadata first.')); + } else reply(event, 'subtitle-stream-reply', new Error('Missing videoPath entry, should request metadata first.')); + }); + ipcMain.on('subtitle-destroy-request', async (event: Event, videoPath: string, streamIndex: number) => { + const streamSubtitlesMap = videoSubtitlesMap.get(videoPath); + if (streamSubtitlesMap) { + const subtitle = streamSubtitlesMap.get(streamIndex); + if (subtitle) { + await splayerxProxy.stopExtractSubtitles(); + lastVideoPath = ''; + lastStreamIndex = -1; + } } + reply(event, 'subtitle-destroy-reply'); }); ipcMain.on('thumbnail-request', (event, videoPath, imagePath, interval, diff --git a/src/renderer/plugins/mediaTasks/index.ts b/src/renderer/plugins/mediaTasks/index.ts index 7c192e53e6..60835fd531 100644 --- a/src/renderer/plugins/mediaTasks/index.ts +++ b/src/renderer/plugins/mediaTasks/index.ts @@ -1,11 +1,12 @@ import MediaInfoQueue, { CodecType, ISubtitleStream } from './mediaInfoQueue'; -import SnapshotSubtitleQueue from './snapshotSubtitleQueue'; +import SnapshotQueue from './snapshotSubtitleQueue'; +import SubtitleQueue from './subtitleQueue'; import ThumbnailQueue from './thumbnailQueue'; -import { Format } from '@/interfaces/ISubtitle'; import { log } from '@/libs/Log'; const mediaInfoQueue = new MediaInfoQueue(); -const snapshotSubtitleQueue = new SnapshotSubtitleQueue(); +const snapshotQueue = new SnapshotQueue(); +const subtitleQueue = new SubtitleQueue(); const thumbnailQueue = new ThumbnailQueue(); export async function getMediaInfo(path: string) { @@ -36,7 +37,7 @@ export async function getSnapshotPath( width: number = 1920, height: number = 1080, ) { try { - return snapshotSubtitleQueue.getSnapshotPath( + return snapshotQueue.getSnapshotPath( videoPath, timeInSeconds, width, height, @@ -46,14 +47,42 @@ export async function getSnapshotPath( return ''; } } -export async function getSubtitlePath(videoPath: string, streamIndex: number, format: Format) { + +export async function getSubtitleMetadata(videoPath: string, streamIndex: number) { try { - return snapshotSubtitleQueue.getSubtitlePath(videoPath, streamIndex, format); + return subtitleQueue.getSubtitleMetadata(videoPath, streamIndex); } catch (error) { - log.error('[MediaTask|Subtitle]', error); + log.error('[MediaTask|SubtitleMetadata]', error); return ''; } } +export async function cacheSubtitle(videoPath: string, streamIndex: number) { + try { + return subtitleQueue.cacheSubtitle(videoPath, streamIndex); + } catch (error) { + log.error('[MediaTask|SubtitleCache]', error); + return ''; + } +} +export async function getSubtitleFragment( + videoPath: string, + streamIndex: number, + videoTime: number, +) { + try { + return subtitleQueue.getSubtitleFragment(videoPath, streamIndex, videoTime); + } catch (error) { + log.error('[MediaTask|SubtitleFragment]', error); + return ''; + } +} +export async function finishSubtitleExtraction(videoPath: string, streamIndex: number) { + try { + subtitleQueue.stopSubtitleExtraction(videoPath, streamIndex); + } catch (error) { + log.error('[MediaTask|SubtitleFragment]', error); + } +} /** * 获取进度条缩略图路径 diff --git a/src/renderer/plugins/mediaTasks/snapshotSubtitleQueue.ts b/src/renderer/plugins/mediaTasks/snapshotSubtitleQueue.ts index 52f9b3edd5..c46d95881a 100644 --- a/src/renderer/plugins/mediaTasks/snapshotSubtitleQueue.ts +++ b/src/renderer/plugins/mediaTasks/snapshotSubtitleQueue.ts @@ -1,13 +1,7 @@ -// @ts-ignore import { ipcRenderer } from 'electron'; import { join } from 'path'; import BaseMediaTaskQueue, { IMediaTask } from './baseMediaTaskQueue'; -import { - timecodeFromSeconds, mediaQuickHash, - getVideoDir, getSubtitleDir, -} from '@/libs/utils'; -import { Format } from '@/interfaces/ISubtitle'; -import { formatToExtension } from '@/services/subtitle/utils'; +import { timecodeFromSeconds, mediaQuickHash, getVideoDir } from '@/libs/utils'; class SnapshotTask implements IMediaTask { private readonly videoPath: string; @@ -67,55 +61,7 @@ class SnapshotTask implements IMediaTask { }); } } - -class SubtitleTask implements IMediaTask { - private readonly videoPath: string; - - private readonly videoHash: string; - - private readonly subtitlePath: string; - - private readonly streamIndex: number; - - public constructor( - videoPath: string, videoHash: string, subtitlePath: string, - streamIndex: number, - ) { - this.videoPath = videoPath; - this.videoHash = videoHash; - this.subtitlePath = subtitlePath; - this.streamIndex = streamIndex; - } - - public static async from(videoPath: string, streamIndex: number, format: Format) { - const videoHash = await mediaQuickHash.try(videoPath); - if (videoHash) { - const dirPath = await getSubtitleDir(); - const subtitlePath = join(dirPath, `${[videoHash, streamIndex].join('-')}.${formatToExtension(format)}`); - return new SubtitleTask( - videoPath, videoHash, subtitlePath, - streamIndex, - ); - } - return undefined; - } - - public getId() { return `${[this.videoHash, this.streamIndex]}`; } - - public execute(): Promise { - return new Promise((resolve, reject) => { - ipcRenderer.send('subtitle-request', - this.videoPath, this.subtitlePath, - `0:${this.streamIndex}:0`); - ipcRenderer.once('subtitle-reply', (event: Event, error: string | null, path: string) => { - if (error) reject(new Error(error)); - else resolve(path); - }); - }); - } -} - -export default class SnapshotSubtitleQueue extends BaseMediaTaskQueue { +export default class SnapshotQueue extends BaseMediaTaskQueue { /** get snapshot path, generate it if not exist */ public async getSnapshotPath( videoPath: string, @@ -129,10 +75,4 @@ export default class SnapshotSubtitleQueue extends BaseMediaTaskQueue { ); return task ? super.addTask(task) : ''; } - - /** get a embedded subtitle path, extract it if not exist */ - public async getSubtitlePath(videoPath: string, streamIndex: number, format: Format) { - const task = await SubtitleTask.from(videoPath, streamIndex, format); - return task ? super.addTask(task) : ''; - } } diff --git a/src/renderer/plugins/mediaTasks/subtitleQueue.ts b/src/renderer/plugins/mediaTasks/subtitleQueue.ts new file mode 100644 index 0000000000..bfbd5e5561 --- /dev/null +++ b/src/renderer/plugins/mediaTasks/subtitleQueue.ts @@ -0,0 +1,152 @@ +import { join } from 'path'; +import { ipcRenderer } from 'electron'; +import BaseMediaTaskQueue, { IMediaTask } from './baseMediaTaskQueue'; +import { mediaQuickHash, getSubtitleDir } from '@/libs/utils'; +import { Format } from '@/interfaces/ISubtitle'; + + +class SubtitleMetadataTask implements IMediaTask { + private videoPath: string; + + private streamIndex: number; + + private subtitlePath: string; + + public constructor( + videoPath: string, + streamIndex: number, subtitlePath: string, + ) { + this.videoPath = videoPath; + this.streamIndex = streamIndex; + this.subtitlePath = subtitlePath; + } + + public static async from(videoPath: string, streamIndex: number) { + const hash = await mediaQuickHash(videoPath); + const subtitlePath = join(await getSubtitleDir(), `${hash}-${streamIndex}.${Format.AdvancedSubStationAplha}`); + return new SubtitleMetadataTask(videoPath, streamIndex, subtitlePath); + } + + public getId() { return `${['metadata', this.videoPath, this.streamIndex].join('-')}`; } + + public execute(): Promise { + return new Promise((resolve, reject) => { + ipcRenderer.send('subtitle-metadata-request', this.videoPath, this.streamIndex, this.subtitlePath); + ipcRenderer.once('subtitle-metadata-reply', (event, error, finished, metadata) => { + if (error) reject(error); + else if (finished) reject(new Error('Extraction finished.')); + else resolve(metadata); + }); + }); + } +} + +class SubtitleCacheTask implements IMediaTask { + private readonly videoPath: string; + + private readonly streamIndex: number; + + public constructor(videoPath: string, streamIndex: number) { + this.videoPath = videoPath; + this.streamIndex = streamIndex; + } + + public getId() { + return `${['cache', this.videoPath, this.streamIndex].join('-')}`; + } + + public execute(): Promise { + return new Promise((resolve, reject) => { + ipcRenderer.send('subtitle-cache-request', this.videoPath, this.streamIndex); + ipcRenderer.once('subtitle-cache-reply', (event, error, finished, payload) => { + if (error) reject(error); + else if (!finished) resolve(); + else resolve(payload); + }); + }); + } +} + +class SubtitleFragmentTask implements IMediaTask { + private readonly videoPath: string; + + private readonly streamIndex: number; + + private readonly videoTime: number; + + + public constructor(videoPath: string, streamIndex: number, videoTime: number) { + this.videoPath = videoPath; + this.streamIndex = streamIndex; + this.videoTime = videoTime; + } + + public getId() { + return `${[ + 'fragment', + this.videoPath, + this.streamIndex, + ].join('-')}`; + } + + public execute(): Promise { + return new Promise((resolve, reject) => { + ipcRenderer.send('subtitle-stream-request', this.videoPath, this.streamIndex, this.videoTime); + ipcRenderer.once('subtitle-stream-reply', (event, error, data) => { + if (error) reject(error); + else resolve(data); + }); + }); + } +} + +class SubtitleDestroyTask implements IMediaTask { + private readonly videoPath: string; + + private readonly streamIndex: number; + + public constructor(videoPath: string, streamIndex: number) { + this.videoPath = videoPath; + this.streamIndex = streamIndex; + } + + public getId() { + return `${['finished', this.videoPath, this.streamIndex].join('-')}`; + } + + public execute(): Promise { + return new Promise((resolve, reject) => { + ipcRenderer.send('subtitle-destroy-request', this.videoPath, this.streamIndex); + ipcRenderer.once('subtitle-destroy-reply', (event, error) => { + if (error) reject(error); + else resolve(); + }); + }); + } +} + +export default class SubtitleQueue extends BaseMediaTaskQueue { + public getSubtitleMetadata(videoPath: string, streamIndex: number) { + return SubtitleMetadataTask.from(videoPath, streamIndex) + .then(task => super.addTask(task, { piority: 3 })); + } + + public cacheSubtitle(videoPath: string, streamIndex: number) { + return super.addTask(new SubtitleCacheTask(videoPath, streamIndex), { piority: 1 }); + } + + public getSubtitleFragment(videoPath: string, streamIndex: number, videoTime: number) { + return super.addTask( + new SubtitleFragmentTask(videoPath, streamIndex, videoTime), + { piority: 2 }, + ); + } + + public stopSubtitleExtraction(videoPath: string, streamIndex: number) { + const task = new SubtitleDestroyTask(videoPath, streamIndex); + this.pendingTasks + .filter(({ id }) => new RegExp(`${videoPath}-${streamIndex}`).test(id)) + .forEach(({ id }) => this.cancelTask(id)); + return super.addTask(task, { piority: 4 }); + } +} diff --git a/src/renderer/typings.d.ts b/src/renderer/typings.d.ts index 3fc082e174..f6e5a4be2f 100644 --- a/src/renderer/typings.d.ts +++ b/src/renderer/typings.d.ts @@ -180,9 +180,17 @@ declare module 'electron' { timeString: string, width: number, height: number, ) => void): this; - on(channel: 'subtitle-request', listener: (event: Event, - videoPath: string, subtitlePath: string, - streamIndex: string, + on(channel: 'subtitle-metadata-request', listener: (event: Event, + videoPath: string, streamIndex: number, subtitlePath: string, + ) => void): this; + on(channel: 'subtitle-cache-request', listener: (event: Event, + videoPath: string, streamIndex: number + ) => void): this; + on(channel: 'subtitle-stream-request', listener: (event: Event, + videoPath: string, streamIndex: number, time: number, + ) => void): this; + on(channel: 'subtitle-destroy-request', listener: (event: Event, + videoPath: string, streamIndex: number, ) => void): this; on(channel: 'thumbnail-request', listener: (event: Event, videoPath: string, imagePath: string, @@ -198,10 +206,18 @@ declare module 'electron' { timeString: string, width: number, height: number, ): void; - send(channel: 'subtitle-request', - videoPath: string, subtitlePath: string, - streamIndex: string, - ): void; + send(channel: 'subtitle-metadata-request', + videoPath: string, streamIndex: number, subtitlePath: string, + ): this; + send(channel: 'subtitle-cache-request', + videoPath: string, streamIndex: number + ): this; + send(channel: 'subtitle-stream-request', + videoPath: string, streamIndex: number, time: number, + ): this; + send(channel: 'subtitle-destroy-request', + videoPath: string, streamIndex: number + ): this; send(channel: 'thumbnail-request', videoPath: string, imagePath: string, thumbnailWidth: number, @@ -210,12 +226,18 @@ declare module 'electron' { on(channel: 'media-info-reply', listener: (event: Event, error?: Error, info: string) => void): this; on(channel: 'snapshot-reply', listener: (event: Event, error?: Error, path: string) => void): this; - on(channel: 'subtitle-reply', listener: (event: Event, error: Error | undefined, path: string) => void): this; + on(channel: 'subtitle-metadata-reply', listener: (event: Event, error?: Error, finished: boolean, matadata?: string) => void): this; + on(channel: 'subtitle-cache-reply', listener: (event: Event, error?: Error, finished: boolean, payload?: string) => void): this; + on(channel: 'subtitle-stream-reply', listener: (event: Event, error?: Error, dialogue: string) => void): this; + on(channel: 'subtitle-destroy-reply', listener: (event: Event, error?: Error) => void): this; on(channel: 'thumbnail-reply', listener: (event: Event, error?: Error, path: string) => void): this; once(channel: 'media-info-reply', listener: (event: Event, error?: Error, info: string) => void): this; once(channel: 'snapshot-reply', listener: (event: Event, error?: Error, path: string) => void): this; - once(channel: 'subtitle-reply', listener: (event: Event, error: Error | undefined, path: string) => void): this; + once(channel: 'subtitle-metadata-reply', listener: (event: Event, error?: Error, finished: boolean, matadata?: string) => void): this; + once(channel: 'subtitle-cache-reply', listener: (event: Event, error?: Error, finished: boolean, payload?: string) => void): this; + once(channel: 'subtitle-stream-reply', listener: (event: Event, error?: Error, dialogue: string) => void): this; + once(channel: 'subtitle-destroy-reply', listener: (event: Event, error?: Error) => void): this; once(channel: 'thumbnail-reply', listener: (event: Event, error?: Error, path: string) => void): this; } @@ -223,7 +245,10 @@ declare module 'electron' { reply(channel: string, ...args: any[]): void; reply(channel: 'media-info-reply', error?: Error, info: string): void; reply(channel: 'snapshot-reply', error?: Error, path: string): this; - reply(channel: 'subtitle-reply', error: Error | undefined, path: string): this; + reply(channel: 'subtitle-metadata-reply', error?: Error, finished: boolean, matadata: string): this; + reply(channel: 'subtitle-cache-reply', error?: Error, finished: boolean): this; + reply(channel: 'subtitle-stream-reply', error?: Error, dialogue: string): this; + reply(channel: 'subtitle-destroy-reply', error?: Error): this; reply(channel: 'thumbnail-reply', error?: Error, path: string): void; } } From 9fbc942ae7626252f22b6cb80daa0606379482c3 Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 18:14:55 +0800 Subject: [PATCH 057/123] feat(subtitle): add subtitle loaders Add subtitle loaders implementing ILoader. 1. Add SUBTITLE_REAL_DIRNAME to constants.js. 2. Implement ILoader for embedded, local and sagi subtitles. 3. Add getLoader function to subtitle utils and fix sourceToFormat. --- src/renderer/constants.js | 10 + src/renderer/services/subtitle/utils/index.ts | 30 +- .../services/subtitle/utils/loaders.ts | 287 ++++++++++++++++++ 3 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 src/renderer/services/subtitle/utils/loaders.ts diff --git a/src/renderer/constants.js b/src/renderer/constants.js index 5d4ce13ab1..9dcc844e4d 100644 --- a/src/renderer/constants.js +++ b/src/renderer/constants.js @@ -1,3 +1,8 @@ +import electron from 'electron'; +import { join } from 'path'; + +const app = electron.app || electron.remote.app; + export const DEFAULT_VIDEO_EVENTS = [ 'abort', 'canplay', @@ -133,3 +138,8 @@ export const DEFAULT_LOG_DIRNAME = 'logs'; */ export const VIDEO_DIRNAME = 'videos'; // 视频缓存目录 export const SUBTITLE_DIRNAME = 'subtitles'; // 视频缓存目录 +export const SUBTITLE_REAL_DIRNAME = join( + app.getPath(ELECTRON_CACHE_DIRNAME), + DEFAULT_DIRNAME, + SUBTITLE_DIRNAME, +); diff --git a/src/renderer/services/subtitle/utils/index.ts b/src/renderer/services/subtitle/utils/index.ts index 2b41436d52..a7bc8d9acf 100644 --- a/src/renderer/services/subtitle/utils/index.ts +++ b/src/renderer/services/subtitle/utils/index.ts @@ -5,7 +5,7 @@ import { } from 'fs-extra'; import { extname } from 'path'; import { - ITags, IOrigin, Type, Format, IParser, + ITags, IOrigin, Type, Format, IParser, ILoader, } from '@/interfaces/ISubtitle'; import { LanguageCode } from '@/libs/language'; @@ -14,8 +14,11 @@ import { } from '@/services/subtitle'; import { assFragmentLanguageLoader, srtFragmentLanguageLoader, vttFragmentLanguageLoader } from './languageLoader'; -import { IEmbeddedOrigin } from '../loaders'; import { SagiSubtitlePayload } from '../parsers'; +import { + IEmbeddedOrigin, + EmbeddedTextStreamLoader, LocalTextLoader, SagiLoader, +} from './loaders'; /** * Cue tags getter for SubRip, SubStation Alpha and Online Transcript subtitles. @@ -133,11 +136,8 @@ export function sourceToFormat(subtitleSource: IOrigin) { case Type.Online: case Type.Translated: return Format.Sagi; - case Type.Embedded: { - const { extractedSrc } = (subtitleSource as IEmbeddedOrigin).source; - if (extractedSrc) return pathToFormat(extractedSrc); - return Format.Unknown; - } + case Type.Embedded: + return Format.AdvancedSubStationAplha; default: return pathToFormat(subtitleSource.source as string); } @@ -171,6 +171,22 @@ export async function inferLanguageFromPath(path: string): Promise } } +export function getLoader(source: IOrigin): ILoader { + switch (source.type) { + default: + throw new Error('Unknown source type.'); + case Type.Embedded: { + const { videoPath, streamIndex } = (source as IEmbeddedOrigin).source; + return new EmbeddedTextStreamLoader(videoPath, streamIndex); + } + case Type.Local: + return new LocalTextLoader(source.source as string); + case Type.Online: + case Type.Translated: + return new SagiLoader(source.source as string); + } +} + export function getParser(format: Format, payload: unknown): IParser { switch (format) { case Format.AdvancedSubStationAplha: diff --git a/src/renderer/services/subtitle/utils/loaders.ts b/src/renderer/services/subtitle/utils/loaders.ts new file mode 100644 index 0000000000..e0b74e768a --- /dev/null +++ b/src/renderer/services/subtitle/utils/loaders.ts @@ -0,0 +1,287 @@ +import { dirname, join, extname } from 'path'; +import { + ensureDirSync, existsSync, copyFile, outputFile, writeFile, +} from 'fs-extra'; +import { EventEmitter } from 'events'; +import { + ILoader, IOrigin, Type, Format, +} from '@/interfaces/ISubtitle'; +import { loadLocalFile, formatToExtension } from '.'; +import { SUBTITLE_REAL_DIRNAME } from '@/constants'; +import { mediaQuickHash, getSubtitleDir } from '@/libs/utils'; +import Sagi from '@/libs/sagi'; +import { SagiSubtitlePayload } from '../parsers'; +import { sagiSubtitleToWebVTT } from './transcoders'; +import { + getSubtitleMetadata, cacheSubtitle, getSubtitleFragment, finishSubtitleExtraction, +} from '@/plugins/mediaTasks'; + +enum Status { + NOT_STARTED, + WORKING, + FINISHED, +} +export interface ILocalTextOrigin extends IOrigin { + type: Type.Local; + source: string; +} +export class LocalTextLoader extends EventEmitter implements ILoader { + public readonly canPreload = true; + + private _loadingStatus = Status.NOT_STARTED; + + public get fullyRead() { return this._loadingStatus === Status.FINISHED; } + + public get canUpload() { return this._loadingStatus === Status.FINISHED; } + + private _cacheStatus = Status.NOT_STARTED; + + public get canCache() { + return this._loadingStatus === Status.FINISHED && this._cacheStatus === Status.NOT_STARTED; + } + + public readonly source: ILocalTextOrigin; + + public constructor(path: string) { + super(); + this.source = { type: Type.Local, source: path }; + if (dirname(path) === SUBTITLE_REAL_DIRNAME) this._cacheStatus = Status.FINISHED; + } + + private _payloadString: string = ''; + + public async getPayload(): Promise { + if (this._loadingStatus === Status.NOT_STARTED) { + this._loadingStatus = Status.WORKING; + this._payloadString = await loadLocalFile(this.source.source); + this._loadingStatus = Status.FINISHED; + this.emit('cache', this.canCache); + this.emit('upload', this.canUpload); + this.emit('read', this.fullyRead); + } + return this._payloadString; + } + + public pause() {} + + public async cache() { + if (this.canCache) { + const { source } = this.source; + const hash = await mediaQuickHash.try(source); + if (hash) { + this._cacheStatus = Status.WORKING; + const storedPath = join(SUBTITLE_REAL_DIRNAME, `${hash}${extname(source)}`); + ensureDirSync(SUBTITLE_REAL_DIRNAME); + if (!existsSync(storedPath)) await copyFile(source, storedPath); + this._cacheStatus = Status.FINISHED; + return { + type: Type.Local, + source: storedPath, + }; + } + throw new Error('Invalid hash.'); + } + throw new Error('Cannot cache now.'); + } + + public async destroy() { this._payloadString = ''; } +} +export interface IEmbeddedOrigin extends IOrigin { + type: Type.Embedded; + source: { + videoPath: string; + streamIndex: number; + }; +} +export class EmbeddedTextStreamLoader extends EventEmitter implements ILoader { + public readonly canPreload = false; + + private _loadingStatus = Status.NOT_STARTED; + + public get fullyRead() { return this._loadingStatus === Status.FINISHED; } + + public get canUpload() { return this._loadingStatus === Status.FINISHED; } + + private _cacheStatus = Status.NOT_STARTED; + + public get canCache() { + return this._loadingStatus === Status.FINISHED && this._cacheStatus === Status.NOT_STARTED; + } + + public readonly source: IEmbeddedOrigin; + + public constructor(videoPath: string, streamIndex: number) { + super(); + this.source = { type: Type.Embedded, source: { videoPath, streamIndex } }; + } + + public async getPayload(time?: number): Promise { + if (this.fullyRead) return this._payloadString; + this.paused = false; + if (!this.listenerCount('extraction-finished')) { + this.on('extraction-finished', () => { + if (!this.paused && this.currentTime === -1) this.extractSequentially(); + }); + this.once('all-finished', () => { + this._loadingStatus = Status.FINISHED; + this.emit('cache', this.canCache); + this.emit('upload', this.canUpload); + this.emit('read', this.fullyRead); + this.removeAllListeners(); + }); + } + let result = ''; + if (time) { + this.currentTime = time; + await this.extractRandomly(); + result = this._payloadString; + this._payloadString = ''; + } else this.extractSequentially(); + return result; + } + + public async cache() { + if (this.canCache) { + this._cacheStatus = Status.WORKING; + const { videoPath, streamIndex } = this.source.source; + const hash = await mediaQuickHash(videoPath); + const subtitlePath = join(await getSubtitleDir(), `${hash}-${streamIndex}.${Format.AdvancedSubStationAplha}`); + await writeFile(subtitlePath, this._payloadString); + this._cacheStatus = Status.FINISHED; + return { + type: Type.Local, + source: subtitlePath, + }; + } + throw new Error('Cannot cache now.'); + } + + private _payloadString: string = ''; + + private _metadataString: string = ''; + + private async getMetadata() { + if (!this._metadataString) { + this._loadingStatus = Status.WORKING; + const { videoPath, streamIndex } = this.source.source; + const result = await getSubtitleMetadata(videoPath, streamIndex); + if (result) this._metadataString = result; + } + } + + private cacheCount = 0; + + private async extractSequentially() { + if (!this._metadataString) await this.getMetadata(); + const { videoPath, streamIndex } = this.source.source; + if (!this.cacheCount) console.time(`${this.source.source.streamIndex}-cache-took: `); + this.cacheCount += 1; + console.time(`${this.source.source.streamIndex}-cache-${this.cacheCount}`); + const result = await cacheSubtitle(videoPath, streamIndex); + if (result) { + this._payloadString = result; + this.emit('all-finished'); + console.timeEnd(`${this.source.source.streamIndex}-cache-took: `); + } else this.emit('extraction-finished'); + console.timeEnd(`${this.source.source.streamIndex}-cache-${this.cacheCount}`); + } + + private currentTime: number = -1; + + private initialRandom: boolean = true; + + private streamCount = 0; + + private async extractRandomly() { + if (!this._metadataString) await this.getMetadata(); + if (this.currentTime !== -1) { + this.streamCount += 1; + console.time(`${this.source.source.streamIndex}-stream-${this.streamCount}`); + const { videoPath, streamIndex } = this.source.source; + const result = await getSubtitleFragment(videoPath, streamIndex, this.currentTime); + console.timeEnd(`${this.source.source.streamIndex}-stream-${this.streamCount}`); + this.currentTime = -1; + if (result) { + if (this.initialRandom) { + this.initialRandom = false; + this._payloadString += `${this._metadataString}\n${result}`; + } else this._payloadString = result; + } + } + this.emit('extraction-finished'); + } + + private paused = false; + + public pause() { this.paused = true; } + + public async destroy() { + this.removeAllListeners(); + this._payloadString = ''; + await finishSubtitleExtraction(this.source.source.videoPath, this.source.source.streamIndex); + } +} +export interface ISagiOrigin extends IOrigin { + type: Type.Online; + source: string; +} +export class SagiLoader extends EventEmitter implements ILoader { + public readonly canPreload = true; + + private _loadingStatus = Status.NOT_STARTED; + + public get fullyRead() { return this._loadingStatus === Status.FINISHED; } + + public get canUpload() { return this._loadingStatus === Status.FINISHED; } + + private _cacheStatus = Status.NOT_STARTED; + + public get canCache() { + return this._loadingStatus === Status.FINISHED && this._cacheStatus === Status.NOT_STARTED; + } + + public readonly source: ISagiOrigin; + + public constructor(hash: string) { + super(); + this.source = { type: Type.Online, source: hash }; + } + + private _payloads: SagiSubtitlePayload = []; + + public async getPayload(): Promise { + if (this._loadingStatus === Status.NOT_STARTED) { + this._loadingStatus = Status.WORKING; + this._payloads = await Sagi.getTranscript({ + transcriptIdentity: this.source.source, + startTime: 0, + }); + this._loadingStatus = Status.FINISHED; + this.emit('cache', this.canCache); + this.emit('upload', this.canUpload); + this.emit('read', this.fullyRead); + } + return this._payloads; + } + + public pause() {} + + public async cache() { + if (this.canCache) { + const { source } = this.source; + this._cacheStatus = Status.NOT_STARTED; + const storedPath = join(SUBTITLE_REAL_DIRNAME, `${source}.${formatToExtension(Format.WebVTT)}`); + if (!existsSync(storedPath)) { + await outputFile(storedPath, sagiSubtitleToWebVTT(this._payloads)); + } + this._cacheStatus = Status.FINISHED; + return { + type: Type.Local, + source: storedPath, + }; + } + throw new Error('Cannot cache now.'); + } + + public async destroy() { this._payloads = []; } +} From 572cb7817bbd6036f42044d19a4677139f039a3e Mon Sep 17 00:00:00 2001 From: Yan Yifeng Date: Wed, 4 Sep 2019 18:18:37 +0800 Subject: [PATCH 058/123] refactor(subititle): refactor entity generators Reimplement entity generators as IEntityGenerator in ISubtitle. 1. Fix some typos in Translated.ts. --- .../services/subtitle/loaders/embedded.ts | 73 +++---------------- .../services/subtitle/loaders/local.ts | 17 ++--- .../services/subtitle/loaders/online.ts | 20 +---- .../services/subtitle/loaders/translated.ts | 20 ++--- 4 files changed, 26 insertions(+), 104 deletions(-) diff --git a/src/renderer/services/subtitle/loaders/embedded.ts b/src/renderer/services/subtitle/loaders/embedded.ts index 42158fdcae..e1303e58eb 100644 --- a/src/renderer/services/subtitle/loaders/embedded.ts +++ b/src/renderer/services/subtitle/loaders/embedded.ts @@ -1,90 +1,41 @@ import { cloneDeep } from 'lodash'; -import { - IOrigin, Type, IEntityGenerator, Format, -} from '@/interfaces/ISubtitle'; +import { Type, IEntityGenerator, Format } from '@/interfaces/ISubtitle'; import { LanguageCode } from '@/libs/language'; import { mediaQuickHash } from '@/libs/utils'; -import { inferLanguageFromPath, loadLocalFile } from '../utils'; -import { ISubtitleStream, getSubtitlePath } from '@/plugins/mediaTasks'; - -/** - * Get extracted embedded subtitles's local src - * - * @param {string} videoSrc - path of the video file - * @param {number} subtitleStreamIndex - the number of the subtitle stream index - * @param {string} subtitleCodec - the codec of the embedded subtitle - * @returns the subtitle path string - */ -export { getSubtitlePath as embeddedSrcLoader } from '@/plugins/mediaTasks'; - -export interface IEmbeddedOrigin extends IOrigin { - type: Type.Embedded, - source: { - streamIndex: number; - videoSrc: string; - extractedSrc: string; - }; -} +import { ISubtitleStream } from '@/plugins/mediaTasks'; +import { IEmbeddedOrigin } from '../utils/loaders'; export class EmbeddedGenerator implements IEntityGenerator { private origin: IEmbeddedOrigin; - private format: Format; - private language: LanguageCode = LanguageCode.Default; public readonly isDefault: boolean; - public constructor(videoSrc: string, stream: ISubtitleStream) { + public constructor(videoPath: string, stream: ISubtitleStream) { this.origin = { type: Type.Embedded, source: { - videoSrc, + videoPath, streamIndex: stream.index, - extractedSrc: '', }, }; - this.format = stream.codecName ? stream.codecName as Format : Format.Unknown; this.language = stream.tags && stream.tags.language ? stream.tags.language : LanguageCode.No; this.isDefault = !!(stream.disposition && stream.disposition.default); } - public async getSource() { return cloneDeep(this.origin); } - - private type = Type.Embedded; + public async getDisplaySource() { return cloneDeep(this.origin); } - public async getType() { return this.type; } + public async getRealSource() { return cloneDeep(this.origin); } - public async getFormat() { return this.format; } - - private async getExtractedSrc() { - const { videoSrc, streamIndex, extractedSrc } = this.origin.source; - if (!extractedSrc) { - this.origin.source.extractedSrc = await getSubtitlePath(videoSrc, streamIndex, this.format); - return this.origin.source.extractedSrc; - } - return extractedSrc; - } + public async getFormat() { return Format.AdvancedSubStationAplha; } public async getHash() { - const hash = mediaQuickHash.try(await this.getExtractedSrc()); - return (hash || '') as unknown as string; + const { videoPath, streamIndex } = this.origin.source; + return `${await mediaQuickHash.try(videoPath) || ''}-${streamIndex}`; } - public async getLanguage() { - if (this.language !== LanguageCode.Default) return this.language; - const { videoSrc, streamIndex, extractedSrc } = this.origin.source; - if (!extractedSrc) { - this.origin.source.extractedSrc = await getSubtitlePath(videoSrc, streamIndex, this.format); - } - this.language = await inferLanguageFromPath(this.origin.source.extractedSrc); - return this.language; - } + public async getLanguage() { return this.language; } - private payload: string; - - public async getPayload() { - if (!this.payload) this.payload = await loadLocalFile(await this.getExtractedSrc()); - return this.payload; - } + public async getDelay() { return 0; } } diff --git a/src/renderer/services/subtitle/loaders/local.ts b/src/renderer/services/subtitle/loaders/local.ts index e58c98ad8b..0f624466aa 100644 --- a/src/renderer/services/subtitle/loaders/local.ts +++ b/src/renderer/services/subtitle/loaders/local.ts @@ -1,5 +1,5 @@ import { cloneDeep } from 'lodash'; -import { pathToFormat, loadLocalFile, inferLanguageFromPath } from '../utils'; +import { pathToFormat, inferLanguageFromPath } from '../utils'; import { IOrigin, Type, IEntityGenerator, Format, @@ -15,8 +15,6 @@ export class LocalGenerator implements IEntityGenerator { private format: Format; - private type = Type.Local; - public constructor(subtitlePath: string) { this.origin = { type: Type.Local, source: subtitlePath }; const format = pathToFormat(subtitlePath); @@ -24,9 +22,9 @@ export class LocalGenerator implements IEntityGenerator { this.format = format; } - public async getSource() { return cloneDeep(this.origin); } + public async getDisplaySource() { return cloneDeep(this.origin); } - public async getType() { return this.type; } + public async getRealSource() { return cloneDeep(this.origin); } public async getFormat() { if (this.format) return this.format; @@ -40,15 +38,10 @@ export class LocalGenerator implements IEntityGenerator { return inferLanguageFromPath(this.origin.source); } - private payload: string; - - public async getPayload() { - if (!this.payload) this.payload = await loadLocalFile(this.origin.source); - return this.payload; - } - public async getHash() { const hash = await mediaQuickHash(this.origin.source); return (hash || '') as unknown as string; } + + public async getDelay() { return 0; } } diff --git a/src/renderer/services/subtitle/loaders/online.ts b/src/renderer/services/subtitle/loaders/online.ts index 007077aa7d..2d0a31d352 100644 --- a/src/renderer/services/subtitle/loaders/online.ts +++ b/src/renderer/services/subtitle/loaders/online.ts @@ -1,11 +1,9 @@ import { TranscriptInfo } from 'sagi-api/translation/v1/translation_pb'; import { cloneDeep } from 'lodash'; import { LanguageCode, normalizeCode } from '@/libs/language'; -import Sagi from '@/libs/sagi'; import { IOrigin, IEntityGenerator, Type, Format, } from '@/interfaces/ISubtitle'; -import { SagiSubtitlePayload } from '../parsers'; export type TranscriptInfo = TranscriptInfo.AsObject; @@ -32,11 +30,9 @@ export class OnlineGenerator implements IEntityGenerator { this.delayInSeconds = transcriptInfo.delay / 1000; } - private type = Type.Online + public async getDisplaySource() { return cloneDeep(this.origin); } - public async getType() { return this.type; } - - public async getSource() { return cloneDeep(this.origin); } + public async getRealSource() { return cloneDeep(this.origin); } public async getLanguage() { return this.language; @@ -49,16 +45,4 @@ export class OnlineGenerator implements IEntityGenerator { public async getFormat() { return this.format; } public async getHash() { return this.origin.source; } - - private payload: SagiSubtitlePayload | undefined; - - public async getPayload() { - if (!this.payload) { - this.payload = await Sagi.getTranscript({ - transcriptIdentity: this.origin.source, - startTime: 0, - }); - } - return this.payload; - } } diff --git a/src/renderer/services/subtitle/loaders/translated.ts b/src/renderer/services/subtitle/loaders/translated.ts index 0ae02089e9..b378ec2305 100644 --- a/src/renderer/services/subtitle/loaders/translated.ts +++ b/src/renderer/services/subtitle/loaders/translated.ts @@ -5,7 +5,6 @@ import { LanguageCode, normalizeCode } from '@/libs/language'; import { IOrigin, IEntityGenerator, Type, Format, } from '@/interfaces/ISubtitle'; -import Sagi from '@/libs/sagi'; interface ITranslatedOrigin extends IOrigin { @@ -24,10 +23,10 @@ export class TranslatedGenerator implements IEntityGenerator { public readonly ranking: number; - private tranlsatedType: TranslatedGeneratorType; + private translatedType: TranslatedGeneratorType; public constructor(transcriptInfo: TranscriptInfo.AsObject | null, languageCode?: LanguageCode) { - this.tranlsatedType = transcriptInfo ? TranslatedGeneratorType.Subtitle + this.translatedType = transcriptInfo ? TranslatedGeneratorType.Subtitle : TranslatedGeneratorType.Button; this.origin = { type: Type.Translated, @@ -43,27 +42,22 @@ export class TranslatedGenerator implements IEntityGenerator { this.ranking = transcriptInfo ? transcriptInfo.ranking : 0; } - public async getType() { return Type.Translated; } + public async getDisplaySource() { return cloneDeep(this.origin); } - public async getSource() { return cloneDeep(this.origin); } + public async getRealSource() { return cloneDeep(this.origin); } public async getLanguage() { return this.language; } - public async getFormat() { - return this.tranlsatedType === TranslatedGeneratorType.Subtitle ? Format.Sagi : Format.Unknown; - } + public async getFormat() { return Format.Sagi; } public async getHash() { - if (this.tranlsatedType === TranslatedGeneratorType.Subtitle) { + if (this.translatedType === TranslatedGeneratorType.Subtitle) { return this.origin.source; } return uuidv4(); } - public async getPayload() { - return this.tranlsatedType === TranslatedGeneratorType.Subtitle - ? Sagi.getTranscript({ transcriptIdentity: this.origin.source, startTime: 0 }) : ''; - } + public async getDelay() { return 0; } } From fc1fceff05d2bbcb978ee37078c6dbfbef6a4938 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Wed, 4 Sep 2019 18:32:07 +0800 Subject: [PATCH 059/123] feat(browsing): title --- src/renderer/components/BrowsingView.vue | 11 +++++++++++ .../components/BrowsingView/BrowsingHeader.vue | 5 +++++ .../components/BrowsingView/BrowsingInput.vue | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 3d606d4250..cbfe7241f4 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -9,6 +9,7 @@ { + this.title = this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.getTitle(); this.currentUrl = urlParseLax(state.url).href; this.removeListener(); this.addListenerToBrowser(); @@ -426,6 +432,9 @@ export default { this.isGlobal = true; this.handleEnterPip(); }, + handlePageTitle(e: Event, title: string) { + this.title = title; + }, focusHandler() { this.menuService.updateFocusedWindow(true); this.updatePipState(this.hasVideo); @@ -555,6 +564,7 @@ export default { addListenerToBrowser() { const view = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; view.webContents.addListener('ipc-message', this.ipcMessage); + view.webContents.addListener('page-title-updated', this.handlePageTitle); view.webContents.addListener('dom-ready', this.domReady); view.webContents.addListener('new-window', this.newWindow); view.webContents.addListener('did-start-loading', this.didStartLoading); @@ -571,6 +581,7 @@ export default { 'did-stop-loading', this.didStopLoading, ); + currentWebContents.removeListener('page-title-updated', this.handlePageTitle); currentWebContents.removeListener('dom-ready', this.domReady); currentWebContents.removeListener('ipc-message', this.ipcMessage); currentWebContents.removeListener( diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index 61e7872299..9e6e3e713b 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -11,6 +11,7 @@ /> @@ -40,6 +41,10 @@ export default { type: Boolean, default: false, }, + title: { + type: String, + default: 'Splayer', + }, handleEnterPip: { type: Function, required: true, diff --git a/src/renderer/components/BrowsingView/BrowsingInput.vue b/src/renderer/components/BrowsingView/BrowsingInput.vue index 2c92f5f82c..356d150f79 100644 --- a/src/renderer/components/BrowsingView/BrowsingInput.vue +++ b/src/renderer/components/BrowsingView/BrowsingInput.vue @@ -5,7 +5,7 @@ Date: Wed, 4 Sep 2019 18:52:11 +0800 Subject: [PATCH 060/123] feat(browsing): title bar --- .../assets/icon/pageRefresh-hover-icon.svg | 21 ++++++----- .../components/BrowsingView/BrowsingInput.vue | 37 ++++++++++++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/renderer/assets/icon/pageRefresh-hover-icon.svg b/src/renderer/assets/icon/pageRefresh-hover-icon.svg index 3a931e56e0..61536e5b4f 100644 --- a/src/renderer/assets/icon/pageRefresh-hover-icon.svg +++ b/src/renderer/assets/icon/pageRefresh-hover-icon.svg @@ -1,12 +1,15 @@ - - - - Created with Sketch. - - - - - + + + + + + + + + + + + diff --git a/src/renderer/components/BrowsingView/BrowsingInput.vue b/src/renderer/components/BrowsingView/BrowsingInput.vue index 356d150f79..b0a3d60775 100644 --- a/src/renderer/components/BrowsingView/BrowsingInput.vue +++ b/src/renderer/components/BrowsingView/BrowsingInput.vue @@ -7,14 +7,17 @@ > {{ title }} - +
+ +
@@ -89,12 +92,20 @@ export default { letter-spacing: 0.09px; text-align: center; } + .control-button { + width: 30px; + height: 30px; + border-radius: 100%; + display: flex; + justify-content: center; + align-items: center; + transition: background-color 100ms ease-in; + &:hover { + background-color: #F5F6F8; + } + } .page-refresh-icon { - width: 16px; - height: 16px; - -webkit-app-region: no-drag; - margin-right: 16px; - margin-left: 12px; + margin-right: 8px; } } From 0fc6f211fd8b7c71b3b3138714976dd6a48de543 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Wed, 4 Sep 2019 20:01:01 +0800 Subject: [PATCH 061/123] feat(browsing): pip control --- src/renderer/App.vue | 2 +- .../assets/icon/down-default-icon.svg | 14 ++++++ src/renderer/assets/icon/pip-default-icon.svg | 15 +++---- src/renderer/components/BaseIconContainer.vue | 43 ++++--------------- .../BrowsingView/BrowsingHeader.vue | 1 + .../components/BrowsingView/BrowsingInput.vue | 3 ++ .../BrowsingView/BrowsingPipControl.vue | 39 ++++++++++++++--- 7 files changed, 66 insertions(+), 51 deletions(-) create mode 100644 src/renderer/assets/icon/down-default-icon.svg diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 6cb676a3a0..f3c91bb7da 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -58,7 +58,7 @@ export default { $route(to: any, from: any) { if (to.name === 'landing-view' && from.name === 'language-setting') this.transitionMode = 'fade'; else this.transitionMode = ''; - if (to.name !== 'browsing-view') this.showSidebar = false; + if (to.name !== 'browsing-view' && !(to.name === 'landing-view' && from.name === 'browsing-view')) this.showSidebar = false; }, showSidebar(val: boolean) { ipcRenderer.send('update-sidebar', val); diff --git a/src/renderer/assets/icon/down-default-icon.svg b/src/renderer/assets/icon/down-default-icon.svg new file mode 100644 index 0000000000..e40f996959 --- /dev/null +++ b/src/renderer/assets/icon/down-default-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/renderer/assets/icon/pip-default-icon.svg b/src/renderer/assets/icon/pip-default-icon.svg index 643a634875..653e64813a 100644 --- a/src/renderer/assets/icon/pip-default-icon.svg +++ b/src/renderer/assets/icon/pip-default-icon.svg @@ -1,10 +1,9 @@ - - - - Created with Sketch. - - - + + + + + + - + \ No newline at end of file diff --git a/src/renderer/components/BaseIconContainer.vue b/src/renderer/components/BaseIconContainer.vue index d4115d9c0c..e01c35ffb7 100644 --- a/src/renderer/components/BaseIconContainer.vue +++ b/src/renderer/components/BaseIconContainer.vue @@ -557,6 +557,10 @@ screen and (min-aspect-ratio: 1/1) and (min-height: 1080px) { width: 16px; height: 16px; } +.down { + width: 7px; + height: 5px; +} .showMarks, .hideMarks, .closeSearch { width: 18px; height: 18px; @@ -621,45 +625,14 @@ screen and (min-aspect-ratio: 1/1) and (min-height: 1080px) { } } } -.pipDisabled, .videoRecordDisabled { +.videoRecordDisabled { display: block; width: 20px; height: 20px; } -.pip { - width: 20px; - height: 20px; - .default { - display: block; - } - .hover { - display: none; - } - .active { - display: none; - } - &:hover { - .default { - display: none; - } - .hover { - display: block; - } - .active { - display: none; - } - } - &:active { - .default { - display: none; - } - .hover { - display: none; - } - .active { - display: block; - } - } +.pip, .pipDisabled{ + width: 18px; + height: 14px; } .pipRecord, .pipBack { width: 20px; diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index 9e6e3e713b..f62507dc1d 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -16,6 +16,7 @@ :play-file-with-playing-view="playFileWithPlayingView" /> diff --git a/src/renderer/components/BrowsingView/BrowsingInput.vue b/src/renderer/components/BrowsingView/BrowsingInput.vue index b0a3d60775..0dd935628e 100644 --- a/src/renderer/components/BrowsingView/BrowsingInput.vue +++ b/src/renderer/components/BrowsingView/BrowsingInput.vue @@ -91,6 +91,9 @@ export default { color: rgba(15, 26, 59, 0.5); letter-spacing: 0.09px; text-align: center; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } .control-button { width: 30px; diff --git a/src/renderer/components/BrowsingView/BrowsingPipControl.vue b/src/renderer/components/BrowsingView/BrowsingPipControl.vue index 16c6d348b2..1de8a66f34 100644 --- a/src/renderer/components/BrowsingView/BrowsingPipControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingPipControl.vue @@ -2,11 +2,18 @@
- +
+ + +
From 25b16adbee1f0327a70f1cdbda74d184e1e9c63e Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Thu, 5 Sep 2019 10:57:50 +0800 Subject: [PATCH 062/123] fix(browsingview): fix may play video in hidden window --- src/main/helpers/BrowserViewManager.ts | 34 +++++++++---------- src/renderer/components/BrowsingView.vue | 1 + .../BrowsingView/BrowsingHeader.vue | 1 - static/pip/titlebarPreload.js | 2 +- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main/helpers/BrowserViewManager.ts b/src/main/helpers/BrowserViewManager.ts index 05d59bf7fe..ad70380ff8 100644 --- a/src/main/helpers/BrowserViewManager.ts +++ b/src/main/helpers/BrowserViewManager.ts @@ -65,27 +65,25 @@ export class BrowserViewManager implements IBrowserViewManager { }), }; // loadURL - page.view.webContents.loadURL(page.url).then(() => { - if (channel === this.currentChannel) { - page.view.webContents.executeJavaScript('var iframe = document.querySelector("iframe");if (iframe && iframe.contentDocument) {document.getElementsByTagName("video").length + iframe.contentDocument.getElementsByTagName("video").length} else {document.getElementsByTagName("video").length}').then((r: number) => { - const hasLastPage = this.history[channel].list.length; - if (hasLastPage && r) { - page.view.webContents.once('media-started-playing', () => { - if (this.currentChannel.includes('bilibili')) { - let type = ''; - page.view.webContents - .executeJavaScript(bilibiliFindType).then((r: (HTMLElement | null)[]) => { - type = ['bangumi', 'videoStreaming', 'iframeStreaming', 'iframeStreaming', 'video'][r.findIndex(i => i)] || 'others'; - page.view.webContents.executeJavaScript(bilibiliVideoPause(type)); - }); - } else { - page.view.webContents.executeJavaScript('setTimeout(() => { document.querySelector("video").pause(); }, 100)'); - } - }); + page.view.webContents.loadURL(page.url); + if (channel === this.currentChannel) { + const hasLastPage = this.history[channel].list.length; + if (hasLastPage) { + page.view.webContents.once('media-started-playing', () => { + if (page.view.webContents.getURL() !== page.url) return; + if (this.currentChannel.includes('bilibili')) { + let type = ''; + page.view.webContents + .executeJavaScript(bilibiliFindType).then((r: (HTMLElement | null)[]) => { + type = ['bangumi', 'videoStreaming', 'iframeStreaming', 'iframeStreaming', 'video'][r.findIndex(i => i)] || 'others'; + page.view.webContents.executeJavaScript(bilibiliVideoPause(type)); + }); + } else { + page.view.webContents.executeJavaScript('setTimeout(() => { document.querySelector("video").pause(); }, 100)'); } }); } - }); + } // 暂停当前视频 if (this.currentChannel) { diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 86e23cc92b..111d162147 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -476,6 +476,7 @@ export default { .finally(() => { this.asyncTasksDone = true; if (!this.isPip) { + this.$electron.ipcRenderer.send('remove-browser'); window.close(); } else { this.isGlobal = true; diff --git a/src/renderer/components/BrowsingView/BrowsingHeader.vue b/src/renderer/components/BrowsingView/BrowsingHeader.vue index 24d89e1f49..7ae2eaa51f 100644 --- a/src/renderer/components/BrowsingView/BrowsingHeader.vue +++ b/src/renderer/components/BrowsingView/BrowsingHeader.vue @@ -22,7 +22,6 @@ @@ -110,7 +196,7 @@ export default { From 05bc90ab1fc507abd4e9627b64be0a6428623d8a Mon Sep 17 00:00:00 2001 From: Tan Yang <1029862865@qq.com> Date: Thu, 5 Sep 2019 18:14:02 +0800 Subject: [PATCH 069/123] fix(browsingview): fix iqiyi can not login with third party --- src/main/helpers/BrowserViewManager.ts | 1 + src/main/index.js | 2 +- src/main/menu/Menu.ts | 6 +- src/renderer/components/BrowsingView.vue | 170 +++++++---------------- 4 files changed, 52 insertions(+), 127 deletions(-) diff --git a/src/main/helpers/BrowserViewManager.ts b/src/main/helpers/BrowserViewManager.ts index ad70380ff8..a30ed1ee8f 100644 --- a/src/main/helpers/BrowserViewManager.ts +++ b/src/main/helpers/BrowserViewManager.ts @@ -61,6 +61,7 @@ export class BrowserViewManager implements IBrowserViewManager { view: new BrowserView({ webPreferences: { preload: `${require('path').resolve(__static, 'pip/preload.js')}`, + nativeWindowOpen: true, }, }), }; diff --git a/src/main/index.js b/src/main/index.js index c79936006e..8813fc02e5 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1199,7 +1199,7 @@ const oauthRegex = [ /^https:\/\/account.xiaomi.com\/pass\//i, ]; app.on('web-contents-created', (webContentsCreatedEvent, contents) => { - if (contents.getType() === 'webview') { + if (contents.getType() === 'browserView') { contents.on('new-window', (newWindowEvent, url) => { if (!oauthRegex.some(re => re.test(url))) { newWindowEvent.preventDefault(); diff --git a/src/main/menu/Menu.ts b/src/main/menu/Menu.ts index 1a7e90ac97..239d18a033 100644 --- a/src/main/menu/Menu.ts +++ b/src/main/menu/Menu.ts @@ -65,9 +65,9 @@ export default class Menubar { public updateFocusedWindow(isMainWindow: boolean) { if (this.focusOnMainWindow !== isMainWindow) { if (!isMainWindow) { - this.updateMenuItemEnabled('browsing.history.back', false); - this.updateMenuItemEnabled('browsing.history.forward', false); - this.updateMenuItemEnabled('browsing.history.reload', false); + this.updateMenuItemEnabled('history.back', false); + this.updateMenuItemEnabled('history.forward', false); + this.updateMenuItemEnabled('history.reload', false); this.updateMenuItemEnabled('browsing.window.pip', true); this.updateMenuItemEnabled('browsing.window.keepPipFront', true); this.updateMenuItemLabel('browsing.window.pip', 'msg.window.exitPip'); diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 145d276e70..8804fb4e3b 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -92,7 +92,6 @@ export default { pipBtnsKeepShow: false, asyncTasksDone: false, headerToShow: true, - pipRestore: false, acceleratorAvailable: true, oldDisplayId: -1, backToLandingView: false, @@ -107,6 +106,16 @@ export default { startLoading: false, title: 'Splayer', readyState: '', + oauthRegex: [ + /^https:\/\/cnpassport.youku.com\//i, + /^https:\/\/passport.iqiyi.com\/apis\/thirdparty/i, + /^https:\/\/api.weibo.com\/oauth2/i, + /^https:\/\/graph.qq.com\//i, + /^https:\/\/open.weixin.qq.com\//i, + /^https:\/\/openapi.baidu.com\//i, + /^https:\/\/auth.alipay.com\/login\//i, + /^https:\/\/account.xiaomi.com\/pass\//i, + ], }; }, computed: { @@ -146,7 +155,7 @@ export default { progress() { switch (this.readyState) { case 'loading': - return 30; + return 30; case 'interactive': return 70; case 'complete': @@ -239,45 +248,8 @@ export default { } }, loadingState(val: boolean) { - const loadUrl = this.$electron.remote - .getCurrentWindow() - .getBrowserViews()[0] - .webContents.getURL(); - const hostname = urlParseLax(loadUrl).hostname; - let channel = hostname.slice(hostname.indexOf('.') + 1, hostname.length); - if (loadUrl.includes('youtube')) { - channel = 'youtube.com'; - } - this.updateCanGoBack( - this.$electron.remote - .getCurrentWindow() - .getBrowserViews()[0] - .webContents.canGoBack(), - ); - this.updateCanGoForward( - this.$electron.remote - .getCurrentWindow() - .getBrowserViews()[0] - .webContents.canGoForward(), - ); if (val) { this.hasVideo = false; - } else { - if (this.pipRestore) { - this.pipAdapter(); - this.pipRestore = false; - } - this.$electron.remote - .getCurrentWindow() - .getBrowserViews()[0] - .webContents.executeJavaScript( - this.calculateVideoNum, - (r: number) => { - this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id - ? false - : !!r; - }, - ); } }, headerToShow(val: boolean) { @@ -324,7 +296,8 @@ export default { this.menuService = new MenuService(); this.menuService.updateMenuItemEnabled('file.open', false); - this.title = this.$electron.remote.getCurrentWindow().getBrowserViews()[0].webContents.getTitle(); + this.title = this.$electron.remote.getCurrentWindow() + .getBrowserViews()[0].webContents.getTitle(); this.$bus.$on('toggle-reload', this.handleUrlReload); this.$bus.$on('toggle-back', this.handleUrlBack); @@ -670,30 +643,6 @@ export default { // this.dropFiles = args.files; // } break; - case 'left-drag': - if (this.isPip) { - if (args.windowSize) { - this.$electron.ipcRenderer.send( - 'callBrowsingWindowMethod', - 'setBounds', - [ - { - x: args.x, - y: args.y, - width: args.windowSize[0], - height: args.windowSize[1], - }, - ], - ); - } else { - this.$electron.ipcRenderer.send( - 'callBrowsingWindowMethod', - 'setPosition', - [args.x, args.y], - ); - } - } - break; case 'fullscreenchange': if (!this.isPip) { this.headerToShow = !args.isFullScreen; @@ -727,41 +676,40 @@ export default { } }, handleOpenUrl({ url }: { url: string }) { - if (!this.startLoading) { - this.startLoading = true; - if ( - !url - || url === 'about:blank' - || urlParseLax(url).href === urlParseLax(this.currentUrl).href - ) return; - const protocol = urlParseLax(url).protocol; - const openUrl = protocol ? url : `https:${url}`; - const newHostname = urlParseLax(openUrl).hostname; - const oldHostname = urlParseLax(this.currentUrl).hostname; - let newChannel = newHostname.slice( - newHostname.indexOf('.') + 1, - newHostname.length, - ); - let oldChannel = oldHostname.slice( - oldHostname.indexOf('.') + 1, - oldHostname.length, - ); - if (openUrl.includes('youtube')) { - newChannel = 'youtube.com'; - } - if (this.currentUrl.includes('youtube')) { - oldChannel = 'youtube.com'; - } - if (oldChannel === newChannel) { - this.loadingState = true; - this.currentUrl = urlParseLax(openUrl).href; - this.$electron.ipcRenderer.send('create-browser-view', { - url: openUrl, - isNewWindow: true, - }); - } else { - this.$electron.shell.openExternal(openUrl); - } + this.startLoading = true; + const protocol = urlParseLax(url).protocol; + const openUrl = protocol ? url : `https:${url}`; + if ( + !url + || url === 'about:blank' + || urlParseLax(openUrl).href === urlParseLax(this.currentUrl).href + ) return; + const newHostname = urlParseLax(openUrl).hostname; + const oldHostname = urlParseLax(this.currentUrl).hostname; + let newChannel = newHostname.slice( + newHostname.indexOf('.') + 1, + newHostname.length, + ); + let oldChannel = oldHostname.slice( + oldHostname.indexOf('.') + 1, + oldHostname.length, + ); + if (openUrl.includes('youtube')) { + newChannel = 'youtube.com'; + } + if (this.currentUrl.includes('youtube')) { + oldChannel = 'youtube.com'; + } + if (this.oauthRegex.some((re: RegExp) => re.test(url))) return; + if (oldChannel === newChannel) { + this.loadingState = true; + this.currentUrl = urlParseLax(openUrl).href; + this.$electron.ipcRenderer.send('create-browser-view', { + url: openUrl, + isNewWindow: true, + }); + } else { + this.$electron.shell.openExternal(openUrl); } }, pipAdapter() { @@ -871,7 +819,6 @@ export default { }, handleUrlReload() { if (this.isPip) { - this.pipRestore = true; this.$electron.remote .getCurrentWindow() .getBrowserViews()[0] @@ -917,29 +864,6 @@ export default { if (this.isPip) { this.exitPipOperation(); this.updateIsPip(false); - const loadUrl = this.$electron.remote - .getCurrentWindow() - .getBrowserViews()[0] - .webContents.getURL(); - const hostname = urlParseLax(loadUrl).hostname; - let channel = hostname.slice( - hostname.indexOf('.') + 1, - hostname.length, - ); - if (loadUrl.includes('youtube')) { - channel = 'youtube.com'; - } - this.$electron.remote - .getCurrentWindow() - .getBrowserViews()[0] - .webContents.executeJavaScript( - this.calculateVideoNum, - (r: number) => { - this.hasVideo = channel === 'youtube.com' && !getVideoId(loadUrl).id - ? false - : !!r; - }, - ); } }, othersAdapter() { From a1b0fb4d9d35b3f424c7fcabb6844c04c86d4e03 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Thu, 5 Sep 2019 19:01:04 +0800 Subject: [PATCH 070/123] feat(sidebar): adjust sidebaricon --- src/renderer/App.vue | 4 + src/renderer/components/BrowsingView.vue | 3 + .../LandingView/BilibiliSidebarIcon.vue | 132 ++++++++++------- .../LandingView/YoutubeSidebarIcon.vue | 139 +++++++++++------- .../LandingView/iQiyiSidebarIcon.vue | 26 +++- src/renderer/components/Sidebar.vue | 32 ++-- 6 files changed, 214 insertions(+), 122 deletions(-) diff --git a/src/renderer/App.vue b/src/renderer/App.vue index f3c91bb7da..cda903af73 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -16,6 +16,7 @@ @@ -52,6 +54,7 @@ export default { transitionMode: '', openFileArgs: null, showSidebar: false, + currentUrl: '', }; }, watch: { @@ -59,6 +62,7 @@ export default { if (to.name === 'landing-view' && from.name === 'language-setting') this.transitionMode = 'fade'; else this.transitionMode = ''; if (to.name !== 'browsing-view' && !(to.name === 'landing-view' && from.name === 'browsing-view')) this.showSidebar = false; + if (from.name === 'browsing-view' && to.name === 'landing-view') this.currentUrl = ''; }, showSidebar(val: boolean) { ipcRenderer.send('update-sidebar', val); diff --git a/src/renderer/components/BrowsingView.vue b/src/renderer/components/BrowsingView.vue index 35d0cff179..37027fefe8 100644 --- a/src/renderer/components/BrowsingView.vue +++ b/src/renderer/components/BrowsingView.vue @@ -157,6 +157,9 @@ export default { }, }, watch: { + currentUrl(val: string) { + this.$emit('update-current-url', val); + }, showSidebar(val: boolean) { const browserView = this.$electron.remote.getCurrentWindow().getBrowserViews()[0]; if (!val) { diff --git a/src/renderer/components/LandingView/BilibiliSidebarIcon.vue b/src/renderer/components/LandingView/BilibiliSidebarIcon.vue index 1c5c6a6035..3b28c86f23 100644 --- a/src/renderer/components/LandingView/BilibiliSidebarIcon.vue +++ b/src/renderer/components/LandingView/BilibiliSidebarIcon.vue @@ -1,81 +1,107 @@ diff --git a/src/renderer/components/LandingView/YoutubeSidebarIcon.vue b/src/renderer/components/LandingView/YoutubeSidebarIcon.vue index b2bfa4ee73..c869e4869c 100644 --- a/src/renderer/components/LandingView/YoutubeSidebarIcon.vue +++ b/src/renderer/components/LandingView/YoutubeSidebarIcon.vue @@ -1,8 +1,11 @@ @@ -45,6 +45,10 @@ export default { type: String, default: 'Splayer', }, + isReloading: { + type: Boolean, + required: true, + }, handleEnterPip: { type: Function, required: true, @@ -64,12 +68,7 @@ export default { handleBookmarkOpen: { type: Function, required: true, - }, - handleGlobalPip: { - type: Function, - required: true, - }, - }, + },}, data() { return { showOpenUrl: false, diff --git a/src/renderer/components/BrowsingView/BrowsingInput.vue b/src/renderer/components/BrowsingView/BrowsingInput.vue index c63bf16f7a..3313d641c4 100644 --- a/src/renderer/components/BrowsingView/BrowsingInput.vue +++ b/src/renderer/components/BrowsingView/BrowsingInput.vue @@ -13,7 +13,7 @@ class="control-button page-refresh-icon no-drag" > @@ -32,6 +32,10 @@ export default { type: String, default: 'Splayer', }, + isReloading: { + type: Boolean, + required: true, + }, handleUrlReload: { type: Function, required: true, diff --git a/src/renderer/components/BrowsingView/BrowsingPipControl.vue b/src/renderer/components/BrowsingView/BrowsingPipControl.vue index 177fff3e87..a3ce4fb9c2 100644 --- a/src/renderer/components/BrowsingView/BrowsingPipControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingPipControl.vue @@ -38,10 +38,6 @@ export default { type: Function, required: true, }, - handleGlobalPip: { - type: Function, - required: true, - }, hasVideo: { type: Boolean, default: false, @@ -55,13 +51,13 @@ export default { computed: { pipType() { if (!this.hasVideo) return 'pipDisabled'; - else if (this.pip === 'Enter') return 'pip'; + if (this.pip === 'Enter') return 'pip'; return 'pop'; }, }, methods: { handlePip() { - this.pip === 'Enter' ? this.handleEnterPip() : this.handleGlobalPip(); + this.pip === 'Enter' ? this.handleEnterPip(true) : this.handleEnterPip(false); }, switchPipType() { this.pip = this.pip === 'Enter' ? 'Global' : 'Enter'; diff --git a/src/renderer/locales/lang/en.json b/src/renderer/locales/lang/en.json index b9edd65b71..1bbea43d4f 100644 --- a/src/renderer/locales/lang/en.json +++ b/src/renderer/locales/lang/en.json @@ -198,7 +198,9 @@ "name": "Window", "originSize": "100%", "enterPip": "Enter Picture In Picture", - "exitPip": "Exit Picture In Picture" + "exitPip": "Exit Picture In Picture", + "playInNewWindow": "Play In New Window", + "backToBrowser": "Back To Browser" }, "favourite": { "name": "Channel", diff --git a/src/renderer/main.ts b/src/renderer/main.ts index ab8daff2bb..0a7967a5f5 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -959,7 +959,10 @@ new Vue({ this.browsingViewTop = !this.browsingViewTop; }); this.menuService.on('browsing.window.pip', () => { - this.$bus.$emit('toggle-pip'); + this.$bus.$emit('toggle-pip', true); + }); + this.menuService.on('browsing.window.playInNewWindow', () => { + this.$bus.$emit('toggle-pip', false); }); this.menuService.on('browsing.window.maxmize', () => { const browserWindow = this.$electron.remote.getCurrentWindow(); From 7939ac559564c29c356b1ca295823bc5cc7ed4a1 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Fri, 6 Sep 2019 15:39:54 +0800 Subject: [PATCH 089/123] feat(browsing): adjust ui --- src/renderer/assets/icon/back-default-icon.svg | 2 +- src/renderer/assets/icon/down-default-icon.svg | 2 +- .../assets/icon/forward-default-icon.svg | 2 +- .../assets/icon/pageRefresh-hover-icon.svg | 2 +- .../assets/icon/pipDisabled-default-icon.svg | 4 ++-- src/renderer/assets/icon/pop-default-icon.svg | 2 +- .../assets/icon/sidebar-default-icon.svg | 2 +- src/renderer/components/BaseIconContainer.vue | 8 ++++---- .../BrowsingView/BrowsingControl.vue | 2 +- .../components/BrowsingView/BrowsingInput.vue | 2 +- .../BrowsingView/BrowsingPipControl.vue | 18 ++++++++++++++---- 11 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/renderer/assets/icon/back-default-icon.svg b/src/renderer/assets/icon/back-default-icon.svg index 2611f227d6..fc3d80e59a 100644 --- a/src/renderer/assets/icon/back-default-icon.svg +++ b/src/renderer/assets/icon/back-default-icon.svg @@ -4,6 +4,6 @@ icon--backward Created with Sketch. - + \ No newline at end of file diff --git a/src/renderer/assets/icon/down-default-icon.svg b/src/renderer/assets/icon/down-default-icon.svg index 8f01457df6..446ca80bf1 100644 --- a/src/renderer/assets/icon/down-default-icon.svg +++ b/src/renderer/assets/icon/down-default-icon.svg @@ -1,7 +1,7 @@ - + diff --git a/src/renderer/assets/icon/forward-default-icon.svg b/src/renderer/assets/icon/forward-default-icon.svg index 4fe2a78765..76bcc69584 100644 --- a/src/renderer/assets/icon/forward-default-icon.svg +++ b/src/renderer/assets/icon/forward-default-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/assets/icon/pageRefresh-hover-icon.svg b/src/renderer/assets/icon/pageRefresh-hover-icon.svg index 61536e5b4f..ae72cf1721 100644 --- a/src/renderer/assets/icon/pageRefresh-hover-icon.svg +++ b/src/renderer/assets/icon/pageRefresh-hover-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/assets/icon/pipDisabled-default-icon.svg b/src/renderer/assets/icon/pipDisabled-default-icon.svg index 7d51db9103..565e8bfa3d 100644 --- a/src/renderer/assets/icon/pipDisabled-default-icon.svg +++ b/src/renderer/assets/icon/pipDisabled-default-icon.svg @@ -2,8 +2,8 @@ Created with Sketch. - - + + diff --git a/src/renderer/assets/icon/pop-default-icon.svg b/src/renderer/assets/icon/pop-default-icon.svg index 93ea84e0f1..dcdba07654 100644 --- a/src/renderer/assets/icon/pop-default-icon.svg +++ b/src/renderer/assets/icon/pop-default-icon.svg @@ -1,7 +1,7 @@ - + diff --git a/src/renderer/assets/icon/sidebar-default-icon.svg b/src/renderer/assets/icon/sidebar-default-icon.svg index df6b4868d9..a7afc69f9b 100644 --- a/src/renderer/assets/icon/sidebar-default-icon.svg +++ b/src/renderer/assets/icon/sidebar-default-icon.svg @@ -1,6 +1,6 @@ - + diff --git a/src/renderer/components/BaseIconContainer.vue b/src/renderer/components/BaseIconContainer.vue index 13402869c0..5780a54905 100644 --- a/src/renderer/components/BaseIconContainer.vue +++ b/src/renderer/components/BaseIconContainer.vue @@ -66,8 +66,8 @@ export default { margin: auto; } .sidebar { - width: 14px; - height: 12px; + width: 16px; + height: 16px; } .hoverState { display: flex; @@ -631,8 +631,8 @@ screen and (min-aspect-ratio: 1/1) and (min-height: 1080px) { height: 20px; } .pip, .pop, .pipDisabled { - width: 18px; - height: 14px; + width: 20px; + height: 20px; } .pipRecord, .pipBack { width: 20px; diff --git a/src/renderer/components/BrowsingView/BrowsingControl.vue b/src/renderer/components/BrowsingView/BrowsingControl.vue index 9fabc6fb97..063465c770 100644 --- a/src/renderer/components/BrowsingView/BrowsingControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingControl.vue @@ -96,7 +96,7 @@ export default { transition: background-color 100ms ease-in; } .button-hover:hover { - background-color: #F5F6F8; + background-color: #ECEEF0; } .sidebar-icon { margin-left: 8px; diff --git a/src/renderer/components/BrowsingView/BrowsingInput.vue b/src/renderer/components/BrowsingView/BrowsingInput.vue index c63bf16f7a..d999bca986 100644 --- a/src/renderer/components/BrowsingView/BrowsingInput.vue +++ b/src/renderer/components/BrowsingView/BrowsingInput.vue @@ -109,7 +109,7 @@ export default { align-items: center; transition: background-color 100ms ease-in; &:hover { - background-color: #F5F6F8; + background-color: #ECEEF0; } } .page-refresh-icon { diff --git a/src/renderer/components/BrowsingView/BrowsingPipControl.vue b/src/renderer/components/BrowsingView/BrowsingPipControl.vue index 7896f19ee5..f8b1f41ef5 100644 --- a/src/renderer/components/BrowsingView/BrowsingPipControl.vue +++ b/src/renderer/components/BrowsingView/BrowsingPipControl.vue @@ -7,7 +7,7 @@ :style="{ opacity: hasVideo ? 1.0 : 0.3, }" - :class="hasVideo ? 'button-hover': ''" + :class="hasVideo ? 'button-hover' : ''" class="pip-icon no-drag" > Date: Fri, 6 Sep 2019 15:41:29 +0800 Subject: [PATCH 090/123] feat(browsing): pop opacity --- src/renderer/assets/icon/pop-default-icon.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/assets/icon/pop-default-icon.svg b/src/renderer/assets/icon/pop-default-icon.svg index dcdba07654..79c7fc4b96 100644 --- a/src/renderer/assets/icon/pop-default-icon.svg +++ b/src/renderer/assets/icon/pop-default-icon.svg @@ -1,7 +1,7 @@ - + From 75e0546f347850ed1b6200472ed1c3c86512859a Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Fri, 6 Sep 2019 15:53:59 +0800 Subject: [PATCH 091/123] fix(sidebaricon): iqiyi eslint warning --- .../LandingView/iQiyiSidebarIcon.vue | 111 +++++++++--------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/src/renderer/components/LandingView/iQiyiSidebarIcon.vue b/src/renderer/components/LandingView/iQiyiSidebarIcon.vue index 03d54e1949..efb323f0df 100644 --- a/src/renderer/components/LandingView/iQiyiSidebarIcon.vue +++ b/src/renderer/components/LandingView/iQiyiSidebarIcon.vue @@ -1,5 +1,5 @@
- - - - - + + + - - - - + + + + - + + + - - +
diff --git a/src/renderer/components/BrowsingView/BrowsingTitleBar.vue b/src/renderer/components/BrowsingView/BrowsingTitleBar.vue new file mode 100644 index 0000000000..36391ddc7b --- /dev/null +++ b/src/renderer/components/BrowsingView/BrowsingTitleBar.vue @@ -0,0 +1,83 @@ + + + From e75b9b04b85bb9e7e7822fa3b2b0ceaa2ae55160 Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Fri, 6 Sep 2019 18:02:08 +0800 Subject: [PATCH 103/123] fix(browsing): icon size --- .../icon/menuBar/__icon/--stopRefresh.svg | 9 --------- .../assets/icon/reloadStop-default-icon.svg | 14 ++++++++------ src/renderer/components/BaseIconContainer.vue | 18 ------------------ 3 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 src/renderer/assets/icon/menuBar/__icon/--stopRefresh.svg diff --git a/src/renderer/assets/icon/menuBar/__icon/--stopRefresh.svg b/src/renderer/assets/icon/menuBar/__icon/--stopRefresh.svg deleted file mode 100644 index 73b78d6a48..0000000000 --- a/src/renderer/assets/icon/menuBar/__icon/--stopRefresh.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - menuBar/__icon/--stopRefresh - Created with Sketch. - - - - \ No newline at end of file diff --git a/src/renderer/assets/icon/reloadStop-default-icon.svg b/src/renderer/assets/icon/reloadStop-default-icon.svg index fa983a31b2..73b78d6a48 100644 --- a/src/renderer/assets/icon/reloadStop-default-icon.svg +++ b/src/renderer/assets/icon/reloadStop-default-icon.svg @@ -1,7 +1,9 @@ - - - - - + + + + menuBar/__icon/--stopRefresh + Created with Sketch. + + - + \ No newline at end of file diff --git a/src/renderer/components/BaseIconContainer.vue b/src/renderer/components/BaseIconContainer.vue index 0f708bee1e..3fd82f57db 100644 --- a/src/renderer/components/BaseIconContainer.vue +++ b/src/renderer/components/BaseIconContainer.vue @@ -544,24 +544,6 @@ screen and (min-aspect-ratio: 1/1) and (min-height: 1080px) { height: 16px; display: block; } -.backDisabled, .forwardDisabled { - display: block; - width: 16px; - height: 16px; -} -.back, .forward, .pageRefresh, .reloadStop { - width: 16px; - height: 16px; - .default { - display: block; - } - .hover { - display: none; - } - .active { - display: none; - } -} .down { width: 5px; height: 7px; From a82c1385c3b2400d4a8900c5d22061ad8dcfba2d Mon Sep 17 00:00:00 2001 From: YuRi Jin <875830716@qq.com> Date: Fri, 6 Sep 2019 18:07:12 +0800 Subject: [PATCH 104/123] fix(browsing): lint fix --- src/renderer/components/BrowsingView/BrowsingTitleBar.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/BrowsingView/BrowsingTitleBar.vue b/src/renderer/components/BrowsingView/BrowsingTitleBar.vue index 36391ddc7b..7be4b4da1f 100644 --- a/src/renderer/components/BrowsingView/BrowsingTitleBar.vue +++ b/src/renderer/components/BrowsingView/BrowsingTitleBar.vue @@ -28,10 +28,11 @@ type="browsingclose" /> - +