diff --git a/package-lock.json b/package-lock.json index 48386dc6..d1985748 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12940,6 +12940,11 @@ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -16268,8 +16273,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "resolved": "", "dev": true }, "schema-utils": { @@ -18870,8 +18874,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "resolved": "", "dev": true }, "string-width": { diff --git a/package.json b/package.json index f0a7d32b..0c59a1a7 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "dateformat": "^3.0.3", "electron-context-menu": "^2.0.1", "jquery": "^3.5.1", + "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", "material-design-icons": "^3.0.1", "mediaelement": "^4.2.16", diff --git a/src/renderer/components/ft-input/ft-input.css b/src/renderer/components/ft-input/ft-input.css index 4e3aeee6..4eb90319 100644 --- a/src/renderer/components/ft-input/ft-input.css +++ b/src/renderer/components/ft-input/ft-input.css @@ -22,27 +22,35 @@ color: var(--tertiary-text-color); } -.search .ft-input { +.forceTextColor .ft-input { color: var(--text-with-main-color); border-bottom: 1px solid var(--text-with-main-color); } -.search ::-webkit-input-placeholder { +.forceTextColor ::-webkit-input-placeholder { color: var(--text-with-main-color); } .inputAction { position: absolute; padding: 10px; - top: 10px; + top: 5px; right: 0px; cursor: pointer; border-radius: 200px 200px 200px 200px; color: var(--primary-text-color); } +.search ::-webkit-calendar-picker-indicator { + display: none; +} + .search .inputAction { - color: var(--text-with-main-color) + top: 12px; +} + +.forceTextColor .inputAction { + color: var(--text-with-main-color); } .inputAction:hover { @@ -52,7 +60,7 @@ transition: background 0.2s ease-in; } -.search .inputAction:hover { +.forceTextColor .inputAction:hover { background-color: var(--primary-color-hover); } @@ -63,6 +71,6 @@ transition: background 0.2s ease-in; } -.search .inputAction:active { +.forceTextColor .inputAction:active { background-color: var(--primary-color-active); } diff --git a/src/renderer/components/ft-input/ft-input.js b/src/renderer/components/ft-input/ft-input.js index 86d7a1be..91197b1b 100644 --- a/src/renderer/components/ft-input/ft-input.js +++ b/src/renderer/components/ft-input/ft-input.js @@ -7,14 +7,26 @@ export default Vue.extend({ type: String, required: true }, + value: { + type: String, + default: '' + }, showArrow: { type: Boolean, default: true }, + showLabel: { + type: Boolean, + default: false + }, isSearch: { type: Boolean, default: false - } + }, + dataList: { + type: Array, + default: () => { return [] } + }, }, data: function () { return { @@ -29,6 +41,10 @@ export default Vue.extend({ forceTextColor: function () { return this.isSearch && this.barColor + }, + + idDataList: function () { + return `${this.id}_datalist` } }, mounted: function () { @@ -41,6 +57,11 @@ export default Vue.extend({ this.$emit('click', this.inputData) }, + handleInput: function (input) { + this.inputData = input + this.$emit('input', input) + }, + addListener: function () { const inputElement = document.getElementById(this.id) diff --git a/src/renderer/components/ft-input/ft-input.vue b/src/renderer/components/ft-input/ft-input.vue index 2d3902a6..efd890a7 100644 --- a/src/renderer/components/ft-input/ft-input.vue +++ b/src/renderer/components/ft-input/ft-input.vue @@ -1,14 +1,25 @@ diff --git a/src/renderer/components/general-settings/general-settings.js b/src/renderer/components/general-settings/general-settings.js index 77ebd409..7116bb77 100644 --- a/src/renderer/components/general-settings/general-settings.js +++ b/src/renderer/components/general-settings/general-settings.js @@ -3,14 +3,18 @@ import $ from 'jquery' import { mapActions } from 'vuex' import FtCard from '../ft-card/ft-card.vue' import FtSelect from '../ft-select/ft-select.vue' +import FtInput from '../ft-input/ft-input.vue' import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue' import FtFlexBox from '../ft-flex-box/ft-flex-box.vue' +import debounce from 'lodash.debounce' + export default Vue.extend({ name: 'GeneralSettings', components: { 'ft-card': FtCard, 'ft-select': FtSelect, + 'ft-input': FtInput, 'ft-toggle-switch': FtToggleSwitch, 'ft-flex-box': FtFlexBox }, @@ -597,8 +601,20 @@ export default Vue.extend({ console.log(requestUrl) console.log(error) }) + + this.updateInvidiousInstanceBounce = debounce(this.updateInvidiousInstance, 500) + }, + beforeDestroy: function () { + if (this.invidiousInstance === '') { + this.updateInvidiousInstance('https://invidio.us') + } }, methods: { + handleInvidiousInstanceInput: function (input) { + const invidiousInstance = input.replace(/\/$/, '') + this.updateInvidiousInstanceBounce(invidiousInstance) + }, + ...mapActions([ 'updateBackendFallback', 'updateCheckForUpdates', diff --git a/src/renderer/components/general-settings/general-settings.vue b/src/renderer/components/general-settings/general-settings.vue index d9341f78..91832530 100644 --- a/src/renderer/components/general-settings/general-settings.vue +++ b/src/renderer/components/general-settings/general-settings.vue @@ -57,15 +57,17 @@ :select-values="thumbnailTypeValues" @change="updateThumbnailPreference" /> - + + + diff --git a/src/renderer/components/top-nav/top-nav.js b/src/renderer/components/top-nav/top-nav.js index 224348b2..27609dc2 100644 --- a/src/renderer/components/top-nav/top-nav.js +++ b/src/renderer/components/top-nav/top-nav.js @@ -3,6 +3,8 @@ import FtInput from '../ft-input/ft-input.vue' import FtSearchFilters from '../ft-search-filters/ft-search-filters.vue' import $ from 'jquery' import router from '../../router/index.js' +import debounce from 'lodash.debounce' +import ytSuggest from 'youtube-suggest' export default Vue.extend({ name: 'TopNav', @@ -14,7 +16,9 @@ export default Vue.extend({ return { component: this, windowWidth: 0, - showFilters: false + showFilters: false, + searchValue: '', + searchSuggestionsDataList: [] } }, computed: { @@ -28,7 +32,19 @@ export default Vue.extend({ barColor: function () { return this.$store.getters.getBarColor - } + }, + + invidiousInstance: function () { + return this.$store.getters.getInvidiousInstance + }, + + backendFallback: function () { + return this.$store.getters.getBackendFallback + }, + + backendPreference: function () { + return this.$store.getters.getBackendPreference + }, }, mounted: function () { const appWidth = $(window).width() @@ -48,6 +64,8 @@ export default Vue.extend({ searchContainer.style.display = 'none' } }) + + this.debounceSearchResults = debounce(this.getSearchSuggestions, 500) }, methods: { goToSearch: function (query) { @@ -84,6 +102,55 @@ export default Vue.extend({ this.showFilters = false }, + getSearchSuggestionsDebounce: function (query) { + this.debounceSearchResults(query) + }, + + getSearchSuggestions: function (query) { + switch (this.backendPreference) { + case 'local': + this.getSearchSuggestionsLocal(query) + break + case 'invidious': + this.getSearchSuggestionsInvidious(query) + break + } + }, + + getSearchSuggestionsLocal: function (query) { + if (query === '') { + this.searchSuggestionsDataList = [] + this.searchValue = '' + return + } + + ytSuggest(query).then((results) => { + this.searchSuggestionsDataList = results + this.searchValue = query + }) + }, + + getSearchSuggestionsInvidious: function (query) { + if (query === '') { + this.searchSuggestionsDataList = [] + this.searchValue = '' + return + } + + const searchPayload = { + resource: 'search/suggestions', + id: '', + params: { + q: query + } + } + + this.$store.dispatch('invidiousAPICall', searchPayload).then((results) => { + this.searchSuggestionsDataList = results.suggestions + this.searchValue = query + }) + }, + toggleSearchContainer: function () { const searchContainer = $('.searchContainer').get(0) diff --git a/src/renderer/components/top-nav/top-nav.vue b/src/renderer/components/top-nav/top-nav.vue index 685c86b5..c005b8e0 100644 --- a/src/renderer/components/top-nav/top-nav.vue +++ b/src/renderer/components/top-nav/top-nav.vue @@ -39,6 +39,9 @@ placeholder="Search / Go to URL" class="searchInput" :is-search="true" + :data-list="searchSuggestionsDataList" + :value="searchValue" + @input="getSearchSuggestionsDebounce" @click="goToSearch" /> { if (!err) { console.log(results) results.forEach((result) => { switch (result._id) { case 'invidiousInstance': - commit('setInvidiousInstance', result.value) + if (result.value === '') { + dispatch('updateInvidiousInstance', 'https://invidio.us') + } else { + commit('setInvidiousInstance', result.value) + } break case 'backendFallback': commit('setBackendFallback', result.value) diff --git a/src/renderer/views/Watch/Watch.js b/src/renderer/views/Watch/Watch.js index e6021570..3533c7d7 100644 --- a/src/renderer/views/Watch/Watch.js +++ b/src/renderer/views/Watch/Watch.js @@ -137,6 +137,7 @@ export default Vue.extend({ this.videoId = this.$route.params.id this.firstLoad = true + this.activeFormat = this.defaultVideoFormat this.checkIfPlaylist() @@ -206,6 +207,16 @@ export default Vue.extend({ this.videoDislikeCount = result.dislikes this.isLive = result.player_response.videoDetails.isLive + const subCount = result.author.subscriber_count + + if (subCount >= 1000000) { + this.channelSubscriptionCountText = `${subCount / 1000000}M` + } else if (subCount >= 10000) { + this.channelSubscriptionCountText = `${subCount / 1000}K` + } else { + this.channelSubscriptionCountText = subCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + } + if (this.isLive) { this.showLegacyPlayer = true this.showDashPlayer = false