vidply 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -24
- package/dist/vidply.css +24 -0
- package/dist/vidply.esm.js +448 -212
- package/dist/vidply.esm.js.map +3 -3
- package/dist/vidply.esm.min.js +7 -6
- package/dist/vidply.esm.min.meta.json +31 -25
- package/dist/vidply.js +448 -212
- package/dist/vidply.js.map +3 -3
- package/dist/vidply.min.css +1 -1
- package/dist/vidply.min.js +7 -6
- package/dist/vidply.min.meta.json +31 -25
- package/package.json +4 -1
- package/src/controls/ControlBar.js +45 -7
- package/src/core/Player.js +36 -0
- package/src/features/PlaylistManager.js +611 -437
- package/src/i18n/translations.js +50 -0
- package/src/styles/vidply.css +24 -0
- package/src/utils/TimeUtils.js +9 -4
package/dist/vidply.esm.js
CHANGED
|
@@ -402,184 +402,6 @@ var DOMUtils = {
|
|
|
402
402
|
}
|
|
403
403
|
};
|
|
404
404
|
|
|
405
|
-
// src/utils/TimeUtils.js
|
|
406
|
-
var TimeUtils = {
|
|
407
|
-
/**
|
|
408
|
-
* Format seconds to time string (HH:MM:SS or MM:SS)
|
|
409
|
-
*/
|
|
410
|
-
formatTime(seconds, alwaysShowHours = false) {
|
|
411
|
-
if (!isFinite(seconds) || seconds < 0) {
|
|
412
|
-
return alwaysShowHours ? "00:00:00" : "00:00";
|
|
413
|
-
}
|
|
414
|
-
const hours = Math.floor(seconds / 3600);
|
|
415
|
-
const minutes = Math.floor(seconds % 3600 / 60);
|
|
416
|
-
const secs = Math.floor(seconds % 60);
|
|
417
|
-
const pad = (num) => String(num).padStart(2, "0");
|
|
418
|
-
if (hours > 0 || alwaysShowHours) {
|
|
419
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
|
|
420
|
-
}
|
|
421
|
-
return `${pad(minutes)}:${pad(secs)}`;
|
|
422
|
-
},
|
|
423
|
-
/**
|
|
424
|
-
* Parse time string to seconds
|
|
425
|
-
*/
|
|
426
|
-
parseTime(timeString) {
|
|
427
|
-
const parts = timeString.split(":").map((p) => parseInt(p, 10));
|
|
428
|
-
if (parts.length === 3) {
|
|
429
|
-
return parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
430
|
-
} else if (parts.length === 2) {
|
|
431
|
-
return parts[0] * 60 + parts[1];
|
|
432
|
-
} else if (parts.length === 1) {
|
|
433
|
-
return parts[0];
|
|
434
|
-
}
|
|
435
|
-
return 0;
|
|
436
|
-
},
|
|
437
|
-
/**
|
|
438
|
-
* Format seconds to readable duration
|
|
439
|
-
*/
|
|
440
|
-
formatDuration(seconds) {
|
|
441
|
-
if (!isFinite(seconds) || seconds < 0) {
|
|
442
|
-
return "0 seconds";
|
|
443
|
-
}
|
|
444
|
-
const hours = Math.floor(seconds / 3600);
|
|
445
|
-
const minutes = Math.floor(seconds % 3600 / 60);
|
|
446
|
-
const secs = Math.floor(seconds % 60);
|
|
447
|
-
const parts = [];
|
|
448
|
-
if (hours > 0) {
|
|
449
|
-
parts.push(`${hours} hour${hours !== 1 ? "s" : ""}`);
|
|
450
|
-
}
|
|
451
|
-
if (minutes > 0) {
|
|
452
|
-
parts.push(`${minutes} minute${minutes !== 1 ? "s" : ""}`);
|
|
453
|
-
}
|
|
454
|
-
if (secs > 0 || parts.length === 0) {
|
|
455
|
-
parts.push(`${secs} second${secs !== 1 ? "s" : ""}`);
|
|
456
|
-
}
|
|
457
|
-
return parts.join(", ");
|
|
458
|
-
},
|
|
459
|
-
/**
|
|
460
|
-
* Format percentage
|
|
461
|
-
*/
|
|
462
|
-
formatPercentage(value, total) {
|
|
463
|
-
if (total === 0) return 0;
|
|
464
|
-
return Math.round(value / total * 100);
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
|
|
468
|
-
// src/icons/Icons.js
|
|
469
|
-
var iconPaths = {
|
|
470
|
-
play: `<path d="M8 5v14l11-7z"/>`,
|
|
471
|
-
pause: `<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>`,
|
|
472
|
-
stop: `<rect x="6" y="6" width="12" height="12"/>`,
|
|
473
|
-
rewind: `<path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/>`,
|
|
474
|
-
forward: `<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>`,
|
|
475
|
-
skipPrevious: `<path d="M6 6h2v12H6V6zm3 6l8.5 6V6L9 12z"/>`,
|
|
476
|
-
skipNext: `<path d="M16 6h2v12h-2V6zM6 6l8.5 6L6 18V6z"/>`,
|
|
477
|
-
restart: `<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>`,
|
|
478
|
-
volumeHigh: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>`,
|
|
479
|
-
volumeMedium: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
|
|
480
|
-
volumeLow: `<path d="M7 9v6h4l5 5V4l-5 5H7z"/>`,
|
|
481
|
-
volumeMuted: `<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>`,
|
|
482
|
-
fullscreen: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
|
|
483
|
-
fullscreenExit: `<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>`,
|
|
484
|
-
settings: `<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94L14.4 2.81c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>`,
|
|
485
|
-
captions: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/>`,
|
|
486
|
-
captionsOff: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/><path d="M0 0h24v24H0z" fill="none"/>`,
|
|
487
|
-
pip: `<path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/>`,
|
|
488
|
-
speed: `<path d="M20.38 8.57l-1.23 1.85a8 8 0 0 1-.22 7.58H5.07A8 8 0 0 1 15.58 6.85l1.85-1.23A10 10 0 0 0 3.35 19a2 2 0 0 0 1.72 1h13.85a2 2 0 0 0 1.74-1 10 10 0 0 0-.27-10.44z"/><path d="M10.59 15.41a2 2 0 0 0 2.83 0l5.66-8.49-8.49 5.66a2 2 0 0 0 0 2.83z"/>`,
|
|
489
|
-
close: `<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>`,
|
|
490
|
-
check: `<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>`,
|
|
491
|
-
arrowUp: `<path d="M7 14l5-5 5 5z"/>`,
|
|
492
|
-
arrowDown: `<path d="M7 10l5 5 5-5z"/>`,
|
|
493
|
-
arrowLeft: `<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>`,
|
|
494
|
-
arrowRight: `<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>`,
|
|
495
|
-
loading: `<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>`,
|
|
496
|
-
error: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>`,
|
|
497
|
-
download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
|
|
498
|
-
link: `<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>`,
|
|
499
|
-
playlist: `<path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/>`,
|
|
500
|
-
language: `<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>`,
|
|
501
|
-
hd: `<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/>`,
|
|
502
|
-
transcript: `<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>`,
|
|
503
|
-
audioDescription: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
|
|
504
|
-
audioDescriptionOn: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/><circle cx="19" cy="16" r="3" fill="#3b82f6"/><path d="M18.5 17.5l1-1 1.5 1.5" stroke="white" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
|
|
505
|
-
signLanguage: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
|
|
506
|
-
signLanguageOn: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
|
|
507
|
-
speaker: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
|
|
508
|
-
music: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7zm-1.5 16c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
|
|
509
|
-
moreVertical: `<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
|
|
510
|
-
moreHorizontal: `<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`
|
|
511
|
-
};
|
|
512
|
-
var svgWrapper = (paths) => `<svg viewBox="0 0 24 24" fill="currentColor">${paths}</svg>`;
|
|
513
|
-
var Icons = Object.fromEntries(
|
|
514
|
-
Object.entries(iconPaths).map(([key, value]) => [key, svgWrapper(value)])
|
|
515
|
-
);
|
|
516
|
-
function getIcon(name) {
|
|
517
|
-
return Icons[name] || Icons.play;
|
|
518
|
-
}
|
|
519
|
-
function createIconElement(name, className = "") {
|
|
520
|
-
const wrapper = document.createElement("span");
|
|
521
|
-
wrapper.className = `vidply-icon ${className}`.trim();
|
|
522
|
-
wrapper.innerHTML = getIcon(name);
|
|
523
|
-
wrapper.setAttribute("aria-hidden", "true");
|
|
524
|
-
return wrapper;
|
|
525
|
-
}
|
|
526
|
-
function createPlayOverlay() {
|
|
527
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
528
|
-
svg.setAttribute("class", "vidply-play-overlay");
|
|
529
|
-
svg.setAttribute("viewBox", "0 0 80 80");
|
|
530
|
-
svg.setAttribute("width", "80");
|
|
531
|
-
svg.setAttribute("height", "80");
|
|
532
|
-
svg.setAttribute("aria-hidden", "true");
|
|
533
|
-
svg.setAttribute("role", "presentation");
|
|
534
|
-
svg.style.cursor = "pointer";
|
|
535
|
-
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
536
|
-
const filterId = `vidply-play-shadow-${Math.random().toString(36).substr(2, 9)}`;
|
|
537
|
-
const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
|
538
|
-
filter.setAttribute("id", filterId);
|
|
539
|
-
filter.setAttribute("x", "-50%");
|
|
540
|
-
filter.setAttribute("y", "-50%");
|
|
541
|
-
filter.setAttribute("width", "200%");
|
|
542
|
-
filter.setAttribute("height", "200%");
|
|
543
|
-
const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
|
544
|
-
feGaussianBlur.setAttribute("in", "SourceAlpha");
|
|
545
|
-
feGaussianBlur.setAttribute("stdDeviation", "3");
|
|
546
|
-
const feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
|
|
547
|
-
feOffset.setAttribute("dx", "0");
|
|
548
|
-
feOffset.setAttribute("dy", "2");
|
|
549
|
-
feOffset.setAttribute("result", "offsetblur");
|
|
550
|
-
const feComponentTransfer = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
|
551
|
-
const feFuncA = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
|
|
552
|
-
feFuncA.setAttribute("type", "linear");
|
|
553
|
-
feFuncA.setAttribute("slope", "0.3");
|
|
554
|
-
feComponentTransfer.appendChild(feFuncA);
|
|
555
|
-
const feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
|
556
|
-
const feMergeNode1 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
557
|
-
const feMergeNode2 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
558
|
-
feMergeNode2.setAttribute("in", "SourceGraphic");
|
|
559
|
-
feMerge.appendChild(feMergeNode1);
|
|
560
|
-
feMerge.appendChild(feMergeNode2);
|
|
561
|
-
filter.appendChild(feGaussianBlur);
|
|
562
|
-
filter.appendChild(feOffset);
|
|
563
|
-
filter.appendChild(feComponentTransfer);
|
|
564
|
-
filter.appendChild(feMerge);
|
|
565
|
-
defs.appendChild(filter);
|
|
566
|
-
svg.appendChild(defs);
|
|
567
|
-
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
568
|
-
circle.setAttribute("cx", "40");
|
|
569
|
-
circle.setAttribute("cy", "40");
|
|
570
|
-
circle.setAttribute("r", "40");
|
|
571
|
-
circle.setAttribute("fill", "rgba(255, 255, 255, 0.95)");
|
|
572
|
-
circle.setAttribute("filter", `url(#${filterId})`);
|
|
573
|
-
circle.setAttribute("class", "vidply-play-overlay-bg");
|
|
574
|
-
svg.appendChild(circle);
|
|
575
|
-
const playTriangle = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
576
|
-
playTriangle.setAttribute("points", "32,28 32,52 54,40");
|
|
577
|
-
playTriangle.setAttribute("fill", "#0a406e");
|
|
578
|
-
playTriangle.setAttribute("class", "vidply-play-overlay-icon");
|
|
579
|
-
svg.appendChild(playTriangle);
|
|
580
|
-
return svg;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
405
|
// src/i18n/translations.js
|
|
584
406
|
var translations = {
|
|
585
407
|
en: {
|
|
@@ -680,6 +502,16 @@ var translations = {
|
|
|
680
502
|
},
|
|
681
503
|
speeds: {
|
|
682
504
|
normal: "Normal"
|
|
505
|
+
},
|
|
506
|
+
time: {
|
|
507
|
+
display: "Time display",
|
|
508
|
+
durationPrefix: "Duration: ",
|
|
509
|
+
hour: "{count} hour",
|
|
510
|
+
hours: "{count} hours",
|
|
511
|
+
minute: "{count} minute",
|
|
512
|
+
minutes: "{count} minutes",
|
|
513
|
+
second: "{count} second",
|
|
514
|
+
seconds: "{count} seconds"
|
|
683
515
|
}
|
|
684
516
|
},
|
|
685
517
|
de: {
|
|
@@ -780,6 +612,16 @@ var translations = {
|
|
|
780
612
|
},
|
|
781
613
|
speeds: {
|
|
782
614
|
normal: "Normal"
|
|
615
|
+
},
|
|
616
|
+
time: {
|
|
617
|
+
display: "Zeitanzeige",
|
|
618
|
+
durationPrefix: "Dauer: ",
|
|
619
|
+
hour: "{count} Stunde",
|
|
620
|
+
hours: "{count} Stunden",
|
|
621
|
+
minute: "{count} Minute",
|
|
622
|
+
minutes: "{count} Minuten",
|
|
623
|
+
second: "{count} Sekunde",
|
|
624
|
+
seconds: "{count} Sekunden"
|
|
783
625
|
}
|
|
784
626
|
},
|
|
785
627
|
es: {
|
|
@@ -880,6 +722,16 @@ var translations = {
|
|
|
880
722
|
},
|
|
881
723
|
speeds: {
|
|
882
724
|
normal: "Normal"
|
|
725
|
+
},
|
|
726
|
+
time: {
|
|
727
|
+
display: "Visualizaci\xF3n de tiempo",
|
|
728
|
+
durationPrefix: "Duraci\xF3n: ",
|
|
729
|
+
hour: "{count} hora",
|
|
730
|
+
hours: "{count} horas",
|
|
731
|
+
minute: "{count} minuto",
|
|
732
|
+
minutes: "{count} minutos",
|
|
733
|
+
second: "{count} segundo",
|
|
734
|
+
seconds: "{count} segundos"
|
|
883
735
|
}
|
|
884
736
|
},
|
|
885
737
|
fr: {
|
|
@@ -980,6 +832,16 @@ var translations = {
|
|
|
980
832
|
},
|
|
981
833
|
speeds: {
|
|
982
834
|
normal: "Normal"
|
|
835
|
+
},
|
|
836
|
+
time: {
|
|
837
|
+
display: "Affichage du temps",
|
|
838
|
+
durationPrefix: "Dur\xE9e : ",
|
|
839
|
+
hour: "{count} heure",
|
|
840
|
+
hours: "{count} heures",
|
|
841
|
+
minute: "{count} minute",
|
|
842
|
+
minutes: "{count} minutes",
|
|
843
|
+
second: "{count} seconde",
|
|
844
|
+
seconds: "{count} secondes"
|
|
983
845
|
}
|
|
984
846
|
},
|
|
985
847
|
ja: {
|
|
@@ -1080,6 +942,16 @@ var translations = {
|
|
|
1080
942
|
},
|
|
1081
943
|
speeds: {
|
|
1082
944
|
normal: "\u901A\u5E38"
|
|
945
|
+
},
|
|
946
|
+
time: {
|
|
947
|
+
display: "\u6642\u9593\u8868\u793A",
|
|
948
|
+
durationPrefix: "\u518D\u751F\u6642\u9593: ",
|
|
949
|
+
hour: "{count}\u6642\u9593",
|
|
950
|
+
hours: "{count}\u6642\u9593",
|
|
951
|
+
minute: "{count}\u5206",
|
|
952
|
+
minutes: "{count}\u5206",
|
|
953
|
+
second: "{count}\u79D2",
|
|
954
|
+
seconds: "{count}\u79D2"
|
|
1083
955
|
}
|
|
1084
956
|
}
|
|
1085
957
|
};
|
|
@@ -1135,6 +1007,187 @@ var I18n = class {
|
|
|
1135
1007
|
};
|
|
1136
1008
|
var i18n = new I18n();
|
|
1137
1009
|
|
|
1010
|
+
// src/utils/TimeUtils.js
|
|
1011
|
+
var TimeUtils = {
|
|
1012
|
+
/**
|
|
1013
|
+
* Format seconds to time string (HH:MM:SS or MM:SS)
|
|
1014
|
+
*/
|
|
1015
|
+
formatTime(seconds, alwaysShowHours = false) {
|
|
1016
|
+
if (!isFinite(seconds) || seconds < 0) {
|
|
1017
|
+
return alwaysShowHours ? "00:00:00" : "00:00";
|
|
1018
|
+
}
|
|
1019
|
+
const hours = Math.floor(seconds / 3600);
|
|
1020
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
1021
|
+
const secs = Math.floor(seconds % 60);
|
|
1022
|
+
const pad = (num) => String(num).padStart(2, "0");
|
|
1023
|
+
if (hours > 0 || alwaysShowHours) {
|
|
1024
|
+
return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
|
|
1025
|
+
}
|
|
1026
|
+
return `${pad(minutes)}:${pad(secs)}`;
|
|
1027
|
+
},
|
|
1028
|
+
/**
|
|
1029
|
+
* Parse time string to seconds
|
|
1030
|
+
*/
|
|
1031
|
+
parseTime(timeString) {
|
|
1032
|
+
const parts = timeString.split(":").map((p) => parseInt(p, 10));
|
|
1033
|
+
if (parts.length === 3) {
|
|
1034
|
+
return parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
1035
|
+
} else if (parts.length === 2) {
|
|
1036
|
+
return parts[0] * 60 + parts[1];
|
|
1037
|
+
} else if (parts.length === 1) {
|
|
1038
|
+
return parts[0];
|
|
1039
|
+
}
|
|
1040
|
+
return 0;
|
|
1041
|
+
},
|
|
1042
|
+
/**
|
|
1043
|
+
* Format seconds to readable duration
|
|
1044
|
+
*/
|
|
1045
|
+
formatDuration(seconds) {
|
|
1046
|
+
if (!isFinite(seconds) || seconds < 0) {
|
|
1047
|
+
return i18n.t("time.seconds", { count: 0 });
|
|
1048
|
+
}
|
|
1049
|
+
const hours = Math.floor(seconds / 3600);
|
|
1050
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
1051
|
+
const secs = Math.floor(seconds % 60);
|
|
1052
|
+
const parts = [];
|
|
1053
|
+
if (hours > 0) {
|
|
1054
|
+
const key = hours === 1 ? "time.hour" : "time.hours";
|
|
1055
|
+
parts.push(i18n.t(key, { count: hours }));
|
|
1056
|
+
}
|
|
1057
|
+
if (minutes > 0) {
|
|
1058
|
+
const key = minutes === 1 ? "time.minute" : "time.minutes";
|
|
1059
|
+
parts.push(i18n.t(key, { count: minutes }));
|
|
1060
|
+
}
|
|
1061
|
+
if (secs > 0 || parts.length === 0) {
|
|
1062
|
+
const key = secs === 1 ? "time.second" : "time.seconds";
|
|
1063
|
+
parts.push(i18n.t(key, { count: secs }));
|
|
1064
|
+
}
|
|
1065
|
+
return parts.join(", ");
|
|
1066
|
+
},
|
|
1067
|
+
/**
|
|
1068
|
+
* Format percentage
|
|
1069
|
+
*/
|
|
1070
|
+
formatPercentage(value, total) {
|
|
1071
|
+
if (total === 0) return 0;
|
|
1072
|
+
return Math.round(value / total * 100);
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
// src/icons/Icons.js
|
|
1077
|
+
var iconPaths = {
|
|
1078
|
+
play: `<path d="M8 5v14l11-7z"/>`,
|
|
1079
|
+
pause: `<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>`,
|
|
1080
|
+
stop: `<rect x="6" y="6" width="12" height="12"/>`,
|
|
1081
|
+
rewind: `<path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/>`,
|
|
1082
|
+
forward: `<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>`,
|
|
1083
|
+
skipPrevious: `<path d="M6 6h2v12H6V6zm3 6l8.5 6V6L9 12z"/>`,
|
|
1084
|
+
skipNext: `<path d="M16 6h2v12h-2V6zM6 6l8.5 6L6 18V6z"/>`,
|
|
1085
|
+
restart: `<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>`,
|
|
1086
|
+
volumeHigh: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>`,
|
|
1087
|
+
volumeMedium: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
|
|
1088
|
+
volumeLow: `<path d="M7 9v6h4l5 5V4l-5 5H7z"/>`,
|
|
1089
|
+
volumeMuted: `<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>`,
|
|
1090
|
+
fullscreen: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
|
|
1091
|
+
fullscreenExit: `<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>`,
|
|
1092
|
+
settings: `<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94L14.4 2.81c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>`,
|
|
1093
|
+
captions: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/>`,
|
|
1094
|
+
captionsOff: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/><path d="M0 0h24v24H0z" fill="none"/>`,
|
|
1095
|
+
pip: `<path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/>`,
|
|
1096
|
+
speed: `<path d="M20.38 8.57l-1.23 1.85a8 8 0 0 1-.22 7.58H5.07A8 8 0 0 1 15.58 6.85l1.85-1.23A10 10 0 0 0 3.35 19a2 2 0 0 0 1.72 1h13.85a2 2 0 0 0 1.74-1 10 10 0 0 0-.27-10.44z"/><path d="M10.59 15.41a2 2 0 0 0 2.83 0l5.66-8.49-8.49 5.66a2 2 0 0 0 0 2.83z"/>`,
|
|
1097
|
+
close: `<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>`,
|
|
1098
|
+
check: `<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>`,
|
|
1099
|
+
arrowUp: `<path d="M7 14l5-5 5 5z"/>`,
|
|
1100
|
+
arrowDown: `<path d="M7 10l5 5 5-5z"/>`,
|
|
1101
|
+
arrowLeft: `<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>`,
|
|
1102
|
+
arrowRight: `<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>`,
|
|
1103
|
+
loading: `<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>`,
|
|
1104
|
+
error: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>`,
|
|
1105
|
+
download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
|
|
1106
|
+
link: `<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>`,
|
|
1107
|
+
playlist: `<path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/>`,
|
|
1108
|
+
language: `<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>`,
|
|
1109
|
+
hd: `<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/>`,
|
|
1110
|
+
transcript: `<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>`,
|
|
1111
|
+
audioDescription: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
|
|
1112
|
+
audioDescriptionOn: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/><circle cx="19" cy="16" r="3" fill="#3b82f6"/><path d="M18.5 17.5l1-1 1.5 1.5" stroke="white" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
|
|
1113
|
+
signLanguage: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
|
|
1114
|
+
signLanguageOn: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
|
|
1115
|
+
speaker: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
|
|
1116
|
+
music: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7zm-1.5 16c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
|
|
1117
|
+
moreVertical: `<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
|
|
1118
|
+
moreHorizontal: `<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`
|
|
1119
|
+
};
|
|
1120
|
+
var svgWrapper = (paths) => `<svg viewBox="0 0 24 24" fill="currentColor">${paths}</svg>`;
|
|
1121
|
+
var Icons = Object.fromEntries(
|
|
1122
|
+
Object.entries(iconPaths).map(([key, value]) => [key, svgWrapper(value)])
|
|
1123
|
+
);
|
|
1124
|
+
function getIcon(name) {
|
|
1125
|
+
return Icons[name] || Icons.play;
|
|
1126
|
+
}
|
|
1127
|
+
function createIconElement(name, className = "") {
|
|
1128
|
+
const wrapper = document.createElement("span");
|
|
1129
|
+
wrapper.className = `vidply-icon ${className}`.trim();
|
|
1130
|
+
wrapper.innerHTML = getIcon(name);
|
|
1131
|
+
wrapper.setAttribute("aria-hidden", "true");
|
|
1132
|
+
return wrapper;
|
|
1133
|
+
}
|
|
1134
|
+
function createPlayOverlay() {
|
|
1135
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1136
|
+
svg.setAttribute("class", "vidply-play-overlay");
|
|
1137
|
+
svg.setAttribute("viewBox", "0 0 80 80");
|
|
1138
|
+
svg.setAttribute("width", "80");
|
|
1139
|
+
svg.setAttribute("height", "80");
|
|
1140
|
+
svg.setAttribute("aria-hidden", "true");
|
|
1141
|
+
svg.setAttribute("role", "presentation");
|
|
1142
|
+
svg.style.cursor = "pointer";
|
|
1143
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
1144
|
+
const filterId = `vidply-play-shadow-${Math.random().toString(36).substr(2, 9)}`;
|
|
1145
|
+
const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
|
1146
|
+
filter.setAttribute("id", filterId);
|
|
1147
|
+
filter.setAttribute("x", "-50%");
|
|
1148
|
+
filter.setAttribute("y", "-50%");
|
|
1149
|
+
filter.setAttribute("width", "200%");
|
|
1150
|
+
filter.setAttribute("height", "200%");
|
|
1151
|
+
const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
|
1152
|
+
feGaussianBlur.setAttribute("in", "SourceAlpha");
|
|
1153
|
+
feGaussianBlur.setAttribute("stdDeviation", "3");
|
|
1154
|
+
const feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
|
|
1155
|
+
feOffset.setAttribute("dx", "0");
|
|
1156
|
+
feOffset.setAttribute("dy", "2");
|
|
1157
|
+
feOffset.setAttribute("result", "offsetblur");
|
|
1158
|
+
const feComponentTransfer = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
|
1159
|
+
const feFuncA = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
|
|
1160
|
+
feFuncA.setAttribute("type", "linear");
|
|
1161
|
+
feFuncA.setAttribute("slope", "0.3");
|
|
1162
|
+
feComponentTransfer.appendChild(feFuncA);
|
|
1163
|
+
const feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
|
1164
|
+
const feMergeNode1 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1165
|
+
const feMergeNode2 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1166
|
+
feMergeNode2.setAttribute("in", "SourceGraphic");
|
|
1167
|
+
feMerge.appendChild(feMergeNode1);
|
|
1168
|
+
feMerge.appendChild(feMergeNode2);
|
|
1169
|
+
filter.appendChild(feGaussianBlur);
|
|
1170
|
+
filter.appendChild(feOffset);
|
|
1171
|
+
filter.appendChild(feComponentTransfer);
|
|
1172
|
+
filter.appendChild(feMerge);
|
|
1173
|
+
defs.appendChild(filter);
|
|
1174
|
+
svg.appendChild(defs);
|
|
1175
|
+
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1176
|
+
circle.setAttribute("cx", "40");
|
|
1177
|
+
circle.setAttribute("cy", "40");
|
|
1178
|
+
circle.setAttribute("r", "40");
|
|
1179
|
+
circle.setAttribute("fill", "rgba(255, 255, 255, 0.95)");
|
|
1180
|
+
circle.setAttribute("filter", `url(#${filterId})`);
|
|
1181
|
+
circle.setAttribute("class", "vidply-play-overlay-bg");
|
|
1182
|
+
svg.appendChild(circle);
|
|
1183
|
+
const playTriangle = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
1184
|
+
playTriangle.setAttribute("points", "32,28 32,52 54,40");
|
|
1185
|
+
playTriangle.setAttribute("fill", "#0a406e");
|
|
1186
|
+
playTriangle.setAttribute("class", "vidply-play-overlay-icon");
|
|
1187
|
+
svg.appendChild(playTriangle);
|
|
1188
|
+
return svg;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1138
1191
|
// src/controls/ControlBar.js
|
|
1139
1192
|
var ControlBar = class {
|
|
1140
1193
|
constructor(player) {
|
|
@@ -1651,12 +1704,26 @@ var ControlBar = class {
|
|
|
1651
1704
|
}
|
|
1652
1705
|
createTimeDisplay() {
|
|
1653
1706
|
const container = DOMUtils.createElement("div", {
|
|
1654
|
-
className: `${this.player.options.classPrefix}-time
|
|
1707
|
+
className: `${this.player.options.classPrefix}-time`,
|
|
1708
|
+
attributes: {
|
|
1709
|
+
"role": "group",
|
|
1710
|
+
"aria-label": i18n.t("time.display")
|
|
1711
|
+
}
|
|
1655
1712
|
});
|
|
1656
1713
|
this.controls.currentTimeDisplay = DOMUtils.createElement("span", {
|
|
1657
1714
|
className: `${this.player.options.classPrefix}-current-time`,
|
|
1658
|
-
|
|
1715
|
+
attributes: {
|
|
1716
|
+
"aria-label": i18n.t("time.seconds", { count: 0 })
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
const currentTimeVisual = DOMUtils.createElement("span", {
|
|
1720
|
+
textContent: "00:00",
|
|
1721
|
+
attributes: {
|
|
1722
|
+
"aria-hidden": "true"
|
|
1723
|
+
}
|
|
1659
1724
|
});
|
|
1725
|
+
this.controls.currentTimeDisplay.appendChild(currentTimeVisual);
|
|
1726
|
+
this.controls.currentTimeVisual = currentTimeVisual;
|
|
1660
1727
|
const separator = DOMUtils.createElement("span", {
|
|
1661
1728
|
textContent: " / ",
|
|
1662
1729
|
attributes: {
|
|
@@ -1665,8 +1732,18 @@ var ControlBar = class {
|
|
|
1665
1732
|
});
|
|
1666
1733
|
this.controls.durationDisplay = DOMUtils.createElement("span", {
|
|
1667
1734
|
className: `${this.player.options.classPrefix}-duration`,
|
|
1668
|
-
|
|
1735
|
+
attributes: {
|
|
1736
|
+
"aria-label": i18n.t("time.durationPrefix") + i18n.t("time.seconds", { count: 0 })
|
|
1737
|
+
}
|
|
1669
1738
|
});
|
|
1739
|
+
const durationVisual = DOMUtils.createElement("span", {
|
|
1740
|
+
textContent: "00:00",
|
|
1741
|
+
attributes: {
|
|
1742
|
+
"aria-hidden": "true"
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
this.controls.durationDisplay.appendChild(durationVisual);
|
|
1746
|
+
this.controls.durationVisual = durationVisual;
|
|
1670
1747
|
container.appendChild(this.controls.currentTimeDisplay);
|
|
1671
1748
|
container.appendChild(separator);
|
|
1672
1749
|
container.appendChild(this.controls.durationDisplay);
|
|
@@ -2490,13 +2567,17 @@ var ControlBar = class {
|
|
|
2490
2567
|
const percent = this.player.state.currentTime / this.player.state.duration * 100;
|
|
2491
2568
|
this.controls.played.style.width = `${percent}%`;
|
|
2492
2569
|
this.controls.progress.setAttribute("aria-valuenow", String(Math.round(percent)));
|
|
2493
|
-
if (this.controls.
|
|
2494
|
-
|
|
2570
|
+
if (this.controls.currentTimeVisual) {
|
|
2571
|
+
const currentTime = this.player.state.currentTime;
|
|
2572
|
+
this.controls.currentTimeVisual.textContent = TimeUtils.formatTime(currentTime);
|
|
2573
|
+
this.controls.currentTimeDisplay.setAttribute("aria-label", TimeUtils.formatDuration(currentTime));
|
|
2495
2574
|
}
|
|
2496
2575
|
}
|
|
2497
2576
|
updateDuration() {
|
|
2498
|
-
if (this.controls.
|
|
2499
|
-
|
|
2577
|
+
if (this.controls.durationVisual) {
|
|
2578
|
+
const duration = this.player.state.duration;
|
|
2579
|
+
this.controls.durationVisual.textContent = TimeUtils.formatTime(duration);
|
|
2580
|
+
this.controls.durationDisplay.setAttribute("aria-label", i18n.t("time.durationPrefix") + TimeUtils.formatDuration(duration));
|
|
2500
2581
|
}
|
|
2501
2582
|
}
|
|
2502
2583
|
updateVolumeDisplay() {
|
|
@@ -4558,6 +4639,17 @@ var Player = class extends EventEmitter {
|
|
|
4558
4639
|
this.captionManager.destroy();
|
|
4559
4640
|
this.captionManager = new CaptionManager(this);
|
|
4560
4641
|
}
|
|
4642
|
+
if (this.transcriptManager) {
|
|
4643
|
+
const wasVisible = this.transcriptManager.isVisible;
|
|
4644
|
+
this.transcriptManager.destroy();
|
|
4645
|
+
this.transcriptManager = new TranscriptManager(this);
|
|
4646
|
+
if (wasVisible) {
|
|
4647
|
+
this.transcriptManager.showTranscript();
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
if (this.controlBar) {
|
|
4651
|
+
this.updateControlBar();
|
|
4652
|
+
}
|
|
4561
4653
|
this.emit("sourcechange", config);
|
|
4562
4654
|
this.log("Media loaded successfully");
|
|
4563
4655
|
} catch (error) {
|
|
@@ -4569,6 +4661,17 @@ var Player = class extends EventEmitter {
|
|
|
4569
4661
|
* @param {string} src - New source URL
|
|
4570
4662
|
* @returns {boolean}
|
|
4571
4663
|
*/
|
|
4664
|
+
/**
|
|
4665
|
+
* Update control bar to refresh button visibility based on available features
|
|
4666
|
+
*/
|
|
4667
|
+
updateControlBar() {
|
|
4668
|
+
if (!this.controlBar) return;
|
|
4669
|
+
const controlBar = this.controlBar;
|
|
4670
|
+
controlBar.element.innerHTML = "";
|
|
4671
|
+
controlBar.createControls();
|
|
4672
|
+
controlBar.attachEvents();
|
|
4673
|
+
controlBar.setupAutoHide();
|
|
4674
|
+
}
|
|
4572
4675
|
shouldChangeRenderer(src) {
|
|
4573
4676
|
if (!this.renderer) return true;
|
|
4574
4677
|
const isYouTube = src.includes("youtube.com") || src.includes("youtu.be");
|
|
@@ -5028,7 +5131,9 @@ var PlaylistManager = class {
|
|
|
5028
5131
|
this.trackInfoElement = null;
|
|
5029
5132
|
this.handleTrackEnd = this.handleTrackEnd.bind(this);
|
|
5030
5133
|
this.handleTrackError = this.handleTrackError.bind(this);
|
|
5134
|
+
this.player.playlistManager = this;
|
|
5031
5135
|
this.init();
|
|
5136
|
+
this.updatePlayerControls();
|
|
5032
5137
|
}
|
|
5033
5138
|
init() {
|
|
5034
5139
|
this.player.on("ended", this.handleTrackEnd);
|
|
@@ -5037,6 +5142,17 @@ var PlaylistManager = class {
|
|
|
5037
5142
|
this.createUI();
|
|
5038
5143
|
}
|
|
5039
5144
|
}
|
|
5145
|
+
/**
|
|
5146
|
+
* Update player controls to add playlist navigation buttons
|
|
5147
|
+
*/
|
|
5148
|
+
updatePlayerControls() {
|
|
5149
|
+
if (!this.player.controlBar) return;
|
|
5150
|
+
const controlBar = this.player.controlBar;
|
|
5151
|
+
controlBar.element.innerHTML = "";
|
|
5152
|
+
controlBar.createControls();
|
|
5153
|
+
controlBar.attachEvents();
|
|
5154
|
+
controlBar.setupAutoHide();
|
|
5155
|
+
}
|
|
5040
5156
|
/**
|
|
5041
5157
|
* Load a playlist
|
|
5042
5158
|
* @param {Array} tracks - Array of track objects
|
|
@@ -5057,8 +5173,9 @@ var PlaylistManager = class {
|
|
|
5057
5173
|
/**
|
|
5058
5174
|
* Play a specific track
|
|
5059
5175
|
* @param {number} index - Track index
|
|
5176
|
+
* @param {boolean} userInitiated - Whether this was triggered by user action (default: false)
|
|
5060
5177
|
*/
|
|
5061
|
-
play(index) {
|
|
5178
|
+
play(index, userInitiated = false) {
|
|
5062
5179
|
if (index < 0 || index >= this.tracks.length) {
|
|
5063
5180
|
console.warn("VidPly Playlist: Invalid track index", index);
|
|
5064
5181
|
return;
|
|
@@ -5078,6 +5195,9 @@ var PlaylistManager = class {
|
|
|
5078
5195
|
item: track,
|
|
5079
5196
|
total: this.tracks.length
|
|
5080
5197
|
});
|
|
5198
|
+
if (userInitiated && this.player.container) {
|
|
5199
|
+
this.player.container.focus();
|
|
5200
|
+
}
|
|
5081
5201
|
setTimeout(() => {
|
|
5082
5202
|
this.player.play();
|
|
5083
5203
|
}, 100);
|
|
@@ -5139,12 +5259,17 @@ var PlaylistManager = class {
|
|
|
5139
5259
|
return;
|
|
5140
5260
|
}
|
|
5141
5261
|
this.trackInfoElement = DOMUtils.createElement("div", {
|
|
5142
|
-
className: "vidply-track-info"
|
|
5262
|
+
className: "vidply-track-info",
|
|
5263
|
+
role: "status",
|
|
5264
|
+
"aria-live": "polite",
|
|
5265
|
+
"aria-atomic": "true"
|
|
5143
5266
|
});
|
|
5144
5267
|
this.trackInfoElement.style.display = "none";
|
|
5145
5268
|
this.container.appendChild(this.trackInfoElement);
|
|
5146
5269
|
this.playlistPanel = DOMUtils.createElement("div", {
|
|
5147
|
-
className: "vidply-playlist-panel"
|
|
5270
|
+
className: "vidply-playlist-panel",
|
|
5271
|
+
role: "region",
|
|
5272
|
+
"aria-label": "Media playlist"
|
|
5148
5273
|
});
|
|
5149
5274
|
this.playlistPanel.style.display = "none";
|
|
5150
5275
|
this.container.appendChild(this.playlistPanel);
|
|
@@ -5156,10 +5281,14 @@ var PlaylistManager = class {
|
|
|
5156
5281
|
if (!this.trackInfoElement) return;
|
|
5157
5282
|
const trackNumber = this.currentIndex + 1;
|
|
5158
5283
|
const totalTracks = this.tracks.length;
|
|
5284
|
+
const trackTitle = track.title || "Untitled";
|
|
5285
|
+
const trackArtist = track.artist || "";
|
|
5286
|
+
const announcement = `Now playing: Track ${trackNumber} of ${totalTracks}. ${trackTitle}${trackArtist ? " by " + trackArtist : ""}`;
|
|
5159
5287
|
this.trackInfoElement.innerHTML = `
|
|
5160
|
-
<
|
|
5161
|
-
<div class="vidply-track-
|
|
5162
|
-
|
|
5288
|
+
<span class="vidply-sr-only">${DOMUtils.escapeHTML(announcement)}</span>
|
|
5289
|
+
<div class="vidply-track-number" aria-hidden="true">Track ${trackNumber} of ${totalTracks}</div>
|
|
5290
|
+
<div class="vidply-track-title" aria-hidden="true">${DOMUtils.escapeHTML(trackTitle)}</div>
|
|
5291
|
+
${trackArtist ? `<div class="vidply-track-artist" aria-hidden="true">${DOMUtils.escapeHTML(trackArtist)}</div>` : ""}
|
|
5163
5292
|
`;
|
|
5164
5293
|
this.trackInfoElement.style.display = "block";
|
|
5165
5294
|
}
|
|
@@ -5169,14 +5298,29 @@ var PlaylistManager = class {
|
|
|
5169
5298
|
renderPlaylist() {
|
|
5170
5299
|
if (!this.playlistPanel) return;
|
|
5171
5300
|
this.playlistPanel.innerHTML = "";
|
|
5172
|
-
const header = DOMUtils.createElement("
|
|
5173
|
-
className: "vidply-playlist-header"
|
|
5301
|
+
const header = DOMUtils.createElement("h2", {
|
|
5302
|
+
className: "vidply-playlist-header",
|
|
5303
|
+
id: "vidply-playlist-heading"
|
|
5174
5304
|
});
|
|
5175
5305
|
header.textContent = `Playlist (${this.tracks.length})`;
|
|
5176
5306
|
this.playlistPanel.appendChild(header);
|
|
5177
|
-
const
|
|
5178
|
-
className: "vidply-
|
|
5179
|
-
|
|
5307
|
+
const instructions = DOMUtils.createElement("div", {
|
|
5308
|
+
className: "vidply-sr-only",
|
|
5309
|
+
"aria-hidden": "false"
|
|
5310
|
+
});
|
|
5311
|
+
instructions.textContent = "Use arrow keys to navigate between tracks. Press Enter or Space to play a track. Press Home or End to jump to first or last track.";
|
|
5312
|
+
this.playlistPanel.appendChild(instructions);
|
|
5313
|
+
const list = DOMUtils.createElement("ul", {
|
|
5314
|
+
className: "vidply-playlist-list",
|
|
5315
|
+
"aria-labelledby": "vidply-playlist-heading",
|
|
5316
|
+
"aria-describedby": "vidply-playlist-instructions"
|
|
5317
|
+
});
|
|
5318
|
+
const listDescription = DOMUtils.createElement("div", {
|
|
5319
|
+
className: "vidply-sr-only",
|
|
5320
|
+
id: "vidply-playlist-instructions"
|
|
5321
|
+
});
|
|
5322
|
+
listDescription.textContent = `Playlist with ${this.tracks.length} ${this.tracks.length === 1 ? "track" : "tracks"}`;
|
|
5323
|
+
this.playlistPanel.appendChild(listDescription);
|
|
5180
5324
|
this.tracks.forEach((track, index) => {
|
|
5181
5325
|
const item = this.createPlaylistItem(track, index);
|
|
5182
5326
|
list.appendChild(item);
|
|
@@ -5188,20 +5332,39 @@ var PlaylistManager = class {
|
|
|
5188
5332
|
* Create playlist item element
|
|
5189
5333
|
*/
|
|
5190
5334
|
createPlaylistItem(track, index) {
|
|
5191
|
-
const
|
|
5335
|
+
const trackPosition = `Track ${index + 1} of ${this.tracks.length}`;
|
|
5336
|
+
const trackTitle = track.title || `Track ${index + 1}`;
|
|
5337
|
+
const trackArtist = track.artist ? ` by ${track.artist}` : "";
|
|
5338
|
+
const isActive = index === this.currentIndex;
|
|
5339
|
+
const statusText = isActive ? "Currently playing" : "Not playing";
|
|
5340
|
+
const actionText = isActive ? "Press Enter to restart" : "Press Enter to play";
|
|
5341
|
+
const item = DOMUtils.createElement("li", {
|
|
5192
5342
|
className: "vidply-playlist-item",
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
"aria-label":
|
|
5196
|
-
|
|
5197
|
-
|
|
5343
|
+
tabIndex: index === 0 ? 0 : -1,
|
|
5344
|
+
// Only first item is in tab order initially
|
|
5345
|
+
"aria-label": `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`,
|
|
5346
|
+
"aria-posinset": index + 1,
|
|
5347
|
+
"aria-setsize": this.tracks.length,
|
|
5348
|
+
"data-playlist-index": index
|
|
5349
|
+
});
|
|
5350
|
+
if (isActive) {
|
|
5198
5351
|
item.classList.add("vidply-playlist-item-active");
|
|
5352
|
+
item.setAttribute("aria-current", "true");
|
|
5353
|
+
item.setAttribute("tabIndex", "0");
|
|
5199
5354
|
}
|
|
5355
|
+
const positionInfo = DOMUtils.createElement("span", {
|
|
5356
|
+
className: "vidply-sr-only"
|
|
5357
|
+
});
|
|
5358
|
+
positionInfo.textContent = `${trackPosition}: `;
|
|
5359
|
+
item.appendChild(positionInfo);
|
|
5200
5360
|
const thumbnail = DOMUtils.createElement("div", {
|
|
5201
|
-
className: "vidply-playlist-thumbnail"
|
|
5361
|
+
className: "vidply-playlist-thumbnail",
|
|
5362
|
+
"aria-hidden": "true"
|
|
5202
5363
|
});
|
|
5203
5364
|
if (track.poster) {
|
|
5204
5365
|
thumbnail.style.backgroundImage = `url(${track.poster})`;
|
|
5366
|
+
thumbnail.setAttribute("role", "img");
|
|
5367
|
+
thumbnail.setAttribute("aria-label", `${trackTitle} thumbnail`);
|
|
5205
5368
|
} else {
|
|
5206
5369
|
const icon = createIconElement("music");
|
|
5207
5370
|
icon.classList.add("vidply-playlist-thumbnail-icon");
|
|
@@ -5209,12 +5372,13 @@ var PlaylistManager = class {
|
|
|
5209
5372
|
}
|
|
5210
5373
|
item.appendChild(thumbnail);
|
|
5211
5374
|
const info = DOMUtils.createElement("div", {
|
|
5212
|
-
className: "vidply-playlist-item-info"
|
|
5375
|
+
className: "vidply-playlist-item-info",
|
|
5376
|
+
"aria-hidden": "true"
|
|
5213
5377
|
});
|
|
5214
5378
|
const title = DOMUtils.createElement("div", {
|
|
5215
5379
|
className: "vidply-playlist-item-title"
|
|
5216
5380
|
});
|
|
5217
|
-
title.textContent =
|
|
5381
|
+
title.textContent = trackTitle;
|
|
5218
5382
|
info.appendChild(title);
|
|
5219
5383
|
if (track.artist) {
|
|
5220
5384
|
const artist = DOMUtils.createElement("div", {
|
|
@@ -5224,20 +5388,64 @@ var PlaylistManager = class {
|
|
|
5224
5388
|
info.appendChild(artist);
|
|
5225
5389
|
}
|
|
5226
5390
|
item.appendChild(info);
|
|
5391
|
+
if (isActive) {
|
|
5392
|
+
const statusIndicator = DOMUtils.createElement("span", {
|
|
5393
|
+
className: "vidply-sr-only"
|
|
5394
|
+
});
|
|
5395
|
+
statusIndicator.textContent = " (Currently playing)";
|
|
5396
|
+
item.appendChild(statusIndicator);
|
|
5397
|
+
}
|
|
5227
5398
|
const playIcon = createIconElement("play");
|
|
5228
5399
|
playIcon.classList.add("vidply-playlist-item-icon");
|
|
5400
|
+
playIcon.setAttribute("aria-hidden", "true");
|
|
5229
5401
|
item.appendChild(playIcon);
|
|
5230
5402
|
item.addEventListener("click", () => {
|
|
5231
|
-
this.play(index);
|
|
5403
|
+
this.play(index, true);
|
|
5232
5404
|
});
|
|
5233
5405
|
item.addEventListener("keydown", (e) => {
|
|
5234
|
-
|
|
5235
|
-
e.preventDefault();
|
|
5236
|
-
this.play(index);
|
|
5237
|
-
}
|
|
5406
|
+
this.handlePlaylistItemKeydown(e, index);
|
|
5238
5407
|
});
|
|
5239
5408
|
return item;
|
|
5240
5409
|
}
|
|
5410
|
+
/**
|
|
5411
|
+
* Handle keyboard navigation in playlist items
|
|
5412
|
+
*/
|
|
5413
|
+
handlePlaylistItemKeydown(e, index) {
|
|
5414
|
+
const items = Array.from(this.playlistPanel.querySelectorAll(".vidply-playlist-item"));
|
|
5415
|
+
let newIndex = -1;
|
|
5416
|
+
switch (e.key) {
|
|
5417
|
+
case "Enter":
|
|
5418
|
+
case " ":
|
|
5419
|
+
e.preventDefault();
|
|
5420
|
+
this.play(index, true);
|
|
5421
|
+
break;
|
|
5422
|
+
case "ArrowDown":
|
|
5423
|
+
e.preventDefault();
|
|
5424
|
+
if (index < items.length - 1) {
|
|
5425
|
+
newIndex = index + 1;
|
|
5426
|
+
}
|
|
5427
|
+
break;
|
|
5428
|
+
case "ArrowUp":
|
|
5429
|
+
e.preventDefault();
|
|
5430
|
+
if (index > 0) {
|
|
5431
|
+
newIndex = index - 1;
|
|
5432
|
+
}
|
|
5433
|
+
break;
|
|
5434
|
+
case "Home":
|
|
5435
|
+
e.preventDefault();
|
|
5436
|
+
newIndex = 0;
|
|
5437
|
+
break;
|
|
5438
|
+
case "End":
|
|
5439
|
+
e.preventDefault();
|
|
5440
|
+
newIndex = items.length - 1;
|
|
5441
|
+
break;
|
|
5442
|
+
}
|
|
5443
|
+
if (newIndex !== -1 && newIndex !== index) {
|
|
5444
|
+
items[index].setAttribute("tabIndex", "-1");
|
|
5445
|
+
items[newIndex].setAttribute("tabIndex", "0");
|
|
5446
|
+
items[newIndex].focus();
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5241
5449
|
/**
|
|
5242
5450
|
* Update playlist UI (highlight current track)
|
|
5243
5451
|
*/
|
|
@@ -5245,11 +5453,25 @@ var PlaylistManager = class {
|
|
|
5245
5453
|
if (!this.playlistPanel) return;
|
|
5246
5454
|
const items = this.playlistPanel.querySelectorAll(".vidply-playlist-item");
|
|
5247
5455
|
items.forEach((item, index) => {
|
|
5456
|
+
const track = this.tracks[index];
|
|
5457
|
+
const trackPosition = `Track ${index + 1} of ${this.tracks.length}`;
|
|
5458
|
+
const trackTitle = track.title || `Track ${index + 1}`;
|
|
5459
|
+
const trackArtist = track.artist ? ` by ${track.artist}` : "";
|
|
5248
5460
|
if (index === this.currentIndex) {
|
|
5249
5461
|
item.classList.add("vidply-playlist-item-active");
|
|
5462
|
+
item.setAttribute("aria-current", "true");
|
|
5463
|
+
item.setAttribute("tabIndex", "0");
|
|
5464
|
+
const statusText = "Currently playing";
|
|
5465
|
+
const actionText = "Press Enter to restart";
|
|
5466
|
+
item.setAttribute("aria-label", `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`);
|
|
5250
5467
|
item.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
5251
5468
|
} else {
|
|
5252
5469
|
item.classList.remove("vidply-playlist-item-active");
|
|
5470
|
+
item.removeAttribute("aria-current");
|
|
5471
|
+
item.setAttribute("tabIndex", "-1");
|
|
5472
|
+
const statusText = "Not playing";
|
|
5473
|
+
const actionText = "Press Enter to play";
|
|
5474
|
+
item.setAttribute("aria-label", `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`);
|
|
5253
5475
|
}
|
|
5254
5476
|
});
|
|
5255
5477
|
}
|
|
@@ -5267,10 +5489,24 @@ var PlaylistManager = class {
|
|
|
5267
5489
|
currentIndex: this.currentIndex,
|
|
5268
5490
|
totalTracks: this.tracks.length,
|
|
5269
5491
|
currentTrack: this.getCurrentTrack(),
|
|
5270
|
-
hasNext: this.
|
|
5271
|
-
hasPrevious: this.
|
|
5492
|
+
hasNext: this.hasNext(),
|
|
5493
|
+
hasPrevious: this.hasPrevious()
|
|
5272
5494
|
};
|
|
5273
5495
|
}
|
|
5496
|
+
/**
|
|
5497
|
+
* Check if there is a next track
|
|
5498
|
+
*/
|
|
5499
|
+
hasNext() {
|
|
5500
|
+
if (this.options.loop) return true;
|
|
5501
|
+
return this.currentIndex < this.tracks.length - 1;
|
|
5502
|
+
}
|
|
5503
|
+
/**
|
|
5504
|
+
* Check if there is a previous track
|
|
5505
|
+
*/
|
|
5506
|
+
hasPrevious() {
|
|
5507
|
+
if (this.options.loop) return true;
|
|
5508
|
+
return this.currentIndex > 0;
|
|
5509
|
+
}
|
|
5274
5510
|
/**
|
|
5275
5511
|
* Add track to playlist
|
|
5276
5512
|
*/
|