Support for the 't' parameter in links (#1090)

This allows users to specify the timestamp of a video (in seconds)
- by inputting a link into the search bar
- by making use of the protocol link (freetube://), p.e in a browser
This commit is contained in:
Svallinn 2021-03-06 19:03:40 +00:00 committed by GitHub
parent 1452e361a1
commit 7eda649293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 20 deletions

View File

@ -284,10 +284,11 @@ export default Vue.extend({
const v = this const v = this
electron.ipcRenderer.on('openUrl', function (event, url) { electron.ipcRenderer.on('openUrl', function (event, url) {
if (url) { if (url) {
v.$store.dispatch('getVideoIdFromUrl', url).then((result) => { v.$store.dispatch('getVideoParamsFromUrl', url).then(({ videoId, timestamp }) => {
if (result) { if (videoId) {
v.$router.push({ v.$router.push({
path: `/watch/${result}` path: `/watch/${videoId}`,
query: timestamp ? { timestamp } : {}
}) })
} }
}) })

View File

@ -89,14 +89,15 @@ export default Vue.extend({
searchInput.blur() searchInput.blur()
} }
const videoId = await this.$store.dispatch('getVideoIdFromUrl', query) const { videoId, timestamp } = await this.$store.dispatch('getVideoParamsFromUrl', query)
const playlistId = await this.$store.dispatch('getPlaylistIdFromUrl', query) const playlistId = await this.$store.dispatch('getPlaylistIdFromUrl', query)
console.log(playlistId) console.log(playlistId)
if (videoId) { if (videoId) {
this.$router.push({ this.$router.push({
path: `/watch/${videoId}` path: `/watch/${videoId}`,
query: timestamp ? { timestamp } : {}
}) })
} else if (playlistId) { } else if (playlistId) {
this.$router.push({ this.$router.push({

View File

@ -190,43 +190,53 @@ const actions = {
return date.getTime() - timeSpan return date.getTime() - timeSpan
}, },
getVideoIdFromUrl (_, url) { getVideoParamsFromUrl (_, url) {
/** @type {URL} */ /** @type {URL} */
let urlObject let urlObject
const paramsObject = { videoId: null, timestamp: null }
try { try {
urlObject = new URL(url) urlObject = new URL(url)
} catch (e) { } catch (e) {
return false return paramsObject
}
function extractParams(videoId) {
paramsObject.videoId = videoId
paramsObject.timestamp = urlObject.searchParams.get('t')
} }
const extractors = [ const extractors = [
// anything with /watch?v= // anything with /watch?v=
function() { function() {
if (urlObject.pathname === '/watch' && urlObject.searchParams.has('v')) { if (urlObject.pathname === '/watch' && urlObject.searchParams.has('v')) {
return urlObject.searchParams.get('v') extractParams(urlObject.searchParams.get('v'))
return paramsObject
} }
}, },
// youtu.be // youtu.be
function() { function() {
if (urlObject.host === 'youtu.be' && urlObject.pathname.match(/^\/[A-Za-z0-9_-]+$/)) { if (urlObject.host === 'youtu.be' && urlObject.pathname.match(/^\/[A-Za-z0-9_-]+$/)) {
return urlObject.pathname.slice(1) extractParams(urlObject.pathname.slice(1))
return paramsObject
} }
}, },
// youtube.com/embed // youtube.com/embed
function() { function() {
if (urlObject.pathname.match(/^\/embed\/[A-Za-z0-9_-]+$/)) { if (urlObject.pathname.match(/^\/embed\/[A-Za-z0-9_-]+$/)) {
return urlObject.pathname.replace('/embed/', '') extractParams(urlObject.pathname.replace('/embed/', ''))
return paramsObject
} }
}, },
// cloudtube // cloudtube
function() { function() {
if (urlObject.host.match(/^cadence\.(gq|moe)$/) && urlObject.pathname.match(/^\/cloudtube\/video\/[A-Za-z0-9_-]+$/)) { if (urlObject.host.match(/^cadence\.(gq|moe)$/) && urlObject.pathname.match(/^\/cloudtube\/video\/[A-Za-z0-9_-]+$/)) {
return urlObject.pathname.slice('/cloudtube/video/'.length) extractParams(urlObject.pathname.slice('/cloudtube/video/'.length))
return paramsObject
} }
} }
] ]
return extractors.reduce((a, c) => a || c(), null) || false return extractors.reduce((a, c) => a || c(), null) || paramsObject
}, },
getPlaylistIdFromUrl (_, url) { getPlaylistIdFromUrl (_, url) {

View File

@ -74,6 +74,7 @@ export default Vue.extend({
downloadLinks: [], downloadLinks: [],
watchingPlaylist: false, watchingPlaylist: false,
playlistId: '', playlistId: '',
timestamp: null,
playNextTimeout: null playNextTimeout: null
} }
}, },
@ -156,6 +157,7 @@ export default Vue.extend({
this.downloadLinks = [] this.downloadLinks = []
this.checkIfPlaylist() this.checkIfPlaylist()
this.checkIfTimestamp()
switch (this.backendPreference) { switch (this.backendPreference) {
case 'local': case 'local':
@ -177,6 +179,7 @@ export default Vue.extend({
this.useTheatreMode = this.defaultTheatreMode this.useTheatreMode = this.defaultTheatreMode
this.checkIfPlaylist() this.checkIfPlaylist()
this.checkIfTimestamp()
if (!this.usingElectron) { if (!this.usingElectron) {
this.getVideoInformationInvidious() this.getVideoInformationInvidious()
@ -682,18 +685,32 @@ export default Vue.extend({
console.log(historyIndex) console.log(historyIndex)
if (historyIndex !== -1 && !this.isLive) { if (!this.isLive) {
const watchProgress = this.historyCache[historyIndex].watchProgress if (this.timestamp) {
if (this.timestamp < 0) {
this.$refs.videoPlayer.player.currentTime(0)
} else if (this.timestamp > (this.videoLengthSeconds - 10)) {
this.$refs.videoPlayer.player.currentTime(this.videoLengthSeconds - 10)
} else {
this.$refs.videoPlayer.player.currentTime(this.timestamp)
}
} else if (historyIndex !== -1) {
const watchProgress = this.historyCache[historyIndex].watchProgress
if (watchProgress < (this.videoLengthSeconds - 10)) { if (watchProgress < (this.videoLengthSeconds - 10)) {
this.$refs.videoPlayer.player.currentTime(watchProgress) this.$refs.videoPlayer.player.currentTime(watchProgress)
}
} }
} }
if (this.rememberHistory && historyIndex !== -1) { if (this.rememberHistory) {
this.addToHistory(this.historyCache[historyIndex].watchProgress) if (this.timestamp) {
} else if (this.rememberHistory) { this.addToHistory(this.timestamp)
this.addToHistory(0) } else if (historyIndex !== -1) {
this.addToHistory(this.historyCache[historyIndex].watchProgress)
} else {
this.addToHistory(0)
}
} }
}, },
@ -711,6 +728,16 @@ export default Vue.extend({
} }
}, },
checkIfTimestamp: function () {
if (typeof (this.$route.query) !== 'undefined') {
try {
this.timestamp = parseInt(this.$route.query.timestamp)
} catch {
this.timestamp = null
}
}
},
getLegacyFormats: function () { getLegacyFormats: function () {
this.$store this.$store
.dispatch('ytGetVideoInformation', this.videoId) .dispatch('ytGetVideoInformation', this.videoId)