Closed itzzzme closed 3 weeks ago
Are you looking for:artplayer-plugin-hls-control
Thanks @zhw2590582! That's exactly what I am looking for
@zhw2590582 I had two questions
How to add plugins once the player has already been initiated because once the player has been initiated in order to add a plugin or do any kind of change on pressing button(not associated with player) it doesn't reflect any change.I have to reload the player which is not a viable option.
how to trigger function on video ending. I have tried video:ended
from the official docs but it didn't work
You can add the plug-in after the player initialization is completed, and then I tested that the video:ended event was triggered normally. demo%3B%0A%7D)%3B%0A%0Aart.on(%27video%3Aended%27%2C%20()%20%3D%3E%20%7B%0A%09console.log(%27----%3E%27%2C%20%27video%3Aended%27)%3B%0A%7D)%3B)
in demo what you have done in this part
art.on('ready', () => {
art.plugins.add(function yourPlugin(art) {
console.log('yourPlugin');
});
});
currently I am doing exactly this but what I am saying is on changing of some variable if you try to add some plugins it doesn't work
my implementation
but in this code problem is after initialization of player if autoSkipInto variable changes it doesn't affect the plugins adding
scenario: initaily the autoSkipIntro was false and player has initialized but in between of playing video if it gets true the it doesn't do that plugins adding portion
getting this error when adding this lines
art.on("video:ended", () => {
console.log("---->", "video:ended");
});
my full code
import Hls from "hls.js";
import { useEffect, useRef } from "react";
import Artplayer from "artplayer";
import artplayerPluginHlsQuality from "artplayer-plugin-hls-quality";
import artplayerPluginChapter from "./artPlayerPluinChaper";
import artplayerPluginSkip from "./autoSkip";
import artplayerPluginVttThumbnail from "./artPlayerPluginVttThumbnail";
import {
backward10Icon,
captionIcon,
forward10Icon,
fullScreenOffIcon,
fullScreenOnIcon,
loadingIcon,
logo,
muteIcon,
pauseIcon,
pipIcon,
playIcon,
playIconLg,
settingsIcon,
volumeIcon,
} from "./PlayerStyle";
import "./Player.css";
import website_name from "@/src/config/website";
import getChapterStyles from "./getChapterStyle";
const KEY_CODES = {
M: "m",
I: "i",
F: "f",
V: "v",
SPACE: " ",
ARROW_UP: "arrowup",
ARROW_DOWN: "arrowdown",
ARROW_RIGHT: "arrowright",
ARROW_LEFT: "arrowleft",
};
export default function Player({
streamUrl,
subtitles,
thumbnail,
intro,
outro,
autoSkipIntro,
autoPlay,
}) {
const artRef = useRef(null);
const proxy = import.meta.env.VITE_PROXY_URL;
const m3u8proxy = import.meta.env.VITE_M3U8_PROXY_URL;
useEffect(() => {
const applyChapterStyles = () => {
const existingStyles = document.querySelectorAll(
"style[data-chapter-styles]"
);
existingStyles.forEach((style) => style.remove());
const styleElement = document.createElement("style");
styleElement.setAttribute("data-chapter-styles", "true");
const styles = getChapterStyles(intro, outro);
styleElement.textContent = styles;
document.head.appendChild(styleElement);
return () => {
styleElement.remove();
};
};
if (streamUrl || intro || outro) {
const cleanup = applyChapterStyles();
return cleanup;
}
}, [streamUrl, intro, outro]);
const playM3u8 = (video, url, art) => {
if (Hls.isSupported()) {
if (art.hls) art.hls.destroy();
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);
art.hls = hls;
art.on("destroy", () => hls.destroy());
hls.on(Hls.Events.ERROR, (event, data) => {
console.error("HLS.js error:", data);
});
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = url;
} else {
art.notice.show("Unsupported playback format: m3u8");
}
};
const createChapters = () => {
const chapters = [];
if (intro?.start !== 0 || intro?.end !== 0) {
chapters.push({ start: intro.start, end: intro.end, title: "intro" });
}
if (outro?.start !== 0 || outro?.end !== 0) {
chapters.push({ start: outro.start, end: outro.end, title: "outro" });
}
return chapters;
};
const handleKeydown = (event, art) => {
const tagName = event.target.tagName.toLowerCase();
if (tagName === "input" || tagName === "textarea") return;
switch (event.key.toLowerCase()) {
case KEY_CODES.M:
art.muted = !art.muted;
break;
case KEY_CODES.I:
art.pip = !art.pip;
break;
case KEY_CODES.F:
event.preventDefault();
event.stopPropagation();
art.fullscreen = !art.fullscreen;
break;
case KEY_CODES.V:
event.preventDefault();
event.stopPropagation();
art.subtitle.show = !art.subtitle.show;
break;
case KEY_CODES.SPACE:
event.preventDefault();
event.stopPropagation();
art.playing ? art.pause() : art.play();
break;
case KEY_CODES.ARROW_UP:
event.preventDefault();
event.stopPropagation();
art.volume = Math.min(art.volume + 0.1, 1);
break;
case KEY_CODES.ARROW_DOWN:
event.preventDefault();
event.stopPropagation();
art.volume = Math.max(art.volume - 0.1, 0);
break;
case KEY_CODES.ARROW_RIGHT:
event.preventDefault();
event.stopPropagation();
art.currentTime = Math.min(art.currentTime + 10, art.duration);
break;
case KEY_CODES.ARROW_LEFT:
event.preventDefault();
event.stopPropagation();
art.currentTime = Math.max(art.currentTime - 10, 0);
break;
default:
break;
}
};
useEffect(() => {
if (!streamUrl || !artRef.current) return;
Artplayer.CONTEXTMENU = false;
const art = new Artplayer({
url: m3u8proxy + streamUrl,
container: artRef.current,
type: "m3u8",
volume: 1,
setting: true,
playbackRate: true,
pip: true,
hotkey: false,
fullscreen: true,
mutex: true,
playsInline: true,
autoPlayback: true,
lock: true,
airplay: true,
autoOrientation: true,
fastForward: true,
autoplay: autoPlay,
plugins: [
artplayerPluginHlsQuality({
setting: true,
getResolution: (level) => level.height + "P",
title: "Quality",
auto: "Auto",
}),
artplayerPluginChapter({ chapters: createChapters() }),
],
subtitle: {
style: {
"font-weight": "400",
"background-color": "rgba(0, 0, 0, 0.65)",
height: "fit-content",
width: "fit-content",
marginInline: "auto",
"margin-top": "auto",
"margin-bottom": "2rem",
left: "50%",
transform: "translateX(-50%)",
color: "#fff",
},
escape: false,
},
layers: [
{
name: website_name,
html: logo,
tooltip: website_name,
style: {
opacity: 1,
position: "absolute",
top: "5px",
right: "5px",
transition: "opacity 0.5s ease-out",
},
},
],
controls: [
{
html: backward10Icon,
position: "right",
tooltip: "Backward 10s",
click: () => {
art.currentTime = Math.max(art.currentTime - 10, 0);
},
},
{
html: forward10Icon,
position: "right",
tooltip: "Forward 10s",
click: () => {
art.currentTime = Math.min(art.currentTime + 10, art.duration);
},
},
],
icons: {
play: playIcon,
pause: pauseIcon,
setting: settingsIcon,
volume: volumeIcon,
pip: pipIcon,
volumeClose: muteIcon,
state: playIconLg,
loading: loadingIcon,
fullscreenOn: fullScreenOnIcon,
fullscreenOff: fullScreenOffIcon,
},
customType: {
m3u8: playM3u8,
},
});
art.on("resize", () => {
art.subtitle.style({
fontSize:
(art.height > 500 ? art.height * 0.02 : art.height * 0.04) + "px",
});
});
art.on("ready", () => {
document.addEventListener("keydown", (event) =>
handleKeydown(event, art)
);
art.subtitle.style({
fontSize:
(art.height > 500 ? art.height * 0.02 : art.height * 0.04) + "px",
});
thumbnail &&
art.plugins.add(
artplayerPluginVttThumbnail({
vtt: `${proxy}${thumbnail}`,
})
);
subtitles &&
subtitles.length > 0 &&
art.setting.add({
name: "captions",
icon: captionIcon,
html: "Subtitle",
tooltip:
subtitles.find((sub) => sub.label.toLowerCase() === "english")
?.label || "default",
position: "right",
selector: [
{
html: "Display",
switch: true,
onSwitch: function (item) {
item.tooltip = item.switch ? "Hide" : "Show";
art.subtitle.show = !item.switch;
return !item.switch;
},
},
...subtitles.map((sub) => ({
default: sub.label === "English",
html: sub.label,
url: sub.file,
})),
],
onSelect: function (item) {
art.subtitle.switch(item.url, { name: item.html });
return item.html;
},
});
console.log(autoSkipIntro);
autoSkipIntro &&
art.plugins.add(
artplayerPluginSkip([
...(intro.start && intro.end ? [[intro.start, intro.end]] : []),
...(outro.start && outro.end ? [[outro.start, outro.end]] : []),
])
);
setTimeout(() => {
art.layers[website_name].style.opacity = 0;
}, 2000);
});
const defaultSubtitle = subtitles?.find((sub) => sub.label === "English");
if (defaultSubtitle) {
art.subtitle.switch(defaultSubtitle.file, {
name: defaultSubtitle.label,
});
}
art.on("video:ended", () => {
console.log("---->", "video:ended");
});
return () => {
if (art && art.destroy) {
art.destroy(false);
}
};
}, [streamUrl, subtitles, intro, outro, autoSkipIntro]);
return <div ref={artRef} className="w-full h-full"></div>;
}
I have tested the m3u8 link with several other option and also with standalone hls.js it's not giving any error. But this error is only coming in artplayer when tyring some events (i.e: video:ended
in this case)
here is the comparison
with artplayer it can't read some packets
https://github.com/user-attachments/assets/2283f7f5-dbd8-45de-8bb2-75f9be455bc1
same link with normal hls.js standalone library
https://github.com/user-attachments/assets/4409885c-f79a-4d2a-963a-baab5c902e67
it will be really helpful if you can tell the issue here
btw can i do something like this to rewind in mobile on double tap by adding layers
{
html: "",
style: {
position: "absolute",
left: 0,
top: 0,
width: "40%",
height: "100%",
},
disable: !Artplayer.utils.isMobile,
dblclick: function () {
art.currentTime = Math.max(0, art.currentTime - 10);
}
},
I don't quite understand your logic, and your code is a bit complicated, but the error about the video is from the decoding of hls
I don't quite understand your logic, and your code is a bit complicated, but the error about the video is from the decoding of hls
Understood. Thanks for your help
Currently is there any option that the player supports multiple audios along with multiple qualities of the hls stream?