2023-01-18 08:50:02 +01:00
|
|
|
import { defineComponent } from 'vue'
|
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'
|
2020-09-19 18:19:58 +02:00
|
|
|
import FtTimestampCatcher from '../../components/ft-timestamp-catcher/ft-timestamp-catcher.vue'
|
2023-01-24 03:22:54 +01:00
|
|
|
import { copyToClipboard, showToast } from '../../helpers/utils'
|
2023-01-12 07:55:21 +01:00
|
|
|
import { invidiousGetCommentReplies, invidiousGetComments } from '../../helpers/api/invidious'
|
2023-01-24 03:22:54 +01:00
|
|
|
import { getLocalComments, parseLocalComment } from '../../helpers/api/local'
|
2020-10-05 00:15:06 +02:00
|
|
|
|
2023-01-18 08:50:02 +01:00
|
|
|
export default defineComponent({
|
2020-02-16 19:30:00 +01:00
|
|
|
name: 'WatchVideoComments',
|
|
|
|
components: {
|
|
|
|
'ft-card': FtCard,
|
2020-09-19 18:19:58 +02:00
|
|
|
'ft-loader': FtLoader,
|
2020-10-05 00:15:06 +02:00
|
|
|
'ft-select': FtSelect,
|
2020-09-19 18:19:58 +02:00
|
|
|
'ft-timestamp-catcher': FtTimestampCatcher
|
2020-02-16 19:30:00 +01:00
|
|
|
},
|
|
|
|
props: {
|
|
|
|
id: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
2020-10-19 16:01:24 +02:00
|
|
|
},
|
2021-11-06 03:11:21 +01:00
|
|
|
channelName: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
|
|
|
},
|
2020-10-19 16:01:24 +02:00
|
|
|
channelThumbnail: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
2023-04-27 03:56:42 +02:00
|
|
|
},
|
|
|
|
videoPlayerReady: {
|
|
|
|
type: Boolean,
|
|
|
|
required: true
|
|
|
|
},
|
2023-05-22 23:37:59 +02:00
|
|
|
forceState: {
|
|
|
|
type: String,
|
|
|
|
default: null,
|
|
|
|
},
|
2020-02-16 19:30:00 +01:00
|
|
|
},
|
|
|
|
data: function () {
|
|
|
|
return {
|
|
|
|
isLoading: false,
|
2020-02-18 21:59:01 +01:00
|
|
|
showComments: false,
|
2020-02-16 19:30:00 +01:00
|
|
|
nextPageToken: null,
|
2020-10-05 19:52:26 +02:00
|
|
|
commentData: [],
|
2023-04-27 03:56:42 +02:00
|
|
|
sortNewest: false,
|
2020-02-16 19:30:00 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
backendPreference: function () {
|
|
|
|
return this.$store.getters.getBackendPreference
|
|
|
|
},
|
|
|
|
|
|
|
|
backendFallback: function () {
|
|
|
|
return this.$store.getters.getBackendFallback
|
|
|
|
},
|
|
|
|
|
2020-10-06 04:27:32 +02:00
|
|
|
hideCommentLikes: function () {
|
|
|
|
return this.$store.getters.getHideCommentLikes
|
|
|
|
},
|
2020-10-05 00:15:06 +02:00
|
|
|
|
2023-08-30 02:08:55 +02:00
|
|
|
hideCommentPhotos: function () {
|
|
|
|
return this.$store.getters.getHideCommentPhotos
|
|
|
|
},
|
|
|
|
|
2023-04-27 03:56:42 +02:00
|
|
|
commentAutoLoadEnabled: function () {
|
|
|
|
return this.$store.getters.getCommentAutoLoadEnabled
|
|
|
|
},
|
|
|
|
|
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'
|
|
|
|
]
|
2020-10-05 19:52:26 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
currentSortValue: function () {
|
|
|
|
return (this.sortNewest) ? 'newest' : 'top'
|
2023-04-27 03:56:42 +02:00
|
|
|
},
|
|
|
|
|
2023-08-30 02:08:55 +02:00
|
|
|
observeVisibilityOptions: function () {
|
2023-04-27 03:56:42 +02:00
|
|
|
if (!this.commentAutoLoadEnabled) { return false }
|
|
|
|
if (!this.videoPlayerReady) { return false }
|
|
|
|
|
|
|
|
return {
|
|
|
|
callback: (isVisible, _entry) => {
|
|
|
|
// This is also fired when **hidden**
|
|
|
|
// No point doing anything if not visible
|
|
|
|
if (!isVisible) { return }
|
|
|
|
// It's possible the comments are being loaded/already loaded
|
|
|
|
if (this.canPerformInitialCommentLoading) {
|
|
|
|
this.getCommentData()
|
|
|
|
} else if (this.canPerformMoreCommentLoading) {
|
|
|
|
this.getMoreComments()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
intersection: {
|
|
|
|
// Only when it intersects with N% above bottom
|
|
|
|
rootMargin: '0% 0% 0% 0%',
|
|
|
|
},
|
|
|
|
// Callback responsible for loading multiple comment pages
|
|
|
|
once: false,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2023-08-30 02:08:55 +02:00
|
|
|
canPerformInitialCommentLoading: function () {
|
2023-04-27 03:56:42 +02:00
|
|
|
return this.commentData.length === 0 && !this.isLoading && !this.showComments
|
|
|
|
},
|
|
|
|
|
2023-08-30 02:08:55 +02:00
|
|
|
canPerformMoreCommentLoading: function () {
|
2023-04-27 03:56:42 +02:00
|
|
|
return this.commentData.length > 0 && !this.isLoading && this.showComments && this.nextPageToken
|
|
|
|
},
|
2020-02-16 19:30:00 +01:00
|
|
|
},
|
2023-05-22 23:37:59 +02:00
|
|
|
mounted: function () {
|
|
|
|
// region No comment detection
|
|
|
|
// For videos without any comment (comment disabled?)
|
|
|
|
// e.g. https://youtu.be/8NBSwDEf8a8
|
|
|
|
//
|
|
|
|
// `comments_entry_point_header` is null probably when comment disabled
|
|
|
|
if (this.forceState === 'noComment') {
|
|
|
|
this.commentData = []
|
|
|
|
this.nextPageToken = null
|
|
|
|
this.isLoading = false
|
|
|
|
this.showComments = true
|
|
|
|
}
|
|
|
|
// endregion No comment detection
|
|
|
|
},
|
2020-02-16 19:30:00 +01:00
|
|
|
methods: {
|
2020-10-05 00:15:06 +02:00
|
|
|
onTimestamp: function (timestamp) {
|
2020-10-04 20:30:54 +02:00
|
|
|
this.$emit('timestamp-event', timestamp)
|
2020-09-19 18:19:58 +02:00
|
|
|
},
|
|
|
|
|
2023-01-24 03:22:54 +01:00
|
|
|
handleSortChange: function () {
|
2020-10-05 19:52:26 +02:00
|
|
|
this.sortNewest = !this.sortNewest
|
2023-01-24 03:22:54 +01:00
|
|
|
this.commentData = []
|
2023-03-14 19:18:48 +01:00
|
|
|
// nextPageToken is reset to ensure first page is get
|
|
|
|
this.nextPageToken = null
|
2023-01-24 03:22:54 +01:00
|
|
|
this.getCommentData()
|
2020-10-05 00:15:06 +02:00
|
|
|
},
|
|
|
|
|
2020-10-15 11:51:11 +02:00
|
|
|
getCommentData: function () {
|
2020-02-16 19:30:00 +01:00
|
|
|
this.isLoading = true
|
2023-01-24 03:22:54 +01:00
|
|
|
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
|
|
|
|
this.getCommentDataInvidious()
|
|
|
|
} else {
|
|
|
|
this.getCommentDataLocal()
|
2020-02-16 19:30:00 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-09-23 00:02:20 +02:00
|
|
|
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'))
|
2020-09-23 00:02:20 +02:00
|
|
|
} else {
|
2023-01-24 03:22:54 +01:00
|
|
|
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
|
|
|
|
this.getCommentDataInvidious()
|
|
|
|
} else {
|
|
|
|
this.getCommentDataLocal(true)
|
|
|
|
}
|
2020-09-23 00:02:20 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-02-15 15:59:35 +01:00
|
|
|
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) {
|
2023-01-24 03:22:54 +01:00
|
|
|
if (process.env.IS_ELECTRON) {
|
|
|
|
switch (this.commentData[index].dataType) {
|
|
|
|
case 'local':
|
|
|
|
this.getCommentRepliesLocal(index)
|
|
|
|
break
|
|
|
|
case 'invidious':
|
|
|
|
this.getCommentRepliesInvidious(index)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.getCommentRepliesInvidious(index)
|
2020-02-16 19:30:00 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2023-01-24 03:22:54 +01:00
|
|
|
getCommentDataLocal: async function (more) {
|
|
|
|
try {
|
2023-05-01 15:12:23 +02:00
|
|
|
/** @type {import('youtubei.js').YT.Comments} */
|
2023-01-24 03:22:54 +01:00
|
|
|
let comments
|
|
|
|
if (more) {
|
|
|
|
comments = await this.nextPageToken.getContinuation()
|
|
|
|
} else {
|
|
|
|
comments = await getLocalComments(this.id, this.sortNewest)
|
|
|
|
}
|
|
|
|
|
|
|
|
const parsedComments = comments.contents
|
|
|
|
.map(commentThread => parseLocalComment(commentThread.comment, commentThread))
|
|
|
|
|
|
|
|
if (more) {
|
|
|
|
this.commentData = this.commentData.concat(parsedComments)
|
|
|
|
} else {
|
|
|
|
this.commentData = parsedComments
|
|
|
|
}
|
|
|
|
|
|
|
|
this.nextPageToken = comments.has_continuation ? comments : null
|
|
|
|
this.isLoading = false
|
|
|
|
this.showComments = true
|
|
|
|
} catch (err) {
|
2022-09-23 03:04:10 +02:00
|
|
|
console.error(err)
|
2021-02-15 15:59:35 +01:00
|
|
|
const errorMessage = this.$t('Local API Error (Click to copy)')
|
2022-10-14 07:59:49 +02:00
|
|
|
showToast(`${errorMessage}: ${err}`, 10000, () => {
|
2022-10-18 10:15:28 +02:00
|
|
|
copyToClipboard(err)
|
2021-01-20 16:54:44 +01:00
|
|
|
})
|
2021-02-15 15:59:35 +01:00
|
|
|
if (this.backendFallback && this.backendPreference === 'local') {
|
2022-10-14 07:59:49 +02:00
|
|
|
showToast(this.$t('Falling back to Invidious API'))
|
2022-10-15 06:01:58 +02:00
|
|
|
this.getCommentDataInvidious()
|
2021-02-15 15:59:35 +01:00
|
|
|
} else {
|
|
|
|
this.isLoading = false
|
2021-01-20 16:54:44 +01:00
|
|
|
}
|
2023-01-24 03:22:54 +01:00
|
|
|
}
|
2021-02-15 15:59:35 +01:00
|
|
|
},
|
|
|
|
|
2023-01-24 03:22:54 +01:00
|
|
|
getCommentRepliesLocal: async function (index) {
|
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
|
|
|
|
2023-01-24 03:22:54 +01:00
|
|
|
try {
|
|
|
|
const comment = this.commentData[index]
|
2023-05-01 15:12:23 +02:00
|
|
|
/** @type {import('youtubei.js').YTNodes.CommentThread} */
|
2023-01-24 03:22:54 +01:00
|
|
|
const commentThread = comment.replyToken
|
|
|
|
|
|
|
|
if (comment.replies.length > 0) {
|
|
|
|
await commentThread.getContinuation()
|
|
|
|
comment.replies = comment.replies.concat(commentThread.replies.map(reply => parseLocalComment(reply)))
|
|
|
|
} else {
|
|
|
|
await commentThread.getReplies()
|
|
|
|
comment.replies = commentThread.replies.map(reply => parseLocalComment(reply))
|
|
|
|
}
|
|
|
|
|
|
|
|
comment.replyToken = commentThread.has_continuation ? commentThread : null
|
|
|
|
comment.showReplies = true
|
|
|
|
} catch (err) {
|
2022-09-23 03:04:10 +02:00
|
|
|
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, () => {
|
2022-10-18 10:15:28 +02:00
|
|
|
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'))
|
2022-10-15 06:01:58 +02:00
|
|
|
this.getCommentDataInvidious()
|
2021-01-20 16:54:44 +01:00
|
|
|
} else {
|
|
|
|
this.isLoading = false
|
|
|
|
}
|
2020-02-16 19:30:00 +01:00
|
|
|
}
|
2021-02-15 15:59:35 +01:00
|
|
|
},
|
2020-02-16 19:30:00 +01:00
|
|
|
|
2022-10-15 06:01:58 +02:00
|
|
|
getCommentDataInvidious: function () {
|
2023-01-12 07:55:21 +01:00
|
|
|
invidiousGetComments({
|
2022-10-15 06:01:58 +02:00
|
|
|
id: this.id,
|
2023-01-12 07:55:21 +01:00
|
|
|
nextPageToken: this.nextPageToken,
|
|
|
|
sortNewest: this.sortNewest
|
|
|
|
}).then(({ response, commentData }) => {
|
2020-02-16 19:30:00 +01:00
|
|
|
this.commentData = this.commentData.concat(commentData)
|
|
|
|
this.nextPageToken = response.continuation
|
|
|
|
this.isLoading = false
|
2020-02-18 21:59:01 +01:00
|
|
|
this.showComments = true
|
2023-05-22 23:37:59 +02:00
|
|
|
}).catch((err) => {
|
|
|
|
// region No comment detection
|
|
|
|
// No comment related info when video info requested earlier in parent component
|
|
|
|
if (err.message.includes('Comments not found')) {
|
|
|
|
// For videos without any comment (comment disabled?)
|
|
|
|
// e.g. https://youtu.be/8NBSwDEf8a8
|
|
|
|
this.commentData = []
|
|
|
|
this.nextPageToken = null
|
|
|
|
this.isLoading = false
|
|
|
|
this.showComments = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// endregion No comment detection
|
|
|
|
|
|
|
|
console.error(err)
|
2020-08-08 04:16:06 +02:00
|
|
|
const errorMessage = this.$t('Invidious API Error (Click to copy)')
|
2023-05-22 23:37:59 +02:00
|
|
|
showToast(`${errorMessage}: ${err}`, 10000, () => {
|
|
|
|
copyToClipboard(err)
|
2020-08-08 04:16:06 +02:00
|
|
|
})
|
2023-01-05 04:53:39 +01:00
|
|
|
if (process.env.IS_ELECTRON && this.backendFallback && this.backendPreference === 'invidious') {
|
2022-10-14 07:59:49 +02:00
|
|
|
showToast(this.$t('Falling back to local API'))
|
2023-01-24 03:22:54 +01:00
|
|
|
this.getCommentDataLocal()
|
2020-02-16 19:30:00 +01:00
|
|
|
} else {
|
|
|
|
this.isLoading = false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
getCommentRepliesInvidious: function (index) {
|
2022-10-14 07:59:49 +02:00
|
|
|
showToast(this.$t('Comments.Getting comment replies, please wait'))
|
2023-01-12 07:55:21 +01:00
|
|
|
const replyToken = this.commentData[index].replyToken
|
|
|
|
invidiousGetCommentReplies({ id: this.id, replyToken: replyToken })
|
|
|
|
.then(({ commentData, continuation }) => {
|
2023-05-06 20:31:06 +02:00
|
|
|
this.commentData[index].replies = this.commentData[index].replies.concat(commentData)
|
2023-01-12 07:55:21 +01:00
|
|
|
this.commentData[index].showReplies = true
|
|
|
|
this.commentData[index].replyToken = continuation
|
|
|
|
this.isLoading = false
|
|
|
|
}).catch((xhr) => {
|
|
|
|
console.error(xhr)
|
|
|
|
const errorMessage = this.$t('Invidious API Error (Click to copy)')
|
|
|
|
showToast(`${errorMessage}: ${xhr.responseText}`, 10000, () => {
|
|
|
|
copyToClipboard(xhr.responseText)
|
|
|
|
})
|
|
|
|
this.isLoading = false
|
2020-08-08 04:16:06 +02:00
|
|
|
})
|
2023-04-27 03:56:42 +02:00
|
|
|
},
|
2020-02-16 19:30:00 +01:00
|
|
|
}
|
|
|
|
})
|