Improve YouTube link handler

* Handle /user channel URL type
* Fix minor problems with the regexps
* Display informational toasts for hashtag and unknown URL types
* Add toast messages to the default locale
This commit is contained in:
Svallinn 2021-04-25 01:28:29 +01:00
parent 726d16bc41
commit 61b2fc4b48
No known key found for this signature in database
GPG Key ID: 09FB527F34037CCA
2 changed files with 52 additions and 21 deletions

View File

@ -1,4 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import { mapActions } from 'vuex'
import { ObserveVisibility } from 'vue-observe-visibility' import { ObserveVisibility } from 'vue-observe-visibility'
import FtFlexBox from './components/ft-flex-box/ft-flex-box.vue' import FtFlexBox from './components/ft-flex-box/ft-flex-box.vue'
import TopNav from './components/top-nav/top-nav.vue' import TopNav from './components/top-nav/top-nav.vue'
@ -280,7 +281,7 @@ export default Vue.extend({
console.log(el) console.log(el)
event.preventDefault() event.preventDefault()
// Check if YouTube video, channel or playlist // Check if it's a YouTube link
const youtubeUrlPattern = /^https?:\/\/((www\.)?youtube\.com(\/embed)?|youtu\.be)\/.*$/ const youtubeUrlPattern = /^https?:\/\/((www\.)?youtube\.com(\/embed)?|youtu\.be)\/.*$/
const isYoutubeLink = youtubeUrlPattern.test(el.href) const isYoutubeLink = youtubeUrlPattern.test(el.href)
@ -306,22 +307,31 @@ export default Vue.extend({
query: timestamp ? { timestamp } : {} query: timestamp ? { timestamp } : {}
}) })
} else { } else {
// Could be playlist, search, hashtag or channel // Could be a playlist, channel, search query or hashtag
// For now, ignore hashtags // If it's none of these, do nothing
//
// There's a limitation where some unknown URL types will be
// determined to be channels
// This is due to the ambiguity of some of the existing
// channel URL formats and there's not much that can be
// done to remedy it
const url = new URL(href) const url = new URL(href)
let urlType = 'unknown'
const channelPattern =
/^\/(?:c\/|channel\/|user\/)?([^/]+)(?:\/join)?\/?$/
let urlType
const typePatterns = new Map([ const typePatterns = new Map([
['playlist', /\/playlist$/], ['playlist', /^\/playlist\/?$/],
['search', /\/results$/], ['search', /^\/results\/?$/],
// ['hashtag', /\/hashtag\/([^/?&#]+)/], ['hashtag', /^\/hashtag\/([^/?&#]+)$/],
['channel', /\/(?:c\/|channel\/)?([^/?&#]+).*$/] ['channel', channelPattern]
]) ])
for (const [type, pattern] of typePatterns) { for (const [type, pattern] of typePatterns) {
const isAMatch = pattern.test(url.pathname) const matchFound = pattern.test(url.pathname)
if (isAMatch) { if (matchFound) {
urlType = type urlType = type
break break
} }
@ -330,7 +340,7 @@ export default Vue.extend({
switch (urlType) { switch (urlType) {
case 'playlist': { case 'playlist': {
if (!url.searchParams.has('list')) { if (!url.searchParams.has('list')) {
return throw new Error('Playlist: "list" field not found')
} }
const playlistId = url.searchParams.get('list') const playlistId = url.searchParams.get('list')
@ -350,7 +360,7 @@ export default Vue.extend({
case 'search': { case 'search': {
if (!url.searchParams.has('search_query')) { if (!url.searchParams.has('search_query')) {
return throw new Error('Search: "search_query" field not found')
} }
const searchQuery = url.searchParams.get('search_query') const searchQuery = url.searchParams.get('search_query')
@ -373,14 +383,24 @@ export default Vue.extend({
}) })
break break
} }
/* case 'hashtag': {
// placeholder case 'hashtag': {
// TODO: Implement a hashtag related view
let message = 'Hashtags have not yet been implemented, try again later'
if (this.$te(message) && this.$t(message) !== '') {
message = this.$t(message)
}
this.showToast({
message: message
})
break break
} */ }
case 'channel': { case 'channel': {
const channelId = url.pathname.match(/\/(?:c\/|channel\/)?([^/?&#]+).*$/)[1] const channelId = url.pathname.match(channelPattern)[1]
if (!channelId) { if (!channelId) {
return throw new Error('Channel: could not extract id')
} }
v.$router.push({ v.$router.push({
@ -390,10 +410,15 @@ export default Vue.extend({
} }
default: { default: {
if (typeof (shell) !== 'undefined') { // Unknown URL type
shell.openExternal(href) let message = 'Unknown YouTube url type, cannot be opened in app'
if (this.$te(message) && this.$t(message) !== '') {
message = this.$t(message)
} }
break
this.showToast({
message: message
})
} }
} }
} }
@ -415,6 +440,10 @@ export default Vue.extend({
window.onbeforeunload = (e) => { window.onbeforeunload = (e) => {
electron.ipcRenderer.send('setBounds') electron.ipcRenderer.send('setBounds')
} }
} },
...mapActions([
'showToast'
])
} }
}) })

View File

@ -586,6 +586,8 @@ This video is unavailable because of missing formats. This can happen due to cou
video is unavailable because of missing formats. This can happen due to country video is unavailable because of missing formats. This can happen due to country
unavailability. unavailability.
Subscriptions have not yet been implemented: Subscriptions have not yet been implemented Subscriptions have not yet been implemented: Subscriptions have not yet been implemented
Unknown YouTube url type, cannot be opened in app: Unknown YouTube url type, cannot be opened in app
Hashtags have not yet been implemented, try again later: Hashtags have not yet been implemented, try again later
Loop is now disabled: Loop is now disabled Loop is now disabled: Loop is now disabled
Loop is now enabled: Loop is now enabled Loop is now enabled: Loop is now enabled
Shuffle is now disabled: Shuffle is now disabled Shuffle is now disabled: Shuffle is now disabled