Nuxt / Vue project using Brightcove Video Player has no sound in ie11 - vue.js

I'm using NUXT / Vue along with brightcove video player which has worked for me in the past with very few issues. I noticed today that my videos are playing but have NO SOUND in ie11. The videos work fine in every other browser I tested excpet ie11.
I do not see any errors in ie11 but there is a few warnings that I am not familiar with that I'm hoping have something to do with the no sound and can give me some direction for a solution:
VIDEOJS: WARN: Invalid playlist STREAM-INF detected. Missing BANDWITH attribute
VIDEOJS: WARN: player.dash() is deprecated. Use player.tech().vhs instead
VIDEOJS: WARN: player.hls is deprecated. Use player.tech().vhs instead
VIDEOJS: WARN: player.vhs is deprecated. Use player.tech().vhs instead
<template>
<div id="video-component">
<div class="container--content__full-width-mobile">
<div class="video-headline font--primary font--bold">
<slot name="videoHeadline"></slot>
</div>
<div
class="container--video-player"
:class="{ 'container--video-player__inline': videoInline }"
>
<div ref="playerElement" :id="playerId" class="video-player"></div>
<div v-if="hasTranscript" class="transcript-accordion bg--primary">
<div
class="accordion"
:key="accordion"
v-for="(row, accordion) in accordionRows"
>
<div
class="accordion-title"
v-on:click="
row.open = !row.open;
rotateIcon = !rotateIcon;
"
>
<div class="l-accordion-title">
<p>Reveal Video Transcript</p>
<i
class="fas fa-chevron-down transition-fast"
:class="{ rotateIcon }"
aria-hidden="true"
></i>
</div>
<!-- /.l-accordion-title -->
</div>
<div class="accordion-content" v-if="row.open">
<slot name="videoTranscript"></slot>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import video from "~/mixins/video/videos.js";
export default {
name: "VideoPlaylist",
mixins: [video],
props: {
videoInline: {
type: Boolean,
required: false,
default: false
},
videoId: {
type: String,
required: true
},
hasTranscript: {
type: Boolean,
required: false
},
autoPlay: {
type: Boolean,
required: false,
default: true
}
},
data() {
return {
accordionRows: {
videoTranscript: {
open: false
}
},
rotateIcon: false
};
},
computed: {
playerId() {
return `videoPlayer_${this._uid}`;
}
},
mounted() {
this.$nextTick(() => {
this.$brightcovePlayerLoader({
refNode: `#${this.playerId}`,
accountId: "2272822669001",
playerId: "DMcAHJ5QL",
embedId: "default",
videoId: this.videoId,
options: {
aspectRatio: "16:9",
autoplay: this.autoPlay
}
}).then(data => {
console.log(`[VideoPlayer.vue] init player.then data:`);
console.log(data);
});
});
this.stopVideo();
}
};
</script>

Related

Hide label for particular component in vue / nuxt js

I have created a reusable input component with label, but i want label to hide(if hidden it should not take a space something like display none in css) on some place and label should be visible on some places
here is my code of the input component
<template>
<div>
<label for="" :label="label" class="mb-1 select-label">{{label}}</label> //hide or visible depending on requirement
<div class="custom-select" :tabindex="tabindex" #blur="open = false">
<div class="selected" :class="{ open: open }" #click="open = !open">
{{ selected }}
</div>
<div class="items" :class="{ selectHide: !open }">
<div
v-for="(option, i) of options"
:key="i"
#click="
selected = option;
open = false;
$emit('input', option);
"
class="border-bottom px-3"
>
{{ option }}
</div>
</div>
</div>
</div>
</template>
here is the code of my script
<script>
export default {
props: {
label: {
type: String,
required: false,
default: ''
},
options: {
type: Array,
required: true,
},
default: {
type: String,
required: false,
default: null,
},
tabindex: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
selected: this.default
? this.default
: this.options.length > 0
? this.options[0]
: null,
open: false,
};
},
mounted() {
this.$emit("input", this.selected);
},
}
</script>
You can use v-if directive to conditionally render the label element based on the props value.
As v-if will actually destroy and recreate elements when the conditional is toggled. Hence, all the classes/attributes applied to the element will also destroy.
Demo :
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
showMessage: false
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-if="showMessage">{{ message }}</p>
</div>
If you will run above code snippet and open the developer console. You will see that <p> element will not be there as it has been removed from the DOM.
console screenshot :
You can just add a prop to control label visibility
script:
props: {
showLabel: {
type: Boolean,
default: false
}
}
template:
<label v-show="showLabel" />
parent component:
<MyCustomInput :show-label="false" />
<MyCustomInput :show-label="true" />

No compatible source was found for this media

Get that error when try to get stream from any HLS source.
I tried to add videojs-contrib-hls lib , but its dont help.
Maybe should i try some other player, and what player will properly work with hls sources?
<script>
import 'video.js/dist/video-js.css'
import {videoPlayer} from 'vue-video-player'
import videojs from 'video.js'
window.videojs = videojs
export default {
components: {
videoPlayer
},
data () {
return {
playerOptions: {
height: '700',
width: '1820',
controls: true,
sourceOrder: true,
hls: true,
sources: [{
// type: "video/mp4",
type: "application/x-mpegURL",
// src: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
src: "http://127.0.0.1:8081/live/93UEctBmn/index.m3u8",
withCredentials: false,
}],
autoplay: false,
},
techOrder: ['html5'],
html5: { hls: { withCredentials: false } },
contentData: {}
}
},
mounted() {
this.$store.dispatch("content/getById", this.$route.params.contentId).then(
(res) => {
// this.message = data.message;
// this.snackbar = true;
if(res.data.error){
console.log(res.data.error);
return;
}
this.contentData = Array.isArray(res.data) ? res.data[0] : res.data;
},
(error) => {
// this.message = error.response.data.message || error;
// this.snackbar = true;
console.log(error)
}
);
}
}
</script>
<template>
<div class="content">
<div class="content-player">
<video-player class="vjs-custom-skin videoPlayer" :options="playerOptions" ></video-player>
</div>
<div class="content-info">
<div class="content-info_title">
{{contentData.streamTitle || 'loading'}}
</div>
</div>
<v-divider></v-divider>
<div class="content-author">
<div class="content-author_avatar">
<v-avatar
size="56"
>
<v-img
:src="'https://diploma-rtmp-bucket.s3.eu-central-1.amazonaws.com/'+contentData.author[0]._id || ''"
></v-img>
</v-avatar>
</div>
<div class="content-author_name">
{{contentData.author[0].username || 'undefined username'}}
</div>
</div>
</div>
</template>
Here is my page code. Can it be becouse of some CORS troubles?. I run it player on local machine
I used another player vue-vjs-hls. On this player hls work good, dont now why hls source not works at vue-video-player and video.js. What strange becouse vue-vjs-hls use video.js as core.

Render a child component in the global app component

I wrote a dialog component (global) to show modal dialogs with overlays like popup forms.
Right now the dialog gets rendered inside the component where it is used. This leads to overlapping content, if there is something with position relative in the html code afterwards.
I want it to be rendered in the root App component at the very end so I can force the dialog to be always ontop of every other content.
This is my not working solution:
I tried to use named slots, hoping, that they work backwards in the component tree too. Unfortunately they don't seem to do that.
Anybody a solution how to do it?
My next idea would be to render with an extra component that is stored in the app component and register the dialogs in the global state. But that solution would be super complicated and looks kinda dirty.
The dialog component:
<template v-slot:dialogs>
<div class="dialog" :class="{'dialog--open': show, 'dialog--fullscreen': fullscreen }">
<transition name="dialogfade" duration="300">
<div class="dialog__overlay" v-if="show && !fullscreen" :key="'overlay'" #click="close"></div>
</transition>
<transition name="dialogzoom" duration="300">
<div class="dialog__content" :style="{'max-width': maxWidth}" v-if="show" :key="'content'">
<slot></slot>
</div>
</transition>
</div>
</template>
<script>
export default {
name: "MyDialog",
props: {show: {
type: Boolean,
default: false
},
persistent: {
type: Boolean,
default: true
},
fullscreen: {
type: Boolean,
default: false
},
maxWidth: {
type: String,
default: '600px'
}
},
data: () => ({}),
methods: {
close() {
if(!this.persistent) {
this.$emit('close')
}
}
}
}
</script>
The template of the app component:
<template>
<div class="application">
<div class="background">
<div class="satellite"></div>
<div class="car car-lr" :style="{ transform: `translateY(${car.x}px)`, left: adjustedLRLeft + '%' }" v-for="car in carsLR"></div>
</div>
<div class="content">
<login v-if="!$store.state.user"/>
<template v-else>
<main-menu :show-menu="showMainMenu" #close="showMainMenu = false"/>
<router-view/>
</template>
<notifications/>
<div class="dialogs"><slot name="dialogs"></slot></div>
</div>
</div>
</template>
Another possibility is to use portals. These provide a way to move any element to any place in the dom. Checkout the following library: https://github.com/LinusBorg/portal-vue
You can just place the dialog component directly in the app component and handle the dialog logic/which dialog to display in that component?
In case you want to trigger these dialogs from other places in your app, this would would be a good use case for vuex! That, combined with dynamic webpack imports is how I handle this.
With the help of the guys from the vuetify2 project, I found the solution. The dialog component gets an ref="dialogContent" attribute and the magic happens inside the beforeMount function.
<template>
<div class="dialog" ref="dialogContent" :class="{'dialog--open': show, 'dialog--fullscreen': fullscreen }">
<transition name="dialogfade" duration="300">
<div class="dialog__overlay" v-if="show && !fullscreen" :key="'overlay'" #click="close"></div>
</transition>
<transition name="dialogzoom" duration="300">
<div class="dialog__content" :style="{'max-width': maxWidth}" v-if="show" :key="'content'">
<slot></slot>
</div>
</transition>
</div>
</template>
<script>
export default {
name: "MyDialog",
props: {
show: {
type: Boolean,
default: false
},
persistent: {
type: Boolean,
default: true
},
fullscreen: {
type: Boolean,
default: false
},
maxWidth: {
type: String,
default: '600px'
}
},
data: () => ({}),
methods: {
close() {
if (!this.persistent) {
this.$emit('close')
}
}
},
beforeMount() {
this.$nextTick(() => {
const target = document.getElementById('dialogs');
target.appendChild(
this.$refs.dialogContent
)
})
},
}
</script>

MomentJS + VueJS: Getting Relative Time From Now To A Certain Point In The Past

I use MomentJS.
in my VueJS-Code I want to get the relative time from now to that point in the past. In my template I incorporate the result of this short piece of JavaScript:
<template>
<div>{{ moment(message.createdAt, 'YYYYMMDD').fromNow() }}</div>
</template>
the object receives the date as follows:
message: { createdAt: Date.now() }
the result is always: a few seconds ago ...
how can I get the correct result (not always "a few seconds ago"):
EDIT:
this is my full template:
<template v-for="message in messages">
<div class="message">
<div class="text">{{ message.text }}</div>
<div class="date">{{ moment(message.createdAt).format('D.M.YYYY') }}</div>
<div class="date">{{ moment(message.createdAt).fromNow() }}</div>
</div>
</template>
Well, you can't use moment directly in your template, as it's not white-boxed (not accessible in the template).
Template expressions are sandboxed and only have access to a whitelist of globals such as Math and Date. You should not attempt to access user defined globals in template expressions.
Source: https://v2.vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions
I would advise you to use some filter instead (you can also do it with methods in a very similar way).
Here is a working example.
new Vue({
el: "#app",
data() {
return {
messages: [
{
text: 'Message1',
createdAt: new Date() // Now
},
{
text: 'Message2',
createdAt: new Date(2016, 3, 1) // 1 April 2017
}
],
interval: null
};
},
filters: {
format(date) {
return moment(date).format('D.M.YYYY')
},
fromNow(date) {
return moment(date).fromNow();
}
},
created() {
this.interval = setInterval(() => this.$forceUpdate(), 1000);
// Trigger an update at least each second
// You should probably raise this duration as refreshing so often
// may be not useful
},
beforeDestroy() {
clearInterval(this.interval);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>
<div id="app">
<template v-for="message in messages">
<div class="message">
<div class="text">{{ message.text }}</div>
<div class="date">{{ message.createdAt | format }}</div>
<div class="date">{{ message.createdAt | fromNow }}</div>
</div>
</template>
</div>
Cobaltway's reply is working, but I would advise to extract the whole logic into a small component of its own, so you don't force Vue to re-render the whole component each time.
I created exactly this as an example a while ago, please see this fiddle:
https://jsfiddle.net/Linusborg/meovg84x/
Vue.component('dynamic-from-now',{
name:'DynamicFromNow',
props: {
tag: {type: String, default: 'span'},
value: {type: String, default: ()=> moment().toISOString() },
interval: {type: Number, default: 1000}
},
data() {
return { fromNow: moment(this.value).fromNow() }
},
mounted () {
this.intervalId = setInterval(this.updateFromNow, this.interval)
this.$watch('value', this.updateFromNow)
},
beforeDestroy() {
clearInterval(this.intervalId)
},
methods: {
updateFromNow() {
var newFromNow = moment(this.value).fromNow(this.dropFixes)
if (newFromNow !== this.fromNow) {
this.fromNow = newFromNow
}
}
},
render(h) {
return h(this.tag, this.fromNow)
}
})
Usage:
<dynamic-from-now :value="yourTimeStamp" :interval="2000" :tag="span" class="red" />

Returning promise from api and putting into global data

Sorry if this is obvious but new to vue and my js not so great.....
I'm trying to get some data from an api and then put that data into a global object so it can be passed down to some child components.
I can't seem to $set the data into the global projects object, it either returns undefined or returns a promise.
parent --
<template>
<div id="app">
<section class="intro">
<h1>WELCOME</h1>
</section>
<transition name="fade"> <router-view class="view" :computedProjects=computedProject ></router-view></transition>
</div>
</template>
<script>
import projectsList from './components/projectsList'
var client = contentful.createClient({
// This is the space ID. A space is like a project folder in Contentful terms
space: '',
// This is the access token for this space. Normally you get both ID and the token in the Contentful web app
accessToken: '',
})
var PRODUCT_CONTENT_TYPE_ID = 'project'
export default {
name: 'app',
components: {
projectsList
},
data: function() {
return {
users:'',
projects:'',
}
},
methods: {},
created : function () {
client.getEntries().then((response) => {
console.log(response)
this.projects = response
console.log(this.projects)
return response
});
},
computed: {
computedProject: function() {
if (!this.projects) return null;
return this.projects;
}
}
}
}
child ---
<template>
<section class="project-list">
<swiper :options="swiperOption">
<swiper-slide v-for="project in projects" v-bind:ref="'project' + project.name" :key="project.name" data-swiper-parallax="-100">
<div class="project-cover wrapper">
{{project.name}}
<router-link :to="{ name: 'project', params: { id: project.name }}">
<div class="cover-img" :style="{ 'background-image': 'url(../static/img/projects/'+project.name+'/'+'project-details-header.jpg)' }"> </div>
<div class="project-copy"></div>
<div href="#" class="tilter tilter--4">
<figure class="tilter__figure">
<img class="tilter__image" :src="'+project.fields.coverImage.fields.file.url+'" alt="" />
<div class="tilter__deco tilter__deco--shine"></div>
<div class="tilter__deco tilter__deco--overlay" v-bind:style="{ backgroundImage: project.gradient }"></div>
<figcaption class="tilter__caption">
<h1 class="projectName tilter__title title">{{project.name}}</h1>
<h2 class="tilter__description">{{project.tagline}}</h2>
</figcaption>
<svg class="tilter__deco tilter__deco--lines" viewBox="0 0 300 415">
<path d="M20.5,20.5h260v375h-260V20.5z" />
</svg>
</figure>
</div>
</router-link>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</section>
</template>
<script>
export default {
data: function () {
return {
swiperOption: {
autoplay:false,
setWrapperSize :true,
observeParents:true,
keyboardControl : true,
centeredSlides :true,
freeMode:false,
freeModeSticky:true,
pagination: '.swiper-pagination',
paginationType: 'progress',
watchSlidesProgress : true,
watchSlidesVisibility : true,
speed:650,
grabCursor:true,
parallax:true
},
}
},
props :['computedProject'],
created : function () {
console.log(this.projects)
},
any help will be appreciated.
You can just assign the vue data variable in the callback of getEntries like following:
created : function () {
var self = this
client.getEntries().then((response) => {
console.log(response)
self.projects = response
})
console.log(this.projects)
},