Added yt-xml2vtt and did the corresponding implementation
This commit is contained in:
parent
1d3915b3d2
commit
4fedadcd75
|
@ -33,10 +33,12 @@ module.exports = {
|
|||
plugins: ['vue'],
|
||||
|
||||
rules: {
|
||||
'vue/no-v-html': "off",
|
||||
'space-before-function-paren': 0,
|
||||
'comma-dangle': 0,
|
||||
'vue/no-v-html': 'off',
|
||||
'no-console': 0,
|
||||
'no-unused-vars': 1,
|
||||
'no-undef': 1,
|
||||
'vue/no-template-key': 1
|
||||
'vue/no-template-key': 1,
|
||||
},
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,7 +37,7 @@
|
|||
"youtube-comments-fetch": "^1.0.1",
|
||||
"youtube-comments-task": "^1.3.14",
|
||||
"youtube-suggest": "^1.1.0",
|
||||
"yt-xml2srt": "^1.1.0",
|
||||
"yt-xml2vtt": "^1.0.0",
|
||||
"ytdl-core": "^2.0.0",
|
||||
"ytpl": "^0.1.20",
|
||||
"ytsr": "^0.1.10"
|
||||
|
@ -73,6 +73,7 @@
|
|||
"fast-glob": "^3.2.2",
|
||||
"file-loader": "^5.1.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"jest": "^25.1.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import xml2vtt from 'yt-xml2vtt'
|
||||
import $ from 'jquery'
|
||||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
||||
|
@ -18,9 +20,9 @@ export default Vue.extend({
|
|||
'watch-video-info': WatchVideoInfo,
|
||||
'watch-video-description': WatchVideoDescription,
|
||||
'watch-video-comments': WatchVideoComments,
|
||||
'watch-video-recommendations': WatchVideoRecommendations
|
||||
'watch-video-recommendations': WatchVideoRecommendations,
|
||||
},
|
||||
data: function () {
|
||||
data: function() {
|
||||
return {
|
||||
isLoading: false,
|
||||
firstLoad: true,
|
||||
|
@ -47,44 +49,46 @@ export default Vue.extend({
|
|||
audioUrl: '',
|
||||
videoSourceList: [],
|
||||
captionSourceList: [],
|
||||
recommendedVideos: []
|
||||
recommendedVideos: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
backendPreference: function () {
|
||||
backendPreference: function() {
|
||||
return this.$store.getters.getBackendPreference
|
||||
},
|
||||
|
||||
backendFallback: function () {
|
||||
backendFallback: function() {
|
||||
return this.$store.getters.getBackendFallback
|
||||
},
|
||||
|
||||
invidiousInstance: function () {
|
||||
invidiousInstance: function() {
|
||||
return this.$store.getters.getInvidiousInstance
|
||||
},
|
||||
|
||||
videoFormatPreference: function () {
|
||||
videoFormatPreference: function() {
|
||||
return this.$store.getters.getVideoFormatPreference
|
||||
},
|
||||
|
||||
videoDashUrl: function () {
|
||||
videoDashUrl: function() {
|
||||
return `${this.invidiousInstance}/api/manifest/dash/id/${this.videoId}.mpd`
|
||||
},
|
||||
|
||||
youtubeNoCookieEmbeddedFrame: function () {
|
||||
youtubeNoCookieEmbeddedFrame: function() {
|
||||
return `<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/${this.videoId}?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>`
|
||||
},
|
||||
|
||||
dashSrc: function () {
|
||||
return [{
|
||||
dashSrc: function() {
|
||||
return [
|
||||
{
|
||||
url: `${this.invidiousInstance}/api/manifest/dash/${this.videoId}.mpd`,
|
||||
type: 'application/dash+xml',
|
||||
label: 'Dash'
|
||||
}]
|
||||
}
|
||||
label: 'Dash',
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route () {
|
||||
$route() {
|
||||
// react to route changes...
|
||||
this.videoId = this.$route.params.id
|
||||
|
||||
|
@ -98,9 +102,9 @@ export default Vue.extend({
|
|||
this.getVideoInformationInvidious(this.videoId)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
},
|
||||
mounted: function() {
|
||||
this.videoId = this.$route.params.id
|
||||
this.videoStoryboardSrc = `${this.invidiousInstance}/api/v1/storyboards/${this.videoId}?height=90`
|
||||
|
||||
|
@ -120,25 +124,29 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
toggleTheatreMode: function () {
|
||||
toggleTheatreMode: function() {
|
||||
this.useTheatreMode = !this.useTheatreMode
|
||||
},
|
||||
|
||||
getVideoInformationLocal: function () {
|
||||
getVideoInformationLocal: function() {
|
||||
if (this.firstLoad) {
|
||||
this.isLoading = true
|
||||
}
|
||||
|
||||
this.$store.dispatch('ytGetVideoInformation', this.videoId).then((result) => {
|
||||
console.log(result)
|
||||
|
||||
this.$store
|
||||
.dispatch('ytGetVideoInformation', this.videoId)
|
||||
.then(result => {
|
||||
this.videoTitle = result.title
|
||||
this.videoViewCount = parseInt(result.player_response.videoDetails.viewCount)
|
||||
this.videoViewCount = parseInt(
|
||||
result.player_response.videoDetails.viewCount,
|
||||
10
|
||||
)
|
||||
this.channelId = result.author.id
|
||||
this.channelName = result.author.name
|
||||
this.channelThumbnail = result.author.avatar
|
||||
this.videoPublished = result.published
|
||||
this.videoDescription = result.player_response.videoDetails.shortDescription
|
||||
this.videoDescription =
|
||||
result.player_response.videoDetails.shortDescription
|
||||
this.recommendedVideos = result.related_videos
|
||||
this.videoSourceList = result.player_response.streamingData.formats
|
||||
this.videoLikeCount = result.likes
|
||||
|
@ -148,48 +156,44 @@ export default Vue.extend({
|
|||
// Uncomment this line if that ever changes.
|
||||
// this.videoStoryboardSrc = result.player_response.storyboards.playerStoryboardSpecRenderer.spec
|
||||
|
||||
this.captionSourceList = result.player_response.captions.playerCaptionsTracklistRenderer.captionTracks
|
||||
this.captionSourceList =
|
||||
result.player_response.captions &&
|
||||
result.player_response.captions.playerCaptionsTracklistRenderer
|
||||
.captionTracks
|
||||
|
||||
if (typeof (this.captionSourceList) !== 'undefined') {
|
||||
this.captionSourceList = this.captionSourceList.map((caption) => {
|
||||
caption.baseUrl = `${this.invidiousInstance}/api/v1/captions/${this.videoId}?label=${encodeURI(caption.name.simpleText)}`
|
||||
if (typeof this.captionSourceList !== 'undefined') {
|
||||
this.captionSourceList = this.captionSourceList.map(caption => {
|
||||
caption.type = 'text/vtt'
|
||||
caption.charset = 'charset=utf-8'
|
||||
caption.dataSource = 'local'
|
||||
|
||||
$.get(caption.baseUrl, response => {
|
||||
xml2vtt
|
||||
.Parse(new XMLSerializer().serializeToString(response))
|
||||
.then(vtt => {
|
||||
caption.baseUrl = `data:${caption.type};${caption.charset},${vtt}`
|
||||
})
|
||||
.catch(err =>
|
||||
console.log(`Error while converting XML to VTT : ${err}`)
|
||||
)
|
||||
}).fail((xhr, textStatus, error) => {
|
||||
console.log(xhr)
|
||||
console.log(textStatus)
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
return caption
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: The response returns the captions of the video, however they're returned
|
||||
// in XML / TTML. I haven't found a way to properly convert this for use.
|
||||
// There may be another URL that we can use to grab an appropriate format as well.
|
||||
// Video.js requires that the captions are returned in .vtt format. The below code
|
||||
// Converts it to .srt which may work, but I can't get the player to accept the data.
|
||||
|
||||
// this.captionSourceList = this.captionSourceList.map((caption) => {
|
||||
// caption.type = 'application/ttml+xml'
|
||||
// caption.dataSource = 'local'
|
||||
//
|
||||
// $.get(caption.baseUrl, (response) => {
|
||||
// console.log('response')
|
||||
// console.log(response)
|
||||
// console.log()
|
||||
// xml2srt.Parse(new XMLSerializer().serializeToString(response))
|
||||
// .then(srt => {
|
||||
// caption.track = srt
|
||||
// }).catch(err => console.log(`Error while converting XML to SRT : ${err}`))
|
||||
// }).fail((xhr, textStatus, error) => {
|
||||
// console.log(xhr)
|
||||
// console.log(textStatus)
|
||||
// console.log(error)
|
||||
// })
|
||||
//
|
||||
// return caption
|
||||
// })
|
||||
|
||||
this.isLoading = false
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'local' && this.backendFallback) {
|
||||
console.log('Error getting data with local backend, falling back to Invidious')
|
||||
console.log(
|
||||
'Error getting data with local backend, falling back to Invidious'
|
||||
)
|
||||
this.getVideoInformationInvidious()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
|
@ -198,12 +202,14 @@ export default Vue.extend({
|
|||
})
|
||||
},
|
||||
|
||||
getVideoInformationInvidious: function () {
|
||||
getVideoInformationInvidious: function() {
|
||||
if (this.firstLoad) {
|
||||
this.isLoading = true
|
||||
}
|
||||
|
||||
this.$store.dispatch('invidiousGetVideoInformation', this.videoId).then((result) => {
|
||||
this.$store
|
||||
.dispatch('invidiousGetVideoInformation', this.videoId)
|
||||
.then(result => {
|
||||
console.log(result)
|
||||
|
||||
this.videoTitle = result.title
|
||||
|
@ -218,7 +224,7 @@ export default Vue.extend({
|
|||
this.videoDescriptionHtml = result.descriptionHtml
|
||||
this.recommendedVideos = result.recommendedVideos
|
||||
this.videoSourceList = result.formatStreams.reverse()
|
||||
this.captionSourceList = result.captions.map((caption) => {
|
||||
this.captionSourceList = result.captions.map(caption => {
|
||||
caption.url = this.invidiousInstance + caption.url
|
||||
caption.type = ''
|
||||
caption.dataSource = 'invidious'
|
||||
|
@ -226,10 +232,13 @@ export default Vue.extend({
|
|||
})
|
||||
|
||||
this.isLoading = false
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'invidious' && this.backendFallback) {
|
||||
console.log('Error getting data with Invidious, falling back to local backend')
|
||||
console.log(
|
||||
'Error getting data with Invidious, falling back to local backend'
|
||||
)
|
||||
this.getVideoInformationLocal()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
|
@ -238,7 +247,7 @@ export default Vue.extend({
|
|||
})
|
||||
},
|
||||
|
||||
enableDashFormat: function () {
|
||||
enableDashFormat: function() {
|
||||
if (this.activeFormat === 'dash') {
|
||||
return
|
||||
}
|
||||
|
@ -246,10 +255,12 @@ export default Vue.extend({
|
|||
this.activeFormat = 'dash'
|
||||
this.hidePlayer = true
|
||||
|
||||
setTimeout(() => { this.hidePlayer = false }, 100)
|
||||
setTimeout(() => {
|
||||
this.hidePlayer = false
|
||||
}, 100)
|
||||
},
|
||||
|
||||
enableLegacyFormat: function () {
|
||||
enableLegacyFormat: function() {
|
||||
if (this.activeFormat === 'legacy') {
|
||||
return
|
||||
}
|
||||
|
@ -262,16 +273,18 @@ export default Vue.extend({
|
|||
}, 100)
|
||||
},
|
||||
|
||||
handleVideoError: function (error) {
|
||||
handleVideoError: function(error) {
|
||||
console.log(error)
|
||||
if (error.code === 4) {
|
||||
if (this.activeFormat === 'dash') {
|
||||
console.log('Unable to play dash formats. Reverting to legacy formats...')
|
||||
console.log(
|
||||
'Unable to play dash formats. Reverting to legacy formats...'
|
||||
)
|
||||
this.enableLegacyFormat()
|
||||
} else {
|
||||
this.enableDashFormat()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue