Skip to content

Commit

Permalink
Migrate comments to YouTube.js (#3072)
Browse files Browse the repository at this point in the history
* Migrate comments to YouTube.js

* Various improvements
  • Loading branch information
absidue committed Jan 24, 2023
1 parent cb64004 commit 0145a04
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 162 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@fortawesome/vue-fontawesome": "^2.0.9",
"@freetube/yt-comment-scraper": "^6.2.0",
"@silvermine/videojs-quality-selector": "^1.2.5",
"autolinker": "^4.0.0",
"browserify": "^17.0.0",
Expand Down
188 changes: 68 additions & 120 deletions src/renderer/components/watch-video-comments/watch-video-comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@ import FtCard from '../ft-card/ft-card.vue'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
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,
stripHTML,
toLocalePublicationString
} from '../../helpers/utils'
import { copyToClipboard, showToast } from '../../helpers/utils'
import { invidiousGetCommentReplies, invidiousGetComments } from '../../helpers/api/invidious'
import { getLocalComments, parseLocalComment } from '../../helpers/api/local'

export default defineComponent({
name: 'WatchVideoComments',
Expand Down Expand Up @@ -39,12 +33,9 @@ export default defineComponent({
return {
isLoading: false,
showComments: false,
commentScraper: null,
nextPageToken: null,
commentData: [],
sortNewest: false,
commentProcess: null,
sortingChanged: false
sortNewest: false
}
},
computed: {
Expand Down Expand Up @@ -82,60 +73,35 @@ export default defineComponent({
}
},

beforeDestroy: function () {
if (this.commentProcess !== null) {
this.commentProcess.send('end')
}
},
methods: {
onTimestamp: function (timestamp) {
this.$emit('timestamp-event', timestamp)
},

handleSortChange: function (sortType) {
handleSortChange: function () {
this.sortNewest = !this.sortNewest
switch (this.backendPreference) {
case 'local':
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
}
this.commentData = []
this.getCommentData()
},

getCommentData: function () {
this.isLoading = true
switch (this.backendPreference) {
case 'local':
this.getCommentDataLocal({
videoId: this.id,
setCookie: false,
sortByNewest: this.sortNewest,
continuation: this.nextPageToken ? this.nextPageToken : undefined
})
break
case 'invidious':
this.getCommentDataInvidious()
break
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
this.getCommentDataInvidious()
} else {
this.getCommentDataLocal()
}
},

getMoreComments: function () {
if (this.commentData.length === 0 || this.nextPageToken === null || typeof this.nextPageToken === 'undefined') {
showToast(this.$t('Comments.There are no more comments for this video'))
} else {
this.getCommentData()
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
this.getCommentDataInvidious()
} else {
this.getCommentDataLocal(true)
}
}
},

Expand All @@ -148,26 +114,43 @@ export default defineComponent({
},

getCommentReplies: function (index) {
switch (this.commentData[index].dataType) {
case 'local':
this.getCommentRepliesLocal({
videoId: this.id,
setCookie: false,
sortByNewest: this.sortNewest,
replyToken: this.commentData[index].replyToken,
index: index
})
break
case 'invidious':
this.getCommentRepliesInvidious(index)
break
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)
}
},

getCommentDataLocal: function (payload) {
ytcm.getComments(payload).then((response) => {
this.parseLocalCommentData(response, null)
}).catch((err) => {
getCommentDataLocal: async function (more) {
try {
/** @type {import('youtubei.js/dist/src/parser/youtube/Comments').default} */
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) {
console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)')
showToast(`${errorMessage}: ${err}`, 10000, () => {
Expand All @@ -179,15 +162,28 @@ export default defineComponent({
} else {
this.isLoading = false
}
})
}
},

getCommentRepliesLocal: function (payload) {
getCommentRepliesLocal: async function (index) {
showToast(this.$t('Comments.Getting comment replies, please wait'))

ytcm.getCommentReplies(payload).then((response) => {
this.parseLocalCommentData(response, payload.index)
}).catch((err) => {
try {
const comment = this.commentData[index]
/** @type {import('youtubei.js/dist/src/parser/classes/comments/CommentThread').default} */
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) {
console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)')
showToast(`${errorMessage}: ${err}`, 10000, () => {
Expand All @@ -199,49 +195,6 @@ export default defineComponent({
} else {
this.isLoading = false
}
})
},

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
}

comment.text = autolinker.link(stripHTML(comment.text))
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) {
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
}
this.commentData = this.commentData.concat(commentData)
this.isLoading = false
this.showComments = true
this.nextPageToken = response.continuation
}
},

Expand All @@ -263,12 +216,7 @@ export default defineComponent({
})
if (process.env.IS_ELECTRON && this.backendFallback && this.backendPreference === 'invidious') {
showToast(this.$t('Falling back to local API'))
this.getCommentDataLocal({
videoId: this.id,
setCookie: false,
sortByNewest: this.sortNewest,
continuation: this.nextPageToken ? this.nextPageToken : undefined
})
this.getCommentDataLocal()
} else {
this.isLoading = false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,14 @@
@timestamp-event="onTimestamp"
/>
<p class="commentLikeCount">
<font-awesome-icon
<template
v-if="!hideCommentLikes"
:icon="['fas', 'thumbs-up']"
/>
{{ comment.likes }}
>
<font-awesome-icon
:icon="['fas', 'thumbs-up']"
/>
{{ comment.likes }}
</template>
<span
v-if="comment.isHearted"
class="commentHeartBadge"
Expand Down Expand Up @@ -168,18 +171,15 @@
>
</router-link>
<p class="commentAuthorWrapper">
<span
<router-link
class="commentAuthor"
:class="{
commentOwner: reply.isOwner
}"
:to="`/channel/${reply.authorLink}`"
>
<router-link
:to="`/channel/${reply.authorLink}`"
>
{{ reply.author }}
</router-link>
</span>
{{ reply.author }}
</router-link>
<img
v-if="reply.isMember"
:src="reply.memberIconUrl"
Expand All @@ -196,11 +196,15 @@
@timestamp-event="onTimestamp"
/>
<p class="commentLikeCount">
<font-awesome-icon
<template
v-if="!hideCommentLikes"
:icon="['fas', 'thumbs-up']"
/>
{{ reply.likes }}
>
<font-awesome-icon
v-if="!hideCommentLikes"
:icon="['fas', 'thumbs-up']"
/>
{{ reply.likes }}
</template>
</p>
<p
v-if="reply.numReplies > 0"
Expand Down

0 comments on commit 0145a04

Please sign in to comment.