Im extending the VideoJS default Playlist plugin. Everything is going well tell now, My Issue I need to retrieve each video item duration. As per the API I tried the following:
player.playlist().forEach((item,index) => {
if (player.readyState() < 1) {
// do not have metadata tell this moment.
// waiting
player.one("loadedmetadata", onLoadedMetadata);
}
else {
// metadata already loaded
onLoadedMetadata();
}
function onLoadedMetadata() {
console.log(player.duration()); //<----NEED HELP HERE PLEASE
}
});
The result I got is the first item duration repeated 5 times (count of playlist item) and its not yet loaded in the player window.
Would you please help with fix to show each Playlist video item duration separately?
All the related issues in stackoverflow talking about the player screen itself (I hope I did not miss correct question here) But Im looking for each PlayList item duration.
Thank you.
I'm using the following:
Video.js 7.9.5
video-js-Playlist 4.2
video-js-playlist-ui 3.8.0
I fix my problem with new helper function and 1 extra step in video-js-playlist-ui 3.8.0
The fix as the following:
Step one: Helper function to get the item duration from video-js-playlist-ui plugin:
const itemDuration = function (item) {
const settings = {
itemVideoEl: document.createElement("video"),
itemVideoSource: document.createElement("source"),
itemVideoSrc: item.sources[0].src,
itemType: item.sources[0].type,
};
const { itemVideoEl, itemVideoSource, itemVideoSrc, itemType } = settings;
itemVideoSource.src = itemVideoSrc;
itemVideoSource.type = itemType;
itemVideoEl.appendChild(itemVideoSource);
const getDuration = [];
itemVideoEl.addEventListener("loadedmetadata", (event) => {
const duration = itemVideoEl.duration;
getDuration.push(duration);
});
item.duration = getDuration;
return item;
};
Step two: Add timeout to creating items inside video-js-playlist-ui plugin:
This will guarantee to show the video time in the HTML DOM.
class PlaylistMenuItem extends Component { //default class
createEl() { //default function
const item = itemDuration(this.options_.item); //<---REPLACED WITH THE NEW HELPER FUNCTION
const li = document.createElement("li");//default value
const showDescription = this.options_.showDescription;//default value
setTimeout(() => {
//The rest of the default function createEl() comes here.
},1000);
}
}
NOTE:
My fix is working for HTML5 Video/Audio only, I know more techs will need extra steps, so this is only a hint for anyone may stuck in the same situation. Hope this answer will help other people as I always get help from here.
Thank you.
Related
I want to store video duration and ``src to local storage, but this code not working. The video src and duration showing in inspect element application local storage but not working for the video player.
window.addEventListener("unload", () => {
let setDuration = localStorage.setItem(
"duration",
`${mainVideo.currentTime}`
);
let setSrc = localStorage.setItem("src", `${mainVideo.getAttribute("src")}`);
});
window.addEventListener("load", () => {
let getDuration = localStorage.getItem("duration");
let getSrc = localStorage.getItem("src").src;
if (getSrc) {
mainVideo.src = getSrc;
mainVideo.currentTime = getDuration;
}
});
One problem certainly lays right here
localStorage.getItem("src").src
You are trying to read the property src but localStorage.getItem("src") returns a string or null not an object. Remove the .src part and that error will be resolved.
I'm sorry if that wasn't your problem. If so please provide further information about your problem or any error messages.
I've followed Wix's guide to create a Related Products area at the bottom of their dynamic product page.
It mostly works as you'd expect. The problem comes when you click on one of the related products and see that the related products list doesn't change to reflect the currently loaded product.
The only way to get the related products list to change is by refreshing the page.
Is there possibly a simple fix for this? Below is their code:
import wixData from 'wix-data';
import wixLocation from 'wix-location';
$w.onReady(function () {
loadRelatedProducts();
});
async function loadRelatedProducts() {
let product = await $w('#productPage1').getProduct();
let relatedProductResults = await Promise.all([
relatedProductsByTable(product),
relatedProductsByPrice(product)
]);
if (relatedProductResults[0].length > 0)
showRelatedProducts(relatedProductResults[0]);
else
showRelatedProducts(relatedProductResults[1]);
}
async function relatedProductsByTable(product) {
let productId = product._id;
// find related products by relation table
let relatedByTable = await Promise.all([
wixData.query('RelatedProducts')
.eq('productA', productId)
.include('productB')
.find(),
wixData.query('RelatedProducts')
.eq('productB', productId)
.include('productA')
.find()
]);
let relatedProducts = [
...relatedByTable[0].items.map(_ => _.productB),
...relatedByTable[1].items.map(_ => _.productA)
];
return relatedProducts;
}
async function relatedProductsByPrice(product) {
let productId = product._id;
// find related products by price
let relatedByPrice = await wixData.query('Stores/Products')
.between('price', product.price * 0.8, product.price * 1.2)
.ne('_id', productId)
.find();
return relatedByPrice.items;
}
function showRelatedProducts(relatedProducts){
if(relatedProducts.length > 0){
relatedProducts.splice(4, relatedProducts.length);
$w('#relatedItemsRepeater').onItemReady(relatedItemReady);
$w("#relatedItemsRepeater").data = relatedProducts;
$w("#relatedItems").expand();
}
else {
$w("#relatedItems").collapse();
}
}
function relatedItemReady($w, product){
$w("#productImage").src = product.mainMedia;
$w("#productName").text = product.name;
$w("#productPrice").text = product.formattedPrice;
$w('#productImage').onClick(() => {
wixLocation.to(product.productPageUrl);
});
}
I suspect the issue is in this all being triggered by the .onReady() event. Unfortunately, I'm not sure how to also make this re-run on another trigger like when the related item itself is clicked.
The page itself does not reload when you click one of the related products. Instead, I believe they are simply rewriting the URL and then updating then re-fetching data from the database.
Indeed, you are correct. The page itself doesn't reload when a related item is selected so a new list of related items is not generated. Actually, at the time this example was published there was no simple way to get around this.
Since then, Wix has exposed the wix-location.onChange() function to take care of this very problem. All you need to do is add the following line:
wixLocation.onChange( () => loadRelatedProducts() );
It probably makes the most sense to add it right before the onReady() or even inside the onReady().
I recently began to code on a small frontend project in javascript. I came across the following problem:
Code Part 1
const startFeed = document.getElementById('startFeedButton');
const stopFeed = document.getElementById('stopFeedButton');
startFeed.addEventListener("click", () => {
api.streamingFunction(callback(response){
appendResponseToDom(response);
}
}
stopFeed.addEventListener("click", () => {
endStreamingFunction();
}
The 'api.streamingFunction' is part of a library and streams data via websocket. The 'callback' gets repeatedly called every time new data is available. It'll let you do something with the data inside the callback function. 'endStreamingFunction' closes the websocket.
Now the 'appendResponseToDom' function takes a piece of the data and appends it to the Dom like so:
Code Part 2
const someWhereInDom = document.getElementById('somewhere');
function appendResponseToDom(apiData) {
const newHTML = document.createElement("div");
newHTML.innerHTML = `
<div class="data">${apiData}</div>
<button id=${runningIndex}>Click here</button>
`
someWhereInDom.appendChild(newHTML);
}
Each button has a running index to uniquely select it. I didn't specify how runningIndex is evaluated in the code above.
What I want to do now is:
select every button uniquely after the newHTML has been inserted into the Dom (by adding event listeners) (and optionally in parallel stream new data via websocket)
Is this possible ? How would I usually do that ? Maybe you can point me in the right direction, thanks.
Solved it by observing changes in the Dom. See here: MutationObserver.
This article was also helpful: Detect, Undo And Redo DOM Changes With Mutation Observers
Solution Code
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if(mutation.target.nodeName === "BUTTON") {
mutation.target.addEventListener("click", () => {
console.log("clicked " + mutation.target.className); //Now, I can do sth with it!
})
}
});
});
mutationObserver.observe(someWhereInDom, {
attributes: true,
childList: true
});
I trying to create a rotating banner for cycle a few images.
const images = [url1, url2 url3]
const rotate = (url) => {
document.getElementById('banner').style.background = url
rotate(next)
}
Url's are correct full paths only shortened here in question. I'm striggl ing to solve how to get next in code above. This is some code I write from looking at examples. Im not a js programmer only recently started. Thank you for helping.
Here's one approach:
const rotate = (element, delay, urls) => {
let current = 0;
(function next() {
element.style.background = url[current]; // set image
current = (current + 1) % urls.length; // update for next pass
setTimeout(next, delay); // recycle
})(); // start immediately
};
with usage:
rotate(document.getElementById('banner'), 5000, [url1, url2, url3]);
The modulo arithmetic resets current to zero after the last image has been shown.
Today I've seen a strange problem when using RxJS. Please help me inspect it.
The problem I am working on is: "Given an array of image URLs, load and append all images to a div."
All the code snippets to demonstrate is here:
https://pastebin.com/F3ZkH3F8
At the beginning, I used the first snippet.
However, Rx.Observable.prototype.flatMap sometimes puts the images in wrong order (this behavior is noticed in the documentation). So, I changed to use concatMap (second snippet).
This time, only the first image is loaded. I took some time inspect the problem. I doubt that event load is not trigged from image. But the most confusing situation is that when I add some code to listen to image's load event only, it showed me that the event is trigged properly... (third snippet).
Then I decided to write another version using $.Deferred (fourth snippet).
It worked...
Could you please tell me what is the problem? Thank you very much!
Because fromEvent(image, 'load') on the first sub-observable is not completed, other sub-observables are waiting forever. So you should complete sub-observable after first event.
Use take(1).
excerpt from your second snippet
...
var loadedImageStream = Rx.Observable
.fromEvent(image, 'load')
.map(function() {
return image;
})
...
Add take(1) to complete sub-observable
...
var loadedImageStream = Rx.Observable
.fromEvent(image, 'load')
.map(function() {
return image;
})
.take(1)
...
EDIT:
Using concatMap makes loading image sequential, so it is slow.
If you pass index, you can use replace instead of append to keep the order. In this case, you can use flatMap, which enables fast concurrent loading.
$(function () {
var imageURLList = [
'https://placehold.it/500x100',
'https://placehold.it/500x200',
'https://placehold.it/500x300',
'https://placehold.it/500x400',
'https://placehold.it/500x500'
];
var imagesDOM = $('#images');
Rx.Observable
.fromArray(imageURLList)
.do(function (imageURL) {
imagesDOM.append(new Image()); // placeholder
})
.flatMap(function (imageURL, index) {
var image = new Image();
var loadedImageStream = Rx.Observable
.fromEvent(image, 'load')
.map(function () {
return [image, index];
})
.take(1)
image.src = imageURL;
return loadedImageStream;
})
.subscribe(function (image_index) {
var image = image_index[0];
var index = image_index[1];
imagesDOM.children().get(index).replaceWith(image);
})
})