Closed itzzzme closed 1 month ago
Yes, I wrote an artplayerPluginSkip
plugin: demo%20%7B%0A%09%09%09throw%20new%20TypeError(%27Option%20must%20be%20an%20array%20of%20time%20ranges%27)%3B%0A%09%09%7D%0A%0A%09%09ranges.forEach((range%2C%20index)%20%3D%3E%20%7B%0A%09%09%09if%20(!Array.isArray(range)%20%7C%7C%20range.length%20!%3D%3D%202)%20%7B%0A%09%09%09%09throw%20new%20TypeError(%60Range%20at%20index%20%24%7Bindex%7D%20must%20be%20an%20array%20of%20two%20numbers%60)%3B%0A%09%09%09%7D%0A%0A%09%09%09const%20%5Bstart%2C%20end%5D%20%3D%20range%3B%0A%09%09%09if%20(typeof%20start%20!%3D%3D%20%27number%27%20%7C%7C%20(typeof%20end%20!%3D%3D%20%27number%27%20%26%26%20end%20!%3D%3D%20Infinity))%20%7B%0A%09%09%09%09throw%20new%20TypeError(%60Range%20at%20index%20%24%7Bindex%7D%20must%20contain%20valid%20numbers%20or%20Infinity%60)%3B%0A%09%09%09%7D%0A%0A%09%09%09if%20(start%20%3E%3D%20end%20%26%26%20end%20!%3D%3D%20Infinity)%20%7B%0A%09%09%09%09throw%20new%20RangeError(%60In%20range%20at%20index%20%24%7Bindex%7D%2C%20start%20time%20must%20be%20less%20than%20end%20time%60)%3B%0A%09%09%09%7D%0A%0A%09%09%09if%20(index%20%3E%200)%20%7B%0A%09%09%09%09const%20prevEnd%20%3D%20ranges%5Bindex%20-%201%5D%5B1%5D%3B%0A%09%09%09%09if%20(prevEnd%20!%3D%3D%20Infinity%20%26%26%20start%20%3C%3D%20prevEnd)%20%7B%0A%09%09%09%09%09throw%20new%20RangeError(%60Range%20at%20index%20%24%7Bindex%7D%20overlaps%20with%20the%20previous%20range%60)%3B%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09%7D)%3B%0A%09%7D%0A%0A%09validateRanges(option)%3B%0A%0A%09return%20art%20%3D%3E%20%7B%0A%09%09let%20skipRanges%20%3D%20option%3B%0A%0A%09%09function%20updateRanges()%20%7B%0A%09%09%09const%20duration%20%3D%20art.duration%3B%0A%09%09%09skipRanges%20%3D%20skipRanges.map((%5Bstart%2C%20end%5D)%20%3D%3E%20%5B%0A%09%09%09%09start%2C%0A%09%09%09%09end%20%3D%3D%3D%20Infinity%20%3F%20duration%20%3A%20end%0A%09%09%09%5D)%3B%0A%09%09%7D%0A%0A%09%09function%20checkAndSkip()%20%7B%0A%09%09%09const%20currentTime%20%3D%20art.currentTime%3B%0A%09%09%09for%20(const%20%5Bstart%2C%20end%5D%20of%20skipRanges)%20%7B%0A%09%09%09%09if%20(currentTime%20%3E%3D%20start%20%26%26%20currentTime%20%3C%20end)%20%7B%0A%09%09%09%09%09art.seek%20%3D%20end%3B%0A%09%09%09%09%09break%3B%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09%7D%0A%0A%09%09art.on(%27video%3Atimeupdate%27%2C%20checkAndSkip)%3B%0A%09%09art.on(%27video%3Aloadedmetadata%27%2C%20updateRanges)%3B%0A%0A%09%09return%20%7B%0A%09%09%09name%3A%20%27artplayerPluginSkip%27%2C%0A%09%09%09update(newOption%20%3D%20%5B%5D)%20%7B%0A%09%09%09%09validateRanges(newOption)%3B%0A%09%09%09%09skipRanges%20%3D%20newOption%3B%0A%09%09%09%09updateRanges()%3B%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%7D%0A%0Avar%20art%20%3D%20new%20Artplayer(%7B%0A%09container%3A%20%27.artplayer-app%27%2C%0A%09url%3A%20%27%2Fassets%2Fsample%2Fvideo.mp4%27%2C%0A%09plugins%3A%20%5B%0A%09%09artplayerPluginSkip(%0A%09%09%09%5B%0A%09%09%09%09%5B0%2C%205%5D%2C%0A%09%09%09%09%5B10%2C%2015%5D%2C%0A%09%09%09%09%5B20%2C%20Infinity%5D%20%2F%2F%20Infinity%20is%20duration%20of%20video%0A%09%09%09%5D%0A%09%09)%0A%09%5D%0A%7D)%3B)
T
Yes, I wrote an
artplayerPluginSkip
plugin: demo%20%7B%0A%09%09%09throw%20new%20TypeError(%27Option%20must%20be%20an%20array%20of%20time%20ranges%27)%3B%0A%09%09%7D%0A%0A%09%09ranges.forEach((range%2C%20index)%20%3D%3E%20%7B%0A%09%09%09if%20(!Array.isArray(range)%20%7C%7C%20range.length%20!%3D%3D%202)%20%7B%0A%09%09%09%09throw%20new%20TypeError(%60Range%20at%20index%20%24%7Bindex%7D%20must%20be%20an%20array%20of%20two%20numbers%60)%3B%0A%09%09%09%7D%0A%0A%09%09%09const%20%5Bstart%2C%20end%5D%20%3D%20range%3B%0A%09%09%09if%20(typeof%20start%20!%3D%3D%20%27number%27%20%7C%7C%20(typeof%20end%20!%3D%3D%20%27number%27%20%26%26%20end%20!%3D%3D%20Infinity))%20%7B%0A%09%09%09%09throw%20new%20TypeError(%60Range%20at%20index%20%24%7Bindex%7D%20must%20contain%20valid%20numbers%20or%20Infinity%60)%3B%0A%09%09%09%7D%0A%0A%09%09%09if%20(start%20%3E%3D%20end%20%26%26%20end%20!%3D%3D%20Infinity)%20%7B%0A%09%09%09%09throw%20new%20RangeError(%60In%20range%20at%20index%20%24%7Bindex%7D%2C%20start%20time%20must%20be%20less%20than%20end%20time%60)%3B%0A%09%09%09%7D%0A%0A%09%09%09if%20(index%20%3E%200)%20%7B%0A%09%09%09%09const%20prevEnd%20%3D%20ranges%5Bindex%20-%201%5D%5B1%5D%3B%0A%09%09%09%09if%20(prevEnd%20!%3D%3D%20Infinity%20%26%26%20start%20%3C%3D%20prevEnd)%20%7B%0A%09%09%09%09%09throw%20new%20RangeError(%60Range%20at%20index%20%24%7Bindex%7D%20overlaps%20with%20the%20previous%20range%60)%3B%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09%7D)%3B%0A%09%7D%0A%0A%09validateRanges(option)%3B%0A%0A%09return%20art%20%3D%3E%20%7B%0A%09%09let%20skipRanges%20%3D%20option%3B%0A%0A%09%09function%20updateRanges()%20%7B%0A%09%09%09const%20duration%20%3D%20art.duration%3B%0A%09%09%09skipRanges%20%3D%20skipRanges.map((%5Bstart%2C%20end%5D)%20%3D%3E%20%5B%0A%09%09%09%09start%2C%0A%09%09%09%09end%20%3D%3D%3D%20Infinity%20%3F%20duration%20%3A%20end%0A%09%09%09%5D)%3B%0A%09%09%7D%0A%0A%09%09function%20checkAndSkip()%20%7B%0A%09%09%09const%20currentTime%20%3D%20art.currentTime%3B%0A%09%09%09for%20(const%20%5Bstart%2C%20end%5D%20of%20skipRanges)%20%7B%0A%09%09%09%09if%20(currentTime%20%3E%3D%20start%20%26%26%20currentTime%20%3C%20end)%20%7B%0A%09%09%09%09%09art.seek%20%3D%20end%3B%0A%09%09%09%09%09break%3B%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09%7D%0A%0A%09%09art.on(%27video%3Atimeupdate%27%2C%20checkAndSkip)%3B%0A%09%09art.on(%27video%3Aloadedmetadata%27%2C%20updateRanges)%3B%0A%0A%09%09return%20%7B%0A%09%09%09name%3A%20%27artplayerPluginSkip%27%2C%0A%09%09%09update(newOption%20%3D%20%5B%5D)%20%7B%0A%09%09%09%09validateRanges(newOption)%3B%0A%09%09%09%09skipRanges%20%3D%20newOption%3B%0A%09%09%09%09updateRanges()%3B%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%7D%0A%0Avar%20art%20%3D%20new%20Artplayer(%7B%0A%09container%3A%20%27.artplayer-app%27%2C%0A%09url%3A%20%27%2Fassets%2Fsample%2Fvideo.mp4%27%2C%0A%09plugins%3A%20%5B%0A%09%09artplayerPluginSkip(%0A%09%09%09%5B%0A%09%09%09%09%5B0%2C%205%5D%2C%0A%09%09%09%09%5B10%2C%2015%5D%2C%0A%09%09%09%09%5B20%2C%20Infinity%5D%20%2F%2F%20Infinity%20is%20duration%20of%20video%0A%09%09%09%5D%0A%09%09)%0A%09%5D%0A%7D)%3B)
Thank you for you help it worked. I am integrating the player in my anime streaming website and I had several questions which I didn't find in the docs
1) how to escape special html tags like "i" tags from subtitle vtt. ( I tried escape=true and encoding="utf-8") but it's still coming) 2) how to change the color of highlight ( I saw the code of Artplayer class but the hightlight only accepts array. Setting color is not mentioned) 3) how to assign custom keys to shortcut like pressing m to mute i to toggle pip mode etc.
Thank you for your helpt btw
i
or b
tags of vtt subtitles? Then you should set escape
to false
option.hightlight
is relatively simple at present, and not many people use it, so it does not support custom colors, or you can try: artplayer-plugin-chapter
- Do you want to display the
i
orb
tags of vtt subtitles? Then you should setescape
tofalse
option.hightlight
is relatively simple at present, and not many people use it, so it does not support custom colors, or you can try: artplayer-plugin-chapter- Like this: shortcut%20%7B%0A%09%09art.muted%20%3D%20!art.muted%3B%0A%09%7D%0A%0A%09if%20(event.key.toLowerCase()%20%3D%3D%3D%20%27i%27)%20%7B%0A%09%09art.pip%20%3D%20!art.pip%3B%0A%09%7D%0A%7D)%3B)
the problem with escape false is when I do that the subtitle get automatically very small
escape:false
v/s
escape:true
also is there option to give a color in time slider to particular chapter
You can customize the font size like this: demo
same result here is the code as well as screenshot
import Hls from "hls.js"; import { useEffect, useRef } from "react"; import Artplayer from "artplayer"; import artplayerPluginHlsQuality from "artplayer-plugin-hls-quality"; import artplayerPluginChapter from 'artplayer-plugin-chapter' import artplayerPluginAmbilight from 'artplayer-plugin-ambilight'
function artplayerPluginSkip(option = []) { function validateRanges(ranges) { if (!Array.isArray(ranges)) { throw new TypeError('Option must be an array of time ranges'); }
ranges.forEach((range, index) => {
if (!Array.isArray(range) || range.length !== 2) {
throw new TypeError(`Range at index ${index} must be an array of two numbers`);
}
const [start, end] = range;
if (typeof start !== 'number' || (typeof end !== 'number' && end !== Infinity)) {
throw new TypeError(`Range at index ${index} must contain valid numbers or Infinity`);
}
if (start > end && end !== Infinity) {
throw new RangeError(`In range at index ${index}, start time must be less than end time`);
}
if (index > 0) {
const prevEnd = ranges[index - 1][1];
if (prevEnd !== Infinity && start <= prevEnd) {
throw new RangeError(`Range at index ${index} overlaps with the previous range`);
}
}
});
}
validateRanges(option);
return art => {
let skipRanges = option;
function updateRanges() {
const duration = art.duration;
skipRanges = skipRanges.map(([start, end]) => [
start,
end === Infinity ? duration : end
]);
}
function checkAndSkip() {
const currentTime = art.currentTime;
for (const [start, end] of skipRanges) {
if (currentTime >= start && currentTime < end) {
art.seek = end;
break;
}
}
}
art.on('video:timeupdate', checkAndSkip);
art.on('video:loadedmetadata', updateRanges);
return {
name: 'artplayerPluginSkip',
update(newOption = []) {
validateRanges(newOption);
skipRanges = newOption;
updateRanges();
}
}
}
}
export default function Player({ streamUrl, subtitles, intro, outro }) { const artRef = useRef(null); function 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"); } }
useEffect(() => {
if (!streamUrl || !artRef.current) return;
const art = new Artplayer({
url: streamUrl,
container: artRef.current,
type: "m3u8",
volume: 1,
pip: true,
autoSize: true,
autoMini: true,
setting: true,
loop: true,
flip: true,
playbackRate: true,
aspectRatio: true,
fullscreen: true,
subtitleOffset: true,
miniProgressBar: true,
mutex: true,
backdrop: true,
playsInline: true,
autoPlayback: true,
airplay: true,
plugins: [
artplayerPluginHlsQuality({
control: true,
setting: true,
getResolution: (level) => level.height + "P",
title: "Quality",
auto: "Auto",
}),
artplayerPluginSkip(
[
[intro.start, intro.end],
[outro.start, outro.end],
]
),
artplayerPluginChapter({
chapters: [
{ start: intro.start, end: intro.end, title: 'intro' },
{ start: outro.start, end: outro.end, title: 'outro' },
]
}),
artplayerPluginAmbilight({
blur: '50px',
opacity: 1,
frequency: 10,
duration: 0.3,
}),
],
subtitle: {
style: {
fontWeight: "400",
fontSize: '20px',
backgroundColor: "rgba(0, 0, 0, 0.65)",
color: "white",
},
escape: false,
},
settings: [
{
html: "Subtitle",
tooltip: "Select",
selector: 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;
},
},
],
customType: {
m3u8: playM3u8,
}
});
document.addEventListener('keydown', function (event) {
if (event.key.toLowerCase() === 'm') {
art.muted = !art.muted;
}
if (event.key.toLowerCase() === 'i') {
art.pip = !art.pip;
}
if (event.key.toLowerCase() === 'f') {
art.fullscreen = !art.fullscreen;
}
});
art.on('ready', () => {
art.focus();
});
const defaultSubtitle = subtitles?.find((sub) => sub.label === "English");
if (defaultSubtitle) {
art.subtitle.switch(defaultSubtitle.file, {
name: defaultSubtitle.label,
});
}
art.on("resize", () => {
art.subtitle.style({
fontSize: art.height * 0.05 + "px",
marginBottom: art.height * 0.05 + "px",
});
});
return () => {
if (art && art.destroy) {
art.destroy(false);
}
};
}, [streamUrl, subtitles, intro, outro]);
return <div ref={artRef} className="w-full h-full"></div>;
}
screenshot
What is your artplayer version, or is there a demo link?
It may be caused by this code:
art.on("resize", () => {
art.subtitle.style({
fontSize: art.height * 0.05 + "px",
marginBottom: art.height * 0.05 + "px",
});
});
my version is 5.2.0, currently the website is in development so I don't have a demo link. Also I don't think the problem is occuring because of this line. because this line should only trigger on resize but if my screen is still and there some text with i or b tag in subtitle then the font size gets automatically small.
also can you kindly tell how to hightlight particular chapter portion with different color
I was trying to know that how I can autoskip specific fragment of my video