Add Support for Live Videos and Live Video Chat
This commit is contained in:
parent
8980dc74d2
commit
009174b89b
|
@ -65,17 +65,29 @@ if (isDevMode) {
|
|||
)
|
||||
} else {
|
||||
config.plugins.push(
|
||||
new CopyWebpackPlugin([
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.join(__dirname, '../src/data'),
|
||||
to: path.join(__dirname, '../dist/data'),
|
||||
from: path.join(__dirname, '../static/pwabuilder-sw.js'),
|
||||
to: path.join(__dirname, '../dist/web/pwabuilder-sw.js'),
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../static'),
|
||||
to: path.join(__dirname, '../dist/static'),
|
||||
to: path.join(__dirname, '../dist/web/static'),
|
||||
globOptions: {
|
||||
ignore: ['.*', 'pwabuilder-sw.js'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../_icons'),
|
||||
to: path.join(__dirname, '../dist/web/_icons'),
|
||||
globOptions: {
|
||||
ignore: ['.*'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true,
|
||||
})
|
||||
|
|
|
@ -151,13 +151,29 @@ if (isDevMode) {
|
|||
)
|
||||
} else {
|
||||
config.plugins.push(
|
||||
new CopyWebpackPlugin([
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.join(__dirname, '../static/pwabuilder-sw.js'),
|
||||
to: path.join(__dirname, '../dist/web/pwabuilder-sw.js'),
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../static'),
|
||||
to: path.join(__dirname, '../dist/static'),
|
||||
to: path.join(__dirname, '../dist/web/static'),
|
||||
globOptions: {
|
||||
ignore: ['.*', 'pwabuilder-sw.js'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../_icons'),
|
||||
to: path.join(__dirname, '../dist/web/_icons'),
|
||||
globOptions: {
|
||||
ignore: ['.*'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true,
|
||||
})
|
||||
|
|
|
@ -154,7 +154,8 @@ if (isDevMode) {
|
|||
)
|
||||
} else {
|
||||
config.plugins.push(
|
||||
new CopyWebpackPlugin([
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.join(__dirname, '../static/pwabuilder-sw.js'),
|
||||
to: path.join(__dirname, '../dist/web/pwabuilder-sw.js'),
|
||||
|
@ -162,14 +163,20 @@ if (isDevMode) {
|
|||
{
|
||||
from: path.join(__dirname, '../static'),
|
||||
to: path.join(__dirname, '../dist/web/static'),
|
||||
globOptions: {
|
||||
ignore: ['.*', 'pwabuilder-sw.js'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../_icons'),
|
||||
to: path.join(__dirname, '../dist/web/_icons'),
|
||||
globOptions: {
|
||||
ignore: ['.*'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true,
|
||||
})
|
||||
|
|
|
@ -95,9 +95,9 @@
|
|||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"main": "./dist/main.js",
|
||||
"name": "freetube",
|
||||
"name": "freetube-vue",
|
||||
"private": true,
|
||||
"productName": "FreeTube",
|
||||
"productName": "FreeTube-Vue",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mubaidr/vue-electron-template.git"
|
||||
|
|
|
@ -191,11 +191,7 @@ export default Vue.extend({
|
|||
this.hideViews = true
|
||||
}
|
||||
|
||||
if (typeof (this.data.uploaded_at) !== 'undefined' && this.data.uploaded_at !== null && this.data.uploaded_at.includes('watching')) {
|
||||
const uploadSplit = this.data.uploaded_at.split(' ')
|
||||
this.viewCount = parseInt(uploadSplit[0])
|
||||
this.isLive = true
|
||||
}
|
||||
this.isLive = this.data.live
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -217,7 +217,7 @@ export default Vue.extend({
|
|||
src: this.storyboardSrc
|
||||
})
|
||||
|
||||
if (this.useDash) {
|
||||
if (this.useDash || this.useHls) {
|
||||
this.dataSetup.plugins.httpSourceSelector = {
|
||||
default: 'auto'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.messageContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 18px;
|
||||
color: var(--tertiary-text-color);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.errorIcon {
|
||||
width: 100%;
|
||||
color: var(--tertiary-text-color);
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.enableLiveChat {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.superChatComments {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.superChat {
|
||||
display: inline-block;
|
||||
padding: 1px;
|
||||
padding-right: 10px;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.superChatContent {
|
||||
margin-left: 32px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.superChat .channelThumbnail {
|
||||
margin-top: 3px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.donationAmount {
|
||||
color: var(--text-with-main-color);
|
||||
}
|
||||
|
||||
.openedSuperChat {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
width: 100%;
|
||||
height: 415px;
|
||||
position: absolute;
|
||||
margin-left: -16px;
|
||||
padding-right: 32px;
|
||||
bottom: -15px;
|
||||
cursor: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.openedSuperChat .superChatMessage {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.superChatMessage {
|
||||
width: 90%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
margin-top: 10px;
|
||||
background-color: var(--primary-color);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
-webkit-border-radius: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
.upperSuperChatMessage {
|
||||
margin-top: -15px;
|
||||
width: 100%;
|
||||
height: 55px;
|
||||
background-color: var(--primary-color-hover);
|
||||
}
|
||||
|
||||
.upperSuperChatMessage .channelThumbnail {
|
||||
width: 45px;
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.upperSuperChatMessage .channelName {
|
||||
color: var(--text-with-main-color);
|
||||
opacity: 0.7;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.upperSuperChatMessage .donationAmount {
|
||||
color: var(--text-with-main-color);
|
||||
font-weight: bold;
|
||||
margin-left: 65px;
|
||||
position: relative;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.superChatMessage .chatMessage {
|
||||
color: var(--text-with-main-color);
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.liveChatComments {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.comment .superChatMessage {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.comment .upperSuperChatMessage {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.comment {
|
||||
width: 100%;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
|
||||
.channelThumbnail {
|
||||
width: 25px;
|
||||
float: left;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.chatContent {
|
||||
margin-left: 30px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 2px;
|
||||
font-size: 12px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
color: var(--tertiary-text-color);
|
||||
font-weight: bold;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.member {
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.moderator {
|
||||
color: #2196F3;
|
||||
}
|
||||
|
||||
.owner {
|
||||
margin-right: 2px;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--text-with-main-color);
|
||||
}
|
||||
|
||||
.badgeImage {
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.scrollToBottom {
|
||||
background-color: var(--accent-color);
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
position: absolute;
|
||||
left: 45%;
|
||||
bottom: 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.scrollToBottom:hover {
|
||||
background-color: var(--accent-color-light);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--text-with-accent-color);
|
||||
font-size: 22px;
|
||||
position: relative;
|
||||
left: 0.5rem;
|
||||
top: 0.45rem;
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
import Vue from 'vue'
|
||||
import FtLoader from '../ft-loader/ft-loader.vue'
|
||||
import FtCard from '../ft-card/ft-card.vue'
|
||||
import FtButton from '../ft-button/ft-button.vue'
|
||||
import FtListVideo from '../ft-list-video/ft-list-video.vue'
|
||||
|
||||
import $ from 'jquery'
|
||||
import autolinker from 'autolinker'
|
||||
import { LiveChat } from 'youtube-chat'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'WatchVideoLiveChat',
|
||||
components: {
|
||||
'ft-loader': FtLoader,
|
||||
'ft-card': FtCard,
|
||||
'ft-button': FtButton,
|
||||
'ft-list-video': FtListVideo
|
||||
},
|
||||
props: {
|
||||
videoId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
channelName: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
liveChat: null,
|
||||
isLoading: true,
|
||||
hasError: false,
|
||||
hasEnded: false,
|
||||
showEnableChat: false,
|
||||
errorMessage: '',
|
||||
stayAtBottom: true,
|
||||
showSuperChat: false,
|
||||
showScrollToBottom: false,
|
||||
comments: [],
|
||||
superChatComments: [],
|
||||
superChat: {
|
||||
author: {
|
||||
name: '',
|
||||
thumbnail: ''
|
||||
},
|
||||
message: [
|
||||
''
|
||||
],
|
||||
superChat: {
|
||||
amount: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
usingElectron: function () {
|
||||
return this.$store.getters.getUsingElectron
|
||||
},
|
||||
|
||||
backendPreference: function () {
|
||||
return this.$store.getters.getBackendPreference
|
||||
},
|
||||
|
||||
backendFallback: function () {
|
||||
return this.$store.getters.getBackendFallback
|
||||
},
|
||||
|
||||
chatHeight: function () {
|
||||
if (this.superChatComments.length > 0) {
|
||||
return '390px'
|
||||
} else {
|
||||
return '445px'
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
if (!this.usingElectron) {
|
||||
this.hasError = true
|
||||
this.errorMessage = 'Live Chat is currently not supported in this build.'
|
||||
} else {
|
||||
switch (this.backendPreference) {
|
||||
case 'local':
|
||||
console.log('Getting Chat')
|
||||
this.getLiveChatLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
if (this.backendFallback) {
|
||||
this.getLiveChatLocal()
|
||||
} else {
|
||||
this.hasError = true
|
||||
this.errorMessage = 'Live Chat is currently not supported with the Invidious API. A direct connection to YouTube is required.'
|
||||
this.showEnableChat = true
|
||||
this.isLoading = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enableLiveChat: function () {
|
||||
this.hasError = false
|
||||
this.showEnableChat = false
|
||||
this.isLoading = true
|
||||
this.getLiveChatLocal()
|
||||
},
|
||||
|
||||
getLiveChatLocal: function () {
|
||||
this.liveChat = new LiveChat({ liveId: this.videoId })
|
||||
|
||||
this.isLoading = false
|
||||
|
||||
this.liveChat.on('start', (liveId) => {
|
||||
console.log('Live chat is enabled')
|
||||
this.isLoading = false
|
||||
})
|
||||
|
||||
this.liveChat.on('end', (reason) => {
|
||||
console.log('Live chat has ended')
|
||||
console.log(reason)
|
||||
})
|
||||
|
||||
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()
|
||||
},
|
||||
|
||||
parseLiveChatComment: function (comment) {
|
||||
if (this.hasEnded) {
|
||||
return
|
||||
}
|
||||
|
||||
comment.messageHtml = ''
|
||||
|
||||
comment.message.forEach((text) => {
|
||||
comment.messageHtml = comment.messageHtml + text.text
|
||||
})
|
||||
|
||||
comment.messageHtml = autolinker.link(comment.messageHtml)
|
||||
|
||||
const liveChatComments = $('.liveChatComments')
|
||||
|
||||
if (typeof (liveChatComments.get(0)) === 'undefined' && this.comments.length !== 0) {
|
||||
this.liveChat.stop()
|
||||
return
|
||||
}
|
||||
|
||||
this.comments.push(comment)
|
||||
|
||||
if (typeof (comment.superchat) !== 'undefined') {
|
||||
this.$store.dispatch('getRandomColorClass').then((data) => {
|
||||
comment.superchat.colorClass = data
|
||||
|
||||
this.superChatComments.unshift(comment)
|
||||
|
||||
setTimeout(() => {
|
||||
this.removeFromSuperChat(comment.id)
|
||||
}, 120000)
|
||||
})
|
||||
}
|
||||
|
||||
if (comment.author.name[0] === 'Ge' || comment.author.name[0] === 'Ne') {
|
||||
this.$store.dispatch('getRandomColorClass').then((data) => {
|
||||
comment.superChat = {
|
||||
amount: '$5.00',
|
||||
colorClass: data
|
||||
}
|
||||
|
||||
this.superChatComments.unshift(comment)
|
||||
|
||||
setTimeout(() => {
|
||||
this.removeFromSuperChat(comment.id)
|
||||
}, 120000)
|
||||
})
|
||||
}
|
||||
|
||||
if (this.stayAtBottom) {
|
||||
liveChatComments.animate({ scrollTop: liveChatComments.prop('scrollHeight') })
|
||||
}
|
||||
},
|
||||
|
||||
removeFromSuperChat: function (id) {
|
||||
this.superChatComments = this.superChatComments.filter((comment) => {
|
||||
return comment.id !== id
|
||||
})
|
||||
},
|
||||
|
||||
showSuperChatComment: function (comment) {
|
||||
if (this.superChat.id === comment.id && this.showSuperChat) {
|
||||
this.showSuperChat = false
|
||||
} else {
|
||||
this.superChat = comment
|
||||
this.showSuperChat = true
|
||||
}
|
||||
},
|
||||
|
||||
onScroll: function (event) {
|
||||
const liveChatComments = $('.liveChatComments').get(0)
|
||||
const scrollTop = liveChatComments.scrollTop
|
||||
const scrollHeight = liveChatComments.scrollHeight
|
||||
const clientHeight = liveChatComments.clientHeight
|
||||
if (event.wheelDelta >= 0 && this.stayAtBottom) {
|
||||
$('.liveChatComments').data('animating', 0)
|
||||
this.stayAtBottom = false
|
||||
|
||||
if (liveChatComments.scrollHeight > liveChatComments.clientHeight) {
|
||||
this.showScrollToBottom = true
|
||||
}
|
||||
} else if (event.wheelDelta < 0 && !this.stayAtBottom) {
|
||||
if ((liveChatComments.scrollHeight - liveChatComments.scrollTop) === liveChatComments.clientHeight) {
|
||||
this.scrollToBottom()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
scrollToBottom: function () {
|
||||
const liveChatComments = $('.liveChatComments')
|
||||
liveChatComments.animate({ scrollTop: liveChatComments.prop('scrollHeight') })
|
||||
this.stayAtBottom = true
|
||||
this.showScrollToBottom = false
|
||||
},
|
||||
|
||||
preventDefault: function (event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
}
|
||||
},
|
||||
beforeRouteLeave: function () {
|
||||
this.liveChat.stop()
|
||||
this.hasEnded = true
|
||||
}
|
||||
})
|
|
@ -0,0 +1,198 @@
|
|||
<template>
|
||||
<ft-card class="relative">
|
||||
<ft-loader
|
||||
v-if="isLoading"
|
||||
/>
|
||||
<div
|
||||
v-else-if="hasError"
|
||||
class="messageContainer"
|
||||
>
|
||||
<p
|
||||
class="message"
|
||||
>
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<font-awesome-icon
|
||||
icon="exclamation-circle"
|
||||
class="errorIcon"
|
||||
/>
|
||||
<ft-button
|
||||
label="Enable Live Chat"
|
||||
class="enableLiveChat"
|
||||
@click="enableLiveChat"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="comments.length === 0"
|
||||
class="messageContainer"
|
||||
>
|
||||
<p
|
||||
class="message"
|
||||
>
|
||||
Live chat is enabled. Chat messages will appear here once sent.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="relative"
|
||||
>
|
||||
<h4>Live Chat</h4>
|
||||
<div
|
||||
v-if="superChatComments.length > 0"
|
||||
class="superChatComments"
|
||||
>
|
||||
<div
|
||||
v-for="(comment, index) in superChatComments"
|
||||
:key="index"
|
||||
:style="{ backgroundColor: 'var(--primary-color)' }"
|
||||
class="superChat"
|
||||
:class="comment.superchat.colorClass"
|
||||
@click="showSuperChatComment(comment)"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
<p
|
||||
class="superChatContent"
|
||||
:style="{ color: 'var(--text-with-main-color)' }"
|
||||
>
|
||||
<span
|
||||
class="donationAmount"
|
||||
>
|
||||
{{ comment.superchat.amount }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="openedSuperChat"
|
||||
v-if="showSuperChat"
|
||||
:class="superChat.superchat.colorClass"
|
||||
@click="showSuperChat = false"
|
||||
>
|
||||
<div
|
||||
class="superChatMessage"
|
||||
@click="e => preventDefault(e)"
|
||||
>
|
||||
<div
|
||||
class="upperSuperChatMessage"
|
||||
>
|
||||
<img
|
||||
:src="superChat.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
<p
|
||||
class="channelName"
|
||||
>
|
||||
{{ superChat.author.name }}
|
||||
</p>
|
||||
<p
|
||||
class="donationAmount"
|
||||
>
|
||||
{{ superChat.superchat.amount }}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="chatMessage"
|
||||
v-if="superChat.message.length > 0"
|
||||
v-html="comment.messageHtml"
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="liveChatComments"
|
||||
:style="{ height: chatHeight }"
|
||||
@mousewheel="e => onScroll(e)"
|
||||
>
|
||||
<div v-for="(comment, index) in comments"
|
||||
:key="index"
|
||||
class="comment">
|
||||
<div
|
||||
v-if="typeof (comment.superchat) !== 'undefined'"
|
||||
class="superChatMessage"
|
||||
:class="comment.superchat.colorClass"
|
||||
>
|
||||
<div
|
||||
class="upperSuperChatMessage"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
<p
|
||||
class="channelName"
|
||||
>
|
||||
{{ comment.author.name }}
|
||||
</p>
|
||||
<p
|
||||
class="donationAmount"
|
||||
>
|
||||
{{ comment.superchat.amount }}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="chatMessage"
|
||||
v-if="comment.message.length > 0"
|
||||
v-html="comment.messageHtml"
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
<p
|
||||
class="chatContent"
|
||||
>
|
||||
<span
|
||||
class="channelName"
|
||||
:class="{
|
||||
member: typeof (comment.author.badge) !== 'undefined' || comment.membership,
|
||||
moderator: comment.isOwner,
|
||||
owner: comment.author.name === channelName
|
||||
}"
|
||||
>
|
||||
{{ comment.author.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="typeof (comment.author.badge) !== 'undefined'"
|
||||
class="badge"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.badge.thumbnail.url"
|
||||
:alt="comment.author.badge.thumbnail.alt"
|
||||
:title="comment.author.badge.thumbnail.alt"
|
||||
class="badgeImage"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="chatMessage"
|
||||
v-if="comment.message.length > 0"
|
||||
v-html="comment.messageHtml"
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showScrollToBottom"
|
||||
class="scrollToBottom"
|
||||
@click="scrollToBottom"
|
||||
>
|
||||
<font-awesome-icon
|
||||
class="icon"
|
||||
icon="arrow-down"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ft-card>
|
||||
</template>
|
||||
|
||||
<script src="./watch-video-live-chat.js" />
|
||||
<style scoped src="./watch-video-live-chat.css" />
|
|
@ -8,7 +8,25 @@ const state = {
|
|||
time: '',
|
||||
type: 'all',
|
||||
duration: ''
|
||||
}
|
||||
},
|
||||
colorClasses: [
|
||||
'mainRed',
|
||||
'mainPink',
|
||||
'mainPurple',
|
||||
'mainDeepPurple',
|
||||
'mainIndigo',
|
||||
'mainBlue',
|
||||
'mainLightBlue',
|
||||
'mainCyan',
|
||||
'mainTeal',
|
||||
'mainGreen',
|
||||
'mainLightGreen',
|
||||
'mainLime',
|
||||
'mainYellow',
|
||||
'mainAmber',
|
||||
'mainOrange',
|
||||
'mainDeepOrange'
|
||||
]
|
||||
}
|
||||
|
||||
const getters = {
|
||||
|
@ -29,7 +47,12 @@ const getters = {
|
|||
}
|
||||
}
|
||||
|
||||
const actions = {}
|
||||
const actions = {
|
||||
getRandomColorClass () {
|
||||
const randomInt = Math.floor(Math.random() * state.colorClasses.length)
|
||||
return state.colorClasses[randomInt]
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
toggleSideNav (state) {
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
.watchVideoPlaylist {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 10px;
|
||||
width: 85%;
|
||||
max-width: none;
|
||||
position: static;
|
||||
|
|
|
@ -8,6 +8,7 @@ import FtVideoPlayer from '../../components/ft-video-player/ft-video-player.vue'
|
|||
import WatchVideoInfo from '../../components/watch-video-info/watch-video-info.vue'
|
||||
import WatchVideoDescription from '../../components/watch-video-description/watch-video-description.vue'
|
||||
import WatchVideoComments from '../../components/watch-video-comments/watch-video-comments.vue'
|
||||
import WatchVideoLiveChat from '../../components/watch-video-live-chat/watch-video-live-chat.vue'
|
||||
import WatchVideoPlaylist from '../../components/watch-video-playlist/watch-video-playlist.vue'
|
||||
import WatchVideoRecommendations from '../../components/watch-video-recommendations/watch-video-recommendations.vue'
|
||||
|
||||
|
@ -21,6 +22,7 @@ export default Vue.extend({
|
|||
'watch-video-info': WatchVideoInfo,
|
||||
'watch-video-description': WatchVideoDescription,
|
||||
'watch-video-comments': WatchVideoComments,
|
||||
'watch-video-live-chat': WatchVideoLiveChat,
|
||||
'watch-video-playlist': WatchVideoPlaylist,
|
||||
'watch-video-recommendations': WatchVideoRecommendations,
|
||||
},
|
||||
|
@ -185,16 +187,21 @@ export default Vue.extend({
|
|||
this.isLive = result.player_response.videoDetails.isLive
|
||||
|
||||
if (this.isLive) {
|
||||
this.showLegacyPlayer = false
|
||||
this.showDashPlayer = true
|
||||
this.videoSourceList = [
|
||||
{
|
||||
url: 'https://invidious.snopyta.org/api/manifest/dash/id/EEIk7gwjgIM',
|
||||
type: 'application/dash+xml',
|
||||
this.showLegacyPlayer = true
|
||||
this.showDashPlayer = false
|
||||
|
||||
this.videoSourceList = result.formats.filter((format) => {
|
||||
if (typeof (format.mimeType) !== 'undefined') {
|
||||
return format.mimeType.includes('video/ts')
|
||||
}
|
||||
}).map((format) => {
|
||||
return {
|
||||
url: format.url,
|
||||
type: 'application/x-mpegURL',
|
||||
label: 'Dash',
|
||||
qualityLabel: 'Auto'
|
||||
},
|
||||
]
|
||||
qualityLabel: format.qualityLabel
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.videoSourceList = result.player_response.streamingData.formats
|
||||
}
|
||||
|
@ -271,6 +278,7 @@ export default Vue.extend({
|
|||
this.videoPublished = result.published * 1000
|
||||
this.videoDescriptionHtml = result.descriptionHtml
|
||||
this.recommendedVideos = result.recommendedVideos
|
||||
this.isLive = result.liveNow
|
||||
this.captionSourceList = result.captions.map(caption => {
|
||||
caption.url = this.invidiousInstance + caption.url
|
||||
caption.type = ''
|
||||
|
@ -278,7 +286,35 @@ export default Vue.extend({
|
|||
return caption
|
||||
})
|
||||
|
||||
if (this.forceLocalBackendForLegacy) {
|
||||
if (this.isLive) {
|
||||
this.showLegacyPlayer = true
|
||||
this.showDashPlayer = false
|
||||
this.activeFormat = 'legacy'
|
||||
|
||||
this.videoSourceList = [
|
||||
{
|
||||
url: result.hlsUrl,
|
||||
type: 'application/x-mpegURL',
|
||||
label: 'Dash',
|
||||
qualityLabel: 'Live'
|
||||
}
|
||||
]
|
||||
|
||||
// Grabs the adaptive formats from Invidious. Might be worth making these work.
|
||||
// The type likely needs to be changed in order for these to be played properly.
|
||||
// this.videoSourceList = result.adaptiveFormats.filter((format) => {
|
||||
// if (typeof (format.type) !== 'undefined') {
|
||||
// return format.type.includes('video/mp4')
|
||||
// }
|
||||
// }).map((format) => {
|
||||
// return {
|
||||
// url: format.url,
|
||||
// type: 'application/x-mpegURL',
|
||||
// label: 'Dash',
|
||||
// qualityLabel: format.qualityLabel
|
||||
// }
|
||||
// })
|
||||
} else if (this.forceLocalBackendForLegacy) {
|
||||
this.getLegacyFormats()
|
||||
} else {
|
||||
this.videoSourceList = result.formatStreams.reverse()
|
||||
|
@ -302,8 +338,6 @@ export default Vue.extend({
|
|||
|
||||
checkIfPlaylist: function () {
|
||||
if (typeof (this.$route.query) !== 'undefined') {
|
||||
console.log('defined')
|
||||
console.log(this.$route.query)
|
||||
this.playlistId = this.$route.query.playlistId
|
||||
|
||||
if (typeof (this.playlistId) !== 'undefined') {
|
||||
|
@ -325,7 +359,7 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
enableDashFormat: function () {
|
||||
if (this.activeFormat === 'dash') {
|
||||
if (this.activeFormat === 'dash' || this.isLive) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -361,6 +395,10 @@ export default Vue.extend({
|
|||
|
||||
handleVideoError: function(error) {
|
||||
console.log(error)
|
||||
if (this.isLive) {
|
||||
return
|
||||
}
|
||||
|
||||
if (error.code === 4) {
|
||||
if (this.activeFormat === 'dash') {
|
||||
console.log(
|
||||
|
|
|
@ -41,11 +41,18 @@
|
|||
:class="{ theatreWatchVideo: useTheatreMode }"
|
||||
/>
|
||||
<watch-video-comments
|
||||
v-if="!isLoading"
|
||||
v-if="!isLoading && !isLive"
|
||||
:id="videoId"
|
||||
class="watchVideo"
|
||||
:class="{ theatreWatchVideo: useTheatreMode }"
|
||||
/>
|
||||
<watch-video-live-chat
|
||||
v-if="!isLoading && isLive"
|
||||
:video-id="videoId"
|
||||
:channel-name="channelName"
|
||||
class="watchVideoSideBar watchVideoPlaylist"
|
||||
:class="{ theatrePlaylist: useTheatreMode }"
|
||||
/>
|
||||
<watch-video-playlist
|
||||
v-if="watchingPlaylist"
|
||||
v-show="!isLoading"
|
||||
|
@ -61,8 +68,8 @@
|
|||
class="watchVideoSideBar watchVideoRecommendations"
|
||||
:class="{
|
||||
theatreRecommendations: useTheatreMode,
|
||||
watchVideoRecommendationsLowerCard: watchingPlaylist,
|
||||
watchVideoRecommendationsNoCard: !watchingPlaylist
|
||||
watchVideoRecommendationsLowerCard: watchingPlaylist || isLive,
|
||||
watchVideoRecommendationsNoCard: !watchingPlaylist || !isLive
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue