FreeTube/src/renderer/components/watch-video-comments/watch-video-comments.js

354 lines
11 KiB
JavaScript
Raw Normal View History

2020-02-16 19:30:00 +01:00
import Vue from 'vue'
2020-08-05 05:44:34 +02:00
import { mapActions } from 'vuex'
2020-02-16 19:30:00 +01:00
import FtCard from '../ft-card/ft-card.vue'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
2020-10-05 00:15:06 +02:00
import FtSelect from '../../components/ft-select/ft-select.vue'
import FtTimestampCatcher from '../../components/ft-timestamp-catcher/ft-timestamp-catcher.vue'
import autolinker from 'autolinker'
import ytcm from '@freetube/yt-comment-scraper'
import { copyToClipboard, showToast, toLocalePublicationString } from '../../helpers/utils'
2020-10-05 00:15:06 +02:00
2020-02-16 19:30:00 +01:00
export default Vue.extend({
name: 'WatchVideoComments',
components: {
'ft-card': FtCard,
'ft-loader': FtLoader,
2020-10-05 00:15:06 +02:00
'ft-select': FtSelect,
'ft-timestamp-catcher': FtTimestampCatcher
2020-02-16 19:30:00 +01:00
},
props: {
id: {
type: String,
required: true
},
channelName: {
type: String,
required: true
},
channelThumbnail: {
type: String,
required: true
2020-02-16 19:30:00 +01:00
}
},
data: function () {
return {
isLoading: false,
showComments: false,
2020-09-26 17:43:01 +02:00
commentScraper: null,
2020-02-16 19:30:00 +01:00
nextPageToken: null,
commentData: [],
sortNewest: false,
commentProcess: null,
sortingChanged: false
2020-02-16 19:30:00 +01:00
}
},
computed: {
backendPreference: function () {
return this.$store.getters.getBackendPreference
},
backendFallback: function () {
return this.$store.getters.getBackendFallback
},
currentInvidiousInstance: function () {
return this.$store.getters.getCurrentInvidiousInstance
2020-10-05 00:15:06 +02:00
},
hideCommentLikes: function () {
return this.$store.getters.getHideCommentLikes
},
2020-10-05 00:15:06 +02:00
sortNames: function () {
return [
this.$t('Comments.Top comments'),
this.$t('Comments.Newest first')
]
},
sortValues: function () {
return [
'top',
'newest'
]
},
currentSortValue: function () {
return (this.sortNewest) ? 'newest' : 'top'
2020-02-16 19:30:00 +01:00
}
},
beforeDestroy: function () {
if (this.commentProcess !== null) {
this.commentProcess.send('end')
}
},
2020-02-16 19:30:00 +01:00
methods: {
2020-10-05 00:15:06 +02:00
onTimestamp: function (timestamp) {
this.$emit('timestamp-event', timestamp)
},
2020-10-05 00:15:06 +02:00
handleSortChange: function (sortType) {
this.sortNewest = !this.sortNewest
switch (this.backendPreference) {
case 'local':
2021-01-20 16:54:44 +01:00
this.isLoading = true
this.commentData = []
this.nextPageToken = undefined
this.getCommentDataLocal({
videoId: this.id,
setCookie: false,
sortByNewest: this.sortNewest,
continuation: this.nextPageToken ? this.nextPageToken : undefined
})
break
case 'invidious':
this.isLoading = true
this.commentData = []
this.getCommentDataInvidious()
break
}
2020-10-05 00:15:06 +02:00
},
getCommentData: function () {
2020-02-16 19:30:00 +01:00
this.isLoading = true
switch (this.backendPreference) {
case 'local':
this.getCommentDataLocal({
videoId: this.id,
setCookie: false,
sortByNewest: this.sortNewest,
continuation: this.nextPageToken ? this.nextPageToken : undefined
})
2020-02-16 19:30:00 +01:00
break
case 'invidious':
this.getCommentDataInvidious()
2020-02-16 19:30:00 +01:00
break
}
},
getMoreComments: function () {
if (this.commentData.length === 0 || this.nextPageToken === null || typeof this.nextPageToken === 'undefined') {
2022-10-14 07:59:49 +02:00
showToast(this.$t('Comments.There are no more comments for this video'))
} else {
this.getCommentData()
}
},
toggleCommentReplies: function (index) {
if (this.commentData[index].showReplies || this.commentData[index].replies.length > 0) {
this.commentData[index].showReplies = !this.commentData[index].showReplies
} else {
this.getCommentReplies(index)
}
},
2020-02-16 19:30:00 +01:00
getCommentReplies: function (index) {
switch (this.commentData[index].dataType) {
case 'local':
this.getCommentRepliesLocal({
videoId: this.id,
setCookie: false,
sortByNewest: this.sortNewest,
2021-06-13 22:08:12 +02:00
replyToken: this.commentData[index].replyToken,
index: index
})
2020-02-16 19:30:00 +01:00
break
case 'invidious':
this.getCommentRepliesInvidious(index)
2020-02-16 19:30:00 +01:00
break
}
},
getCommentDataLocal: function (payload) {
2021-01-20 16:54:44 +01:00
ytcm.getComments(payload).then((response) => {
this.parseLocalCommentData(response, null)
}).catch((err) => {
console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)')
2022-10-14 07:59:49 +02:00
showToast(`${errorMessage}: ${err}`, 10000, () => {
copyToClipboard(err)
2021-01-20 16:54:44 +01:00
})
if (this.backendFallback && this.backendPreference === 'local') {
2022-10-14 07:59:49 +02:00
showToast(this.$t('Falling back to Invidious API'))
this.getCommentDataInvidious()
} else {
this.isLoading = false
2021-01-20 16:54:44 +01:00
}
})
},
getCommentRepliesLocal: function (payload) {
2022-10-14 07:59:49 +02:00
showToast(this.$t('Comments.Getting comment replies, please wait'))
2021-06-13 22:08:12 +02:00
ytcm.getCommentReplies(payload).then((response) => {
this.parseLocalCommentData(response, payload.index)
2021-01-20 16:54:44 +01:00
}).catch((err) => {
console.error(err)
2021-01-20 16:54:44 +01:00
const errorMessage = this.$t('Local API Error (Click to copy)')
2022-10-14 07:59:49 +02:00
showToast(`${errorMessage}: ${err}`, 10000, () => {
copyToClipboard(err)
2021-01-20 16:54:44 +01:00
})
if (this.backendFallback && this.backendPreference === 'local') {
2022-10-14 07:59:49 +02:00
showToast(this.$t('Falling back to Invidious API'))
this.getCommentDataInvidious()
2021-01-20 16:54:44 +01:00
} else {
this.isLoading = false
}
})
2020-02-16 19:30:00 +01:00
},
parseLocalCommentData: function (response, index = null) {
const commentData = response.comments.map((comment) => {
comment.authorLink = comment.authorId
comment.showReplies = false
comment.authorThumb = comment.authorThumb[0].url
comment.replies = []
comment.dataType = 'local'
comment.time = toLocalePublicationString({
publishText: (comment.time + ' ago')
})
if (this.hideCommentLikes) {
comment.likes = null
}
// strip html tags but keep <br>, <b>, </b> <s>, </s>, <i>, </i>
comment.text = autolinker.link(comment.text.replace(/(<(?!br|\/?(?:b|s|i)>)([^>]+)>)/ig, ''))
if (comment.customEmojis.length > 0) {
comment.customEmojis.forEach(emoji => {
comment.text = comment.text.replace(emoji.text, `<img width="14" height="14" class="commentCustomEmoji" alt="${emoji.text.substring(2, emoji.text.length - 1)}" src="${emoji.emojiThumbnails[0].url}">`)
})
}
return comment
})
if (index !== null) {
2022-02-06 20:38:17 +01:00
if (this.commentData[index].replies.length === 0 || this.commentData[index].replies[this.commentData[index].replies.length - 1].commentId !== commentData[commentData.length - 1].commentId) {
this.commentData[index].replies = this.commentData[index].replies.concat(commentData)
this.commentData[index].replyToken = response.continuation
this.commentData[index].showReplies = true
}
} else {
if (this.sortingChanged) {
this.commentData = []
this.sortingChanged = false
2020-02-16 19:30:00 +01:00
}
this.commentData = this.commentData.concat(commentData)
this.isLoading = false
this.showComments = true
this.nextPageToken = response.continuation
2020-02-16 19:30:00 +01:00
}
},
2020-02-16 19:30:00 +01:00
getCommentDataInvidious: function () {
const payload = {
resource: 'comments',
id: this.id,
params: {
continuation: this.nextPageToken ?? '',
sort_by: this.sortNewest ? 'new' : 'top'
}
}
this.invidiousAPICall(payload).then((response) => {
2020-02-16 19:30:00 +01:00
const commentData = response.comments.map((comment) => {
comment.showReplies = false
comment.authorLink = comment.authorId
comment.authorThumb = comment.authorThumbnails[1].url.replace('https://yt3.ggpht.com', `${this.currentInvidiousInstance}/ggpht/`)
if (this.hideCommentLikes) {
comment.likes = null
} else {
comment.likes = comment.likeCount
}
comment.text = autolinker.link(comment.content.replace(/(<(?!br>)([^>]+)>)/ig, ''))
2020-02-16 19:30:00 +01:00
comment.dataType = 'invidious'
comment.isOwner = comment.authorIsChannelOwner
2020-02-16 19:30:00 +01:00
if (typeof (comment.replies) !== 'undefined' && typeof (comment.replies.replyCount) !== 'undefined') {
comment.numReplies = comment.replies.replyCount
comment.replyContinuation = comment.replies.continuation
} else {
comment.numReplies = 0
comment.replyContinuation = ''
}
comment.replies = []
comment.time = toLocalePublicationString({
publishText: comment.publishedText
})
2020-09-26 17:43:01 +02:00
2020-02-16 19:30:00 +01:00
return comment
})
this.commentData = this.commentData.concat(commentData)
this.nextPageToken = response.continuation
this.isLoading = false
this.showComments = true
2020-02-16 19:30:00 +01:00
}).catch((xhr) => {
console.error(xhr)
const errorMessage = this.$t('Invidious API Error (Click to copy)')
2022-10-14 07:59:49 +02:00
showToast(`${errorMessage}: ${xhr.responseText}`, 10000, () => {
copyToClipboard(xhr.responseText)
})
2020-02-16 19:30:00 +01:00
if (this.backendFallback && this.backendPreference === 'invidious') {
2022-10-14 07:59:49 +02:00
showToast(this.$t('Falling back to local API'))
2020-02-16 19:30:00 +01:00
this.getCommentDataLocal()
} else {
this.isLoading = false
}
})
},
getCommentRepliesInvidious: function (index) {
2022-10-14 07:59:49 +02:00
showToast(this.$t('Comments.Getting comment replies, please wait'))
2020-02-16 19:30:00 +01:00
const payload = {
resource: 'comments',
id: this.id,
params: {
continuation: this.commentData[index].replyContinuation
}
}
this.invidiousAPICall(payload).then((response) => {
2020-02-16 19:30:00 +01:00
const commentData = response.comments.map((comment) => {
comment.showReplies = false
comment.authorLink = comment.authorId
comment.authorThumb = comment.authorThumbnails[1].url.replace('https://yt3.ggpht.com', `${this.currentInvidiousInstance}/ggpht/`)
if (this.hideCommentLikes) {
comment.likes = null
} else {
comment.likes = comment.likeCount
}
comment.text = autolinker.link(comment.content.replace(/(<(?!br>)([^>]+)>)/ig, ''))
2020-09-26 17:43:01 +02:00
comment.time = comment.publishedText
2020-02-16 19:30:00 +01:00
comment.dataType = 'invidious'
comment.numReplies = 0
comment.replyContinuation = ''
comment.replies = []
return comment
})
this.commentData[index].replies = commentData
this.commentData[index].showReplies = true
this.isLoading = false
}).catch((xhr) => {
console.error(xhr)
const errorMessage = this.$t('Invidious API Error (Click to copy)')
2022-10-14 07:59:49 +02:00
showToast(`${errorMessage}: ${xhr.responseText}`, 10000, () => {
copyToClipboard(xhr.responseText)
})
2020-02-16 19:30:00 +01:00
this.isLoading = false
})
2020-08-05 05:44:34 +02:00
},
goToChannel: function (channelId) {
this.$router.push({ path: `/channel/${channelId}` })
},
2020-08-05 05:44:34 +02:00
...mapActions([
'invidiousAPICall'
2020-08-05 05:44:34 +02:00
])
2020-02-16 19:30:00 +01:00
}
})