diff --git a/package.json b/package.json index b11664ed0..223284980 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "freetube", "productName": "FreeTube", "description": "A private YouTube client", - "version": "0.15.0", + "version": "0.15.1", "license": "AGPL-3.0-or-later", "main": "./dist/main.js", "private": true, @@ -47,7 +47,6 @@ "release": "run-s test build", "test": "run-s rebuild:node pack:workers jest", "test:watch": "run-s rebuild:node pack:workers jest:watch", - "ci": "yarn install --frozen-lockfile" }, "dependencies": { @@ -93,7 +92,7 @@ "yt-channel-info": "^2.2.0", "yt-dash-manifest-generator": "1.1.0", "yt-trending-scraper": "^2.0.1", - "ytdl-core": "^4.9.1", + "ytdl-core": "fent/node-ytdl-core#pull/1022/head", "ytpl": "^2.2.3", "ytsr": "^3.5.3" }, @@ -109,7 +108,7 @@ "babel-loader": "^8.2.2", "copy-webpack-plugin": "^9.0.1", "css-loader": "5.2.6", - "electron": "^13.5.1", + "electron": "^15.3.1", "electron-builder": "^22.11.7", "electron-builder-squirrel-windows": "^22.13.1", "electron-debug": "^3.2.0", diff --git a/src/main/index.js b/src/main/index.js index ac6d54f06..849f12c58 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -180,7 +180,7 @@ function runApp() { /** * Initial window options */ - const newWindow = new BrowserWindow({ + const commonBrowserWindowOptions = { backgroundColor: '#212121', icon: isDev ? path.join(__dirname, '../../_icons/iconColor.png') @@ -194,10 +194,36 @@ function runApp() { webSecurity: false, backgroundThrottling: false, contextIsolation: false - }, - show: false + } + } + const newWindow = new BrowserWindow( + Object.assign( + { + // It will be shown later when ready via `ready-to-show` event + show: false + }, + commonBrowserWindowOptions + ) + ) + + // region Ensure child windows use same options since electron 14 + + // https://github.com/electron/electron/blob/14-x-y/docs/api/window-open.md#native-window-example + newWindow.webContents.setWindowOpenHandler(() => { + return { + action: 'allow', + overrideBrowserWindowOptions: Object.assign( + { + // It should be visible on click + show: true + }, + commonBrowserWindowOptions + ) + } }) + // endregion Ensure child windows use same options since electron 14 + if (replaceMainWindow) { mainWindow = newWindow } diff --git a/src/renderer/App.js b/src/renderer/App.js index 504782cc4..74975d914 100644 --- a/src/renderer/App.js +++ b/src/renderer/App.js @@ -130,7 +130,7 @@ export default Vue.extend({ }, mounted: function () { this.grabUserSettings().then(async () => { - await this.fetchInvidiousInstances() + await this.fetchInvidiousInstances({ isDev: this.isDev }) if (this.defaultInvidiousInstance === '') { await this.setRandomCurrentInvidiousInstance() } @@ -398,10 +398,10 @@ export default Vue.extend({ } case 'channel': { - const { channelId } = result + const { channelId, subPath } = result this.$router.push({ - path: `/channel/${channelId}` + path: `/channel/${channelId}/${subPath}` }) break } diff --git a/src/renderer/components/external-player-settings/external-player-settings.js b/src/renderer/components/external-player-settings/external-player-settings.js index 62a150400..04d7bd260 100644 --- a/src/renderer/components/external-player-settings/external-player-settings.js +++ b/src/renderer/components/external-player-settings/external-player-settings.js @@ -40,6 +40,18 @@ export default Vue.extend({ }, externalPlayerCustomArgs: function () { return this.$store.getters.getExternalPlayerCustomArgs + }, + externalPlayerCustomArgsTooltip: function () { + const tooltip = this.$t('Tooltips.External Player Settings.Custom External Player Arguments') + + const cmdArgs = this.$store.getters.getExternalPlayerCmdArguments[this.externalPlayer] + if (cmdArgs && typeof cmdArgs.defaultCustomArguments === 'string' && cmdArgs.defaultCustomArguments !== '') { + const defaultArgs = this.$t('Tooltips.External Player Settings.DefaultCustomArgumentsTemplate') + .replace('$', cmdArgs.defaultCustomArguments) + return `${tooltip} ${defaultArgs}` + } + + return tooltip } }, methods: { diff --git a/src/renderer/components/external-player-settings/external-player-settings.vue b/src/renderer/components/external-player-settings/external-player-settings.vue index c2298dc22..e1f962c50 100644 --- a/src/renderer/components/external-player-settings/external-player-settings.vue +++ b/src/renderer/components/external-player-settings/external-player-settings.vue @@ -21,6 +21,7 @@ diff --git a/src/renderer/components/ft-button/ft-button.css b/src/renderer/components/ft-button/ft-button.css index 2edc62660..49e3a6e1c 100644 --- a/src/renderer/components/ft-button/ft-button.css +++ b/src/renderer/components/ft-button/ft-button.css @@ -19,6 +19,11 @@ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); } +.btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + .ripple { position: relative; overflow: hidden; diff --git a/src/renderer/components/ft-input/ft-input.css b/src/renderer/components/ft-input/ft-input.css index 7b69b0299..7487db1c5 100644 --- a/src/renderer/components/ft-input/ft-input.css +++ b/src/renderer/components/ft-input/ft-input.css @@ -2,6 +2,11 @@ position: relative; } +.disabled label, .disabled .ft-input{ + opacity: 0.4; + cursor: not-allowed; +} + .clearInputTextButton { position: absolute; /* horizontal intentionally reduced to keep "I-beam pointer" visible */ diff --git a/src/renderer/components/ft-input/ft-input.js b/src/renderer/components/ft-input/ft-input.js index 87641d6a7..659eb5e4f 100644 --- a/src/renderer/components/ft-input/ft-input.js +++ b/src/renderer/components/ft-input/ft-input.js @@ -62,6 +62,7 @@ export default Vue.extend({ selectedOption: -1, isPointerInList: false }, + visibleDataList: this.dataList, // This button should be invisible on app start // As the text input box should be empty clearTextButtonExisting: false, @@ -87,9 +88,6 @@ export default Vue.extend({ } }, watch: { - value: function (val) { - this.inputData = val - }, inputDataPresent: function (newVal, oldVal) { if (newVal) { // The button needs to be visible **immediately** @@ -114,6 +112,7 @@ export default Vue.extend({ mounted: function () { this.id = this._uid this.inputData = this.value + this.updateVisibleDataList() setTimeout(this.addListener, 200) }, @@ -127,17 +126,19 @@ export default Vue.extend({ this.$emit('click', this.inputData) }, - handleInput: function () { + handleInput: function (val) { if (this.isSearch && this.searchState.selectedOption !== -1 && - this.inputData === this.dataList[this.searchState.selectedOption]) { return } + this.inputData === this.visibleDataList[this.searchState.selectedOption]) { return } this.handleActionIconChange() - this.$emit('input', this.inputData) + this.updateVisibleDataList() + this.$emit('input', val) }, handleClearTextClick: function () { this.inputData = '' this.handleActionIconChange() + this.updateVisibleDataList() this.$emit('input', this.inputData) // Focus on input element after text is clear for better UX @@ -208,14 +209,13 @@ export default Vue.extend({ handleOptionClick: function (index) { this.searchState.showOptions = false - this.inputData = this.dataList[index] + this.inputData = this.visibleDataList[index] this.$emit('input', this.inputData) this.handleClick() }, handleKeyDown: function (keyCode) { if (this.dataList.length === 0) { return } - // Update selectedOption based on arrow key pressed if (keyCode === 40) { this.searchState.selectedOption = (this.searchState.selectedOption + 1) % this.dataList.length @@ -229,8 +229,16 @@ export default Vue.extend({ this.searchState.selectedOption = -1 } + // Key pressed isn't enter + if (keyCode !== 13) { + this.searchState.showOptions = true + } // Update Input box value if arrow keys were pressed - if ((keyCode === 40 || keyCode === 38) && this.searchState.selectedOption !== -1) { this.inputData = this.dataList[this.searchState.selectedOption] } + if ((keyCode === 40 || keyCode === 38) && this.searchState.selectedOption !== -1) { + this.inputData = this.visibleDataList[this.searchState.selectedOption] + } else { + this.updateVisibleDataList() + } }, handleInputBlur: function () { @@ -244,6 +252,24 @@ export default Vue.extend({ } }, + updateVisibleDataList: function () { + if (this.dataList.length === 0) { return } + if (this.inputData === '') { + this.visibleDataList = this.dataList + return + } + // get list of items that match input + const visList = this.dataList.filter(x => { + if (x.toLowerCase().indexOf(this.inputData.toLowerCase()) !== -1) { + return true + } else { + return false + } + }) + + this.visibleDataList = visList + }, + ...mapActions([ 'getYoutubeUrlInfo' ]) diff --git a/src/renderer/components/ft-input/ft-input.vue b/src/renderer/components/ft-input/ft-input.vue index bd78dceb2..7803dddea 100644 --- a/src/renderer/components/ft-input/ft-input.vue +++ b/src/renderer/components/ft-input/ft-input.vue @@ -5,7 +5,8 @@ search: isSearch, forceTextColor: forceTextColor, showActionButton: showActionButton, - showClearTextButton: showClearTextButton + showClearTextButton: showClearTextButton, + disabled: disabled }" >