mirror of
https://github.com/FreeTubeApp/FreeTube
synced 2024-11-29 05:30:13 +01:00
Migrate live chat to YouTube.js (#3054)
This commit is contained in:
parent
7becd36120
commit
ae50ec7205
@ -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/youtube-chat": "^1.1.2",
|
||||
"@freetube/yt-comment-scraper": "^6.2.0",
|
||||
"@silvermine/videojs-quality-selector": "^1.2.5",
|
||||
"autolinker": "^4.0.0",
|
||||
@ -77,7 +76,7 @@
|
||||
"vue-observe-visibility": "^1.0.0",
|
||||
"vue-router": "^3.6.5",
|
||||
"vuex": "^3.6.2",
|
||||
"youtubei.js": "^2.8.0",
|
||||
"youtubei.js": "^2.9.0",
|
||||
"yt-channel-info": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -4,9 +4,8 @@ import FtCard from '../ft-card/ft-card.vue'
|
||||
import FtButton from '../ft-button/ft-button.vue'
|
||||
|
||||
import autolinker from 'autolinker'
|
||||
import { LiveChat } from '@freetube/youtube-chat'
|
||||
import { getRandomColorClass } from '../../helpers/colors'
|
||||
import { stripHTML } from '../../helpers/utils'
|
||||
import { getLocalVideoInfo, parseLocalTextRuns } from '../../helpers/api/local'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'WatchVideoLiveChat',
|
||||
@ -15,23 +14,23 @@ export default Vue.extend({
|
||||
'ft-card': FtCard,
|
||||
'ft-button': FtButton
|
||||
},
|
||||
beforeRouteLeave: function () {
|
||||
this.liveChat.stop()
|
||||
this.hasEnded = true
|
||||
},
|
||||
props: {
|
||||
liveChat: {
|
||||
type: EventTarget,
|
||||
default: null
|
||||
},
|
||||
videoId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
channelName: {
|
||||
channelId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
liveChat: null,
|
||||
liveChatInstance: null,
|
||||
isLoading: true,
|
||||
hasError: false,
|
||||
hasEnded: false,
|
||||
@ -43,15 +42,15 @@ export default Vue.extend({
|
||||
comments: [],
|
||||
superChatComments: [],
|
||||
superChat: {
|
||||
id: '',
|
||||
author: {
|
||||
name: '',
|
||||
thumbnail: ''
|
||||
thumbnailUrl: ''
|
||||
},
|
||||
message: [
|
||||
''
|
||||
],
|
||||
message: '',
|
||||
superChat: {
|
||||
amount: ''
|
||||
amount: '',
|
||||
colorClass: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,14 +72,15 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
hideLiveChat: function () {
|
||||
return this.$store.getters.getHideLiveChat
|
||||
},
|
||||
|
||||
scrollingBehaviour: function () {
|
||||
return this.$store.getters.getDisableSmoothScrolling ? 'auto' : 'smooth'
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
this.hasEnded = true
|
||||
this.liveChatInstance?.stop()
|
||||
this.liveChatInstance = null
|
||||
},
|
||||
created: function () {
|
||||
if (!process.env.IS_ELECTRON) {
|
||||
this.hasError = true
|
||||
@ -88,7 +88,8 @@ export default Vue.extend({
|
||||
} else {
|
||||
switch (this.backendPreference) {
|
||||
case 'local':
|
||||
this.getLiveChatLocal()
|
||||
this.liveChatInstance = this.liveChat
|
||||
this.startLiveChatLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
if (this.backendFallback) {
|
||||
@ -111,95 +112,137 @@ export default Vue.extend({
|
||||
this.getLiveChatLocal()
|
||||
},
|
||||
|
||||
getLiveChatLocal: function () {
|
||||
this.liveChat = new LiveChat({ liveId: this.videoId })
|
||||
getLiveChatLocal: async function () {
|
||||
const videoInfo = await getLocalVideoInfo(this.videoId)
|
||||
this.liveChatInstance = videoInfo.getLiveChat()
|
||||
|
||||
this.isLoading = false
|
||||
|
||||
this.liveChat.on('start', (liveId) => {
|
||||
this.isLoading = false
|
||||
})
|
||||
|
||||
this.liveChat.on('end', (reason) => {
|
||||
console.error('Live chat has ended')
|
||||
console.error(reason)
|
||||
this.hasError = true
|
||||
this.showEnableChat = false
|
||||
this.errorMessage = this.$t('Video["Chat is disabled or the Live Stream has ended."]')
|
||||
})
|
||||
|
||||
this.liveChat.on('error', (err) => {
|
||||
this.hasError = true
|
||||
this.errorMessage = err
|
||||
this.showEnableChat = false
|
||||
})
|
||||
|
||||
this.liveChat.on('comment', (comment) => {
|
||||
this.parseLiveChatComment(comment)
|
||||
})
|
||||
|
||||
this.liveChat.start()
|
||||
this.startLiveChatLocal()
|
||||
},
|
||||
|
||||
parseLiveChatComment: function (comment) {
|
||||
if (this.hasEnded) {
|
||||
return
|
||||
}
|
||||
startLiveChatLocal: function () {
|
||||
this.liveChatInstance.once('start', initialData => {
|
||||
/**
|
||||
* @type {import ('youtubei.js/dist/src/parser/index').LiveChatContinuation}
|
||||
*/
|
||||
const liveChatContinuation = initialData
|
||||
|
||||
comment.messageHtml = ''
|
||||
const actions = liveChatContinuation.actions.filter(action => action.type === 'AddChatItemAction')
|
||||
|
||||
comment.message.forEach((text) => {
|
||||
if (typeof text === 'undefined') return
|
||||
|
||||
if (typeof (text.navigationEndpoint) !== 'undefined') {
|
||||
if (typeof (text.navigationEndpoint.watchEndpoint) !== 'undefined') {
|
||||
const htmlRef = `<a href="https://www.youtube.com/watch?v=${text.navigationEndpoint.watchEndpoint.videoId}">${text.text}</a>`
|
||||
comment.messageHtml = stripHTML(comment.messageHtml) + htmlRef
|
||||
} else {
|
||||
comment.messageHtml = stripHTML(comment.messageHtml + text.text)
|
||||
for (const { item } of actions) {
|
||||
switch (item.type) {
|
||||
case 'LiveChatTextMessage':
|
||||
this.parseLiveChatComment(item)
|
||||
break
|
||||
case 'LiveChatPaidMessage':
|
||||
this.parseLiveChatSuperChat(item)
|
||||
}
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.liveChatComments?.scrollTo({
|
||||
top: this.$refs.liveChatComments.scrollHeight,
|
||||
behavior: 'instant'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
this.liveChatInstance.on('chat-update', action => {
|
||||
if (this.hasEnded) {
|
||||
return
|
||||
}
|
||||
if (action.type === 'AddChatItemAction') {
|
||||
switch (action.item.type) {
|
||||
case 'LiveChatTextMessage':
|
||||
this.parseLiveChatComment(action.item)
|
||||
break
|
||||
case 'LiveChatPaidMessage':
|
||||
this.parseLiveChatSuperChat(action.item)
|
||||
break
|
||||
}
|
||||
} else if (typeof (text.alt) !== 'undefined') {
|
||||
const htmlImg = `<img src="${text.url}" alt="${text.alt}" class="liveChatEmoji" height="24" width="24" />`
|
||||
comment.messageHtml = stripHTML(comment.messageHtml) + htmlImg
|
||||
} else {
|
||||
comment.messageHtml = stripHTML(comment.messageHtml + text.text)
|
||||
}
|
||||
})
|
||||
|
||||
comment.messageHtml = autolinker.link(comment.messageHtml)
|
||||
this.liveChatInstance.once('end', () => {
|
||||
this.hasEnded = true
|
||||
this.liveChatInstance = null
|
||||
})
|
||||
|
||||
if (typeof this.$refs.liveChatComments === 'undefined' && typeof this.$refs.liveChatMessage === 'undefined') {
|
||||
console.error("Can't find chat object. Stopping chat connection")
|
||||
this.liveChat.stop()
|
||||
return
|
||||
this.liveChatInstance.once('error', error => {
|
||||
this.liveChatInstance.stop()
|
||||
this.liveChatInstance = null
|
||||
console.error(error)
|
||||
this.errorMessage = error
|
||||
this.hasError = true
|
||||
this.isLoading = false
|
||||
this.hasEnded = true
|
||||
})
|
||||
|
||||
this.liveChatInstance.start()
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {import('youtubei.js/dist/src/parser/classes/livechat/items/LiveChatTextMessage').default} comment
|
||||
*/
|
||||
parseLiveChatComment: function (comment) {
|
||||
/**
|
||||
* can also be undefined if there is no badge
|
||||
* @type {import('youtubei.js/dist/src/parser/classes/LiveChatAuthorBadge').default}
|
||||
*/
|
||||
const badge = comment.author.badges.find(badge => badge.type === 'LiveChatAuthorBadge' && badge.custom_thumbnail)
|
||||
|
||||
const parsedComment = {
|
||||
message: autolinker.link(parseLocalTextRuns(comment.message.runs, 20)),
|
||||
author: {
|
||||
name: comment.author.name.text,
|
||||
thumbnailUrl: comment.author.thumbnails.at(-1).url,
|
||||
isOwner: comment.author.id === this.channelId,
|
||||
isModerator: comment.author.is_moderator,
|
||||
isMember: !!badge
|
||||
}
|
||||
}
|
||||
|
||||
this.comments.push(comment)
|
||||
|
||||
if (typeof (comment.superchat) !== 'undefined') {
|
||||
comment.superchat.colorClass = getRandomColorClass()
|
||||
|
||||
this.superChatComments.unshift(comment)
|
||||
|
||||
setTimeout(() => {
|
||||
this.removeFromSuperChat(comment.id)
|
||||
}, 120000)
|
||||
if (badge) {
|
||||
parsedComment.badge = {
|
||||
url: badge.custom_thumbnail.at(-1).url,
|
||||
tooltip: badge.tooltip ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
if (comment.author.name[0] === 'Ge' || comment.author.name[0] === 'Ne') {
|
||||
comment.superChat = {
|
||||
amount: '$5.00',
|
||||
this.pushComment(parsedComment)
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {import('youtubei.js/dist/src/parser/classes/livechat/items/LiveChatPaidMessage').default} superChat
|
||||
*/
|
||||
parseLiveChatSuperChat: function (superChat) {
|
||||
const parsedComment = {
|
||||
id: superChat.id,
|
||||
message: autolinker.link(parseLocalTextRuns(superChat.message.runs, 20)),
|
||||
author: {
|
||||
name: superChat.author.name.text,
|
||||
thumbnailUrl: superChat.author.thumbnails[0].url
|
||||
},
|
||||
superChat: {
|
||||
amount: superChat.purchase_amount,
|
||||
colorClass: getRandomColorClass()
|
||||
}
|
||||
|
||||
this.superChatComments.unshift(comment)
|
||||
|
||||
setTimeout(() => {
|
||||
this.removeFromSuperChat(comment.id)
|
||||
}, 120000)
|
||||
}
|
||||
|
||||
if (this.stayAtBottom) {
|
||||
this.superChatComments.unshift(parsedComment)
|
||||
|
||||
setTimeout(() => {
|
||||
this.removeFromSuperChat(parsedComment.id)
|
||||
}, 120000)
|
||||
|
||||
this.pushComment(parsedComment)
|
||||
},
|
||||
|
||||
pushComment: function (comment) {
|
||||
this.comments.push(comment)
|
||||
|
||||
if (!this.isLoading && this.stayAtBottom) {
|
||||
setTimeout(() => {
|
||||
this.$refs.liveChatComments?.scrollTo({
|
||||
top: this.$refs.liveChatComments.scrollHeight,
|
||||
|
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<ft-card
|
||||
v-if="!hideLiveChat"
|
||||
class="relative"
|
||||
>
|
||||
<ft-loader
|
||||
@ -52,7 +51,7 @@
|
||||
:aria-label="$t('Video.Show Super Chat Comment')"
|
||||
:style="{ backgroundColor: 'var(--primary-color)' }"
|
||||
class="superChat"
|
||||
:class="comment.superchat.colorClass"
|
||||
:class="comment.superChat.colorClass"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="showSuperChatComment(comment)"
|
||||
@ -60,7 +59,7 @@
|
||||
@keydown.enter.prevent="showSuperChatComment(comment)"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
:src="comment.author.thumbnailUrl"
|
||||
class="channelThumbnail"
|
||||
alt=""
|
||||
>
|
||||
@ -70,7 +69,7 @@
|
||||
<span
|
||||
class="donationAmount"
|
||||
>
|
||||
{{ comment.superchat.amount }}
|
||||
{{ comment.superChat.amount }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@ -78,7 +77,7 @@
|
||||
<div
|
||||
v-if="showSuperChat"
|
||||
class="openedSuperChat"
|
||||
:class="superChat.superchat.colorClass"
|
||||
:class="superChat.superChat.colorClass"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="showSuperChat = false"
|
||||
@ -93,7 +92,7 @@
|
||||
class="upperSuperChatMessage"
|
||||
>
|
||||
<img
|
||||
:src="superChat.author.thumbnail.url"
|
||||
:src="superChat.author.thumbnailUrl"
|
||||
class="channelThumbnail"
|
||||
alt=""
|
||||
>
|
||||
@ -105,13 +104,12 @@
|
||||
<p
|
||||
class="donationAmount"
|
||||
>
|
||||
{{ superChat.superchat.amount }}
|
||||
{{ superChat.superChat.amount }}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
v-if="superChat.message.length > 0"
|
||||
class="chatMessage"
|
||||
v-html="superChat.messageHtml"
|
||||
v-html="superChat.message"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -125,17 +123,16 @@
|
||||
v-for="(comment, index) in comments"
|
||||
:key="index"
|
||||
class="comment"
|
||||
:class="{ superChatMessage: typeof (comment.superchat) !== 'undefined' }"
|
||||
:class="comment.superChat ? `superChatMessage ${comment.superChat.colorClass}` : ''"
|
||||
>
|
||||
<div
|
||||
v-if="typeof (comment.superchat) !== 'undefined'"
|
||||
:class="comment.superchat.colorClass"
|
||||
<template
|
||||
v-if="comment.superChat"
|
||||
>
|
||||
<div
|
||||
class="upperSuperChatMessage"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
:src="comment.author.thumbnailUrl"
|
||||
class="channelThumbnail"
|
||||
alt=""
|
||||
>
|
||||
@ -147,20 +144,20 @@
|
||||
<p
|
||||
class="donationAmount"
|
||||
>
|
||||
{{ comment.superchat.amount }}
|
||||
{{ comment.superChat.amount }}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
v-if="comment.message.length > 0"
|
||||
v-if="comment.message"
|
||||
class="chatMessage"
|
||||
v-html="comment.messageHtml"
|
||||
v-html="comment.message"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
v-else
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
:src="comment.author.thumbnailUrl"
|
||||
class="channelThumbnail"
|
||||
alt=""
|
||||
>
|
||||
@ -170,28 +167,27 @@
|
||||
<span
|
||||
class="channelName"
|
||||
:class="{
|
||||
member: typeof (comment.author.badge) !== 'undefined' || comment.membership,
|
||||
moderator: comment.isOwner,
|
||||
owner: comment.author.name === channelName
|
||||
member: comment.author.isMember,
|
||||
moderator: comment.author.isModerator,
|
||||
owner: comment.author.isOwner
|
||||
}"
|
||||
>
|
||||
{{ comment.author.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="typeof (comment.author.badge) !== 'undefined'"
|
||||
v-if="comment.author.badge"
|
||||
class="badge"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.badge.thumbnail.url"
|
||||
:src="comment.author.badge.url"
|
||||
alt=""
|
||||
:title="comment.author.badge.thumbnail.alt"
|
||||
:title="comment.author.badge.tooltip"
|
||||
class="badgeImage"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-if="comment.message.length > 0"
|
||||
class="chatMessage"
|
||||
v-html="comment.messageHtml"
|
||||
v-html="comment.message"
|
||||
/>
|
||||
</p>
|
||||
</template>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Innertube } from 'youtubei.js'
|
||||
import { ClientType } from 'youtubei.js/dist/src/core/Session'
|
||||
import EmojiRun from 'youtubei.js/dist/src/parser/classes/misc/EmojiRun'
|
||||
import { join } from 'path'
|
||||
|
||||
import { PlayerCache } from './PlayerCache'
|
||||
@ -305,63 +306,89 @@ function convertSearchFilters(filters) {
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {TextRun[]} textRuns
|
||||
* @param {(TextRun|EmojiRun)[]} runs
|
||||
* @param {number} emojiSize
|
||||
*/
|
||||
export function parseLocalTextRuns(textRuns) {
|
||||
if (!Array.isArray(textRuns)) {
|
||||
export function parseLocalTextRuns(runs, emojiSize = 16) {
|
||||
if (!Array.isArray(runs)) {
|
||||
throw new Error('not an array of text runs')
|
||||
}
|
||||
|
||||
const timestampRegex = /^(?:\d+:){1,2}\d+$/
|
||||
const runs = []
|
||||
const parsedRuns = []
|
||||
|
||||
for (const { text, endpoint } of textRuns) {
|
||||
if (endpoint && !text.startsWith('#')) {
|
||||
switch (endpoint.metadata.page_type) {
|
||||
case 'WEB_PAGE_TYPE_WATCH':
|
||||
if (timestampRegex.test(text)) {
|
||||
runs.push(text)
|
||||
} else {
|
||||
runs.push(`https://www.youtube.com${endpoint.metadata.url}`)
|
||||
}
|
||||
break
|
||||
case 'WEB_PAGE_TYPE_CHANNEL':
|
||||
if (text.startsWith('@')) {
|
||||
runs.push(`<a href="https://www.youtube.com/channel/${endpoint.payload.browseId}">${text}</a>`)
|
||||
} else {
|
||||
runs.push(`https://www.youtube.com${endpoint.metadata.url}`)
|
||||
}
|
||||
break
|
||||
case 'WEB_PAGE_TYPE_PLAYLIST':
|
||||
runs.push(`https://www.youtube.com${endpoint.metadata.url}`)
|
||||
break
|
||||
case 'WEB_PAGE_TYPE_UNKNOWN':
|
||||
default: {
|
||||
const url = new URL(endpoint.payload.url)
|
||||
if (url.hostname === 'www.youtube.com' && url.pathname === '/redirect' && url.searchParams.has('q')) {
|
||||
// remove utm tracking parameters
|
||||
const realURL = new URL(url.searchParams.get('q'))
|
||||
for (const run of runs) {
|
||||
if (run instanceof EmojiRun) {
|
||||
const { emoji, text } = run
|
||||
|
||||
realURL.searchParams.delete('utm_source')
|
||||
realURL.searchParams.delete('utm_medium')
|
||||
realURL.searchParams.delete('utm_campaign')
|
||||
realURL.searchParams.delete('utm_term')
|
||||
realURL.searchParams.delete('utm_content')
|
||||
let altText
|
||||
|
||||
runs.push(realURL.toString())
|
||||
} else {
|
||||
// this is probably a special YouTube URL like http://www.youtube.com/approachingnirvana
|
||||
runs.push(endpoint.payload.url)
|
||||
}
|
||||
break
|
||||
if (emoji.is_custom) {
|
||||
if (emoji.shortcuts.length > 0) {
|
||||
altText = emoji.shortcuts[0]
|
||||
} else if (emoji.search_terms.length > 0) {
|
||||
altText = emoji.search_terms.join(', ')
|
||||
} else {
|
||||
altText = 'Custom emoji'
|
||||
}
|
||||
} else {
|
||||
altText = text
|
||||
}
|
||||
|
||||
// lazy load the emoji image so it doesn't delay rendering of the text
|
||||
// by defining a height and width, that space is reserved until the image is loaded
|
||||
// that way we avoid layout shifts when it loads
|
||||
parsedRuns.push(`<img src="${emoji.image[0].url}" alt="${altText}" width="${emojiSize}" height="${emojiSize}" loading="lazy" style="vertical-align: middle">`)
|
||||
} else {
|
||||
runs.push(text)
|
||||
const { text, endpoint } = run
|
||||
|
||||
if (endpoint && !text.startsWith('#')) {
|
||||
switch (endpoint.metadata.page_type) {
|
||||
case 'WEB_PAGE_TYPE_WATCH':
|
||||
if (timestampRegex.test(text)) {
|
||||
parsedRuns.push(text)
|
||||
} else {
|
||||
parsedRuns.push(`https://www.youtube.com${endpoint.metadata.url}`)
|
||||
}
|
||||
break
|
||||
case 'WEB_PAGE_TYPE_CHANNEL':
|
||||
if (text.startsWith('@')) {
|
||||
parsedRuns.push(`<a href="https://www.youtube.com/channel/${endpoint.payload.browseId}">${text}</a>`)
|
||||
} else {
|
||||
parsedRuns.push(`https://www.youtube.com${endpoint.metadata.url}`)
|
||||
}
|
||||
break
|
||||
case 'WEB_PAGE_TYPE_PLAYLIST':
|
||||
parsedRuns.push(`https://www.youtube.com${endpoint.metadata.url}`)
|
||||
break
|
||||
case 'WEB_PAGE_TYPE_UNKNOWN':
|
||||
default: {
|
||||
const url = new URL(endpoint.payload.url)
|
||||
if (url.hostname === 'www.youtube.com' && url.pathname === '/redirect' && url.searchParams.has('q')) {
|
||||
// remove utm tracking parameters
|
||||
const realURL = new URL(url.searchParams.get('q'))
|
||||
|
||||
realURL.searchParams.delete('utm_source')
|
||||
realURL.searchParams.delete('utm_medium')
|
||||
realURL.searchParams.delete('utm_campaign')
|
||||
realURL.searchParams.delete('utm_term')
|
||||
realURL.searchParams.delete('utm_content')
|
||||
|
||||
parsedRuns.push(realURL.toString())
|
||||
} else {
|
||||
// this is probably a special YouTube URL like http://www.youtube.com/approachingnirvana
|
||||
parsedRuns.push(endpoint.payload.url)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parsedRuns.push(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return runs.join('')
|
||||
return parsedRuns.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,6 +59,7 @@ export default Vue.extend({
|
||||
hidePlayer: false,
|
||||
isFamilyFriendly: false,
|
||||
isLive: false,
|
||||
liveChat: null,
|
||||
isLiveContent: false,
|
||||
isUpcoming: false,
|
||||
upcomingTimestamp: null,
|
||||
@ -304,7 +305,7 @@ export default Vue.extend({
|
||||
channelId: this.channelId
|
||||
})
|
||||
|
||||
this.videoPublished = new Date(result.primary_info.published.text.replace('-', '/')).getTime()
|
||||
this.videoPublished = new Date(result.page[0].microformat.publish_date.replace('-', '/')).getTime()
|
||||
|
||||
if (result.secondary_info?.description.runs) {
|
||||
try {
|
||||
@ -410,6 +411,12 @@ export default Vue.extend({
|
||||
|
||||
this.videoChapters = chapters
|
||||
|
||||
if (!this.hideLiveChat && this.isLive && this.isLiveContent && result.livechat) {
|
||||
this.liveChat = result.getLiveChat()
|
||||
} else {
|
||||
this.liveChat = null
|
||||
}
|
||||
|
||||
// the bypassed result is missing some of the info that we extract in the code above
|
||||
// so we only overwrite the result here
|
||||
// we need the bypassed result for the streaming data and the subtitles
|
||||
|
@ -153,9 +153,10 @@
|
||||
class="sidebarArea"
|
||||
>
|
||||
<watch-video-live-chat
|
||||
v-if="!isLoading && isLive"
|
||||
v-if="!isLoading && !hideLiveChat && isLive"
|
||||
:live-chat="liveChat"
|
||||
:video-id="videoId"
|
||||
:channel-name="channelName"
|
||||
:channel-id="channelId"
|
||||
class="watchVideoSideBar watchVideoPlaylist"
|
||||
:class="{ theatrePlaylist: useTheatreMode }"
|
||||
/>
|
||||
|
30
yarn.lock
30
yarn.lock
@ -1058,14 +1058,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.9.tgz#7df35818135be54fd87568f16ec7b998a3d0cce8"
|
||||
integrity sha512-tUmO92PFHbLOplitjHNBVGMJm6S57vp16tBXJVPKSI/6CfjrgLycqKxEpC6f7qsOqUdoXs5nIv4HLUfrOMHzuw==
|
||||
|
||||
"@freetube/youtube-chat@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@freetube/youtube-chat/-/youtube-chat-1.1.2.tgz#29e8e1fc94a278e2da38c1241fb611a744a3a0a6"
|
||||
integrity sha512-VT/00nshjwf5WC7cZ9EV5lGMo83DDyC0XYnoNwmSk34sWCco8h8/EXKnCDTG82clZrS2mJHYlYRfXN/6FWd6ig==
|
||||
dependencies:
|
||||
axios "^0.21.1"
|
||||
tslib "^1.11.1"
|
||||
|
||||
"@freetube/yt-comment-scraper@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@freetube/yt-comment-scraper/-/yt-comment-scraper-6.2.0.tgz#ed11d65111d03076ff842eb9c3eb25413f8632ab"
|
||||
@ -2078,13 +2070,6 @@ aws4@^1.8.0:
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.4"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^0.27.2:
|
||||
version "0.27.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
|
||||
@ -4361,7 +4346,7 @@ flatted@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
|
||||
integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
|
||||
|
||||
follow-redirects@^1.0.0, follow-redirects@^1.14.0:
|
||||
follow-redirects@^1.0.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4"
|
||||
integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==
|
||||
@ -8414,11 +8399,6 @@ tsconfig-paths@^3.14.1:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^1.11.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.3, tslib@^2.3.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
@ -9174,10 +9154,10 @@ yocto-queue@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
youtubei.js@^2.8.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-2.8.0.tgz#f0170f390791cde733facf615f3cf0e2470c31de"
|
||||
integrity sha512-UEH3wP1Wmrmnh2wJohhg+rAq94bF2EqGnlnph2z91kZ+I1tQzlZD09Zc7QNqCYV0t2G4Cl2Lu+DqXrdhf1/SoQ==
|
||||
youtubei.js@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-2.9.0.tgz#17426dfb0555169cddede509d50d3db62c102270"
|
||||
integrity sha512-paxfeQGwxGw0oPeKdC96jNalS0OnYQ5xdJY27k3J+vamzVcwX6Ky+idALW6Ej9aUC7FISbchBsEVg0Wa7wgGyA==
|
||||
dependencies:
|
||||
"@protobuf-ts/runtime" "^2.7.0"
|
||||
jintr "^0.3.1"
|
||||
|
Loading…
Reference in New Issue
Block a user