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.js
CHANGED
|
@@ -422,184 +422,6 @@ var VidPly = (() => {
|
|
|
422
422
|
}
|
|
423
423
|
};
|
|
424
424
|
|
|
425
|
-
// src/utils/TimeUtils.js
|
|
426
|
-
var TimeUtils = {
|
|
427
|
-
/**
|
|
428
|
-
* Format seconds to time string (HH:MM:SS or MM:SS)
|
|
429
|
-
*/
|
|
430
|
-
formatTime(seconds, alwaysShowHours = false) {
|
|
431
|
-
if (!isFinite(seconds) || seconds < 0) {
|
|
432
|
-
return alwaysShowHours ? "00:00:00" : "00:00";
|
|
433
|
-
}
|
|
434
|
-
const hours = Math.floor(seconds / 3600);
|
|
435
|
-
const minutes = Math.floor(seconds % 3600 / 60);
|
|
436
|
-
const secs = Math.floor(seconds % 60);
|
|
437
|
-
const pad = (num) => String(num).padStart(2, "0");
|
|
438
|
-
if (hours > 0 || alwaysShowHours) {
|
|
439
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
|
|
440
|
-
}
|
|
441
|
-
return `${pad(minutes)}:${pad(secs)}`;
|
|
442
|
-
},
|
|
443
|
-
/**
|
|
444
|
-
* Parse time string to seconds
|
|
445
|
-
*/
|
|
446
|
-
parseTime(timeString) {
|
|
447
|
-
const parts = timeString.split(":").map((p) => parseInt(p, 10));
|
|
448
|
-
if (parts.length === 3) {
|
|
449
|
-
return parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
450
|
-
} else if (parts.length === 2) {
|
|
451
|
-
return parts[0] * 60 + parts[1];
|
|
452
|
-
} else if (parts.length === 1) {
|
|
453
|
-
return parts[0];
|
|
454
|
-
}
|
|
455
|
-
return 0;
|
|
456
|
-
},
|
|
457
|
-
/**
|
|
458
|
-
* Format seconds to readable duration
|
|
459
|
-
*/
|
|
460
|
-
formatDuration(seconds) {
|
|
461
|
-
if (!isFinite(seconds) || seconds < 0) {
|
|
462
|
-
return "0 seconds";
|
|
463
|
-
}
|
|
464
|
-
const hours = Math.floor(seconds / 3600);
|
|
465
|
-
const minutes = Math.floor(seconds % 3600 / 60);
|
|
466
|
-
const secs = Math.floor(seconds % 60);
|
|
467
|
-
const parts = [];
|
|
468
|
-
if (hours > 0) {
|
|
469
|
-
parts.push(`${hours} hour${hours !== 1 ? "s" : ""}`);
|
|
470
|
-
}
|
|
471
|
-
if (minutes > 0) {
|
|
472
|
-
parts.push(`${minutes} minute${minutes !== 1 ? "s" : ""}`);
|
|
473
|
-
}
|
|
474
|
-
if (secs > 0 || parts.length === 0) {
|
|
475
|
-
parts.push(`${secs} second${secs !== 1 ? "s" : ""}`);
|
|
476
|
-
}
|
|
477
|
-
return parts.join(", ");
|
|
478
|
-
},
|
|
479
|
-
/**
|
|
480
|
-
* Format percentage
|
|
481
|
-
*/
|
|
482
|
-
formatPercentage(value, total) {
|
|
483
|
-
if (total === 0) return 0;
|
|
484
|
-
return Math.round(value / total * 100);
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
// src/icons/Icons.js
|
|
489
|
-
var iconPaths = {
|
|
490
|
-
play: `<path d="M8 5v14l11-7z"/>`,
|
|
491
|
-
pause: `<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>`,
|
|
492
|
-
stop: `<rect x="6" y="6" width="12" height="12"/>`,
|
|
493
|
-
rewind: `<path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/>`,
|
|
494
|
-
forward: `<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>`,
|
|
495
|
-
skipPrevious: `<path d="M6 6h2v12H6V6zm3 6l8.5 6V6L9 12z"/>`,
|
|
496
|
-
skipNext: `<path d="M16 6h2v12h-2V6zM6 6l8.5 6L6 18V6z"/>`,
|
|
497
|
-
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"/>`,
|
|
498
|
-
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"/>`,
|
|
499
|
-
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"/>`,
|
|
500
|
-
volumeLow: `<path d="M7 9v6h4l5 5V4l-5 5H7z"/>`,
|
|
501
|
-
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"/>`,
|
|
502
|
-
fullscreen: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
|
|
503
|
-
fullscreenExit: `<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>`,
|
|
504
|
-
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"/>`,
|
|
505
|
-
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"/>`,
|
|
506
|
-
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"/>`,
|
|
507
|
-
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"/>`,
|
|
508
|
-
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"/>`,
|
|
509
|
-
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"/>`,
|
|
510
|
-
check: `<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>`,
|
|
511
|
-
arrowUp: `<path d="M7 14l5-5 5 5z"/>`,
|
|
512
|
-
arrowDown: `<path d="M7 10l5 5 5-5z"/>`,
|
|
513
|
-
arrowLeft: `<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>`,
|
|
514
|
-
arrowRight: `<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>`,
|
|
515
|
-
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"/>`,
|
|
516
|
-
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"/>`,
|
|
517
|
-
download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
|
|
518
|
-
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"/>`,
|
|
519
|
-
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"/>`,
|
|
520
|
-
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"/>`,
|
|
521
|
-
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"/>`,
|
|
522
|
-
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"/>`,
|
|
523
|
-
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"/>`,
|
|
524
|
-
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"/>`,
|
|
525
|
-
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>`,
|
|
526
|
-
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>`,
|
|
527
|
-
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"/>`,
|
|
528
|
-
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"/>`,
|
|
529
|
-
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"/>`,
|
|
530
|
-
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"/>`
|
|
531
|
-
};
|
|
532
|
-
var svgWrapper = (paths) => `<svg viewBox="0 0 24 24" fill="currentColor">${paths}</svg>`;
|
|
533
|
-
var Icons = Object.fromEntries(
|
|
534
|
-
Object.entries(iconPaths).map(([key, value]) => [key, svgWrapper(value)])
|
|
535
|
-
);
|
|
536
|
-
function getIcon(name) {
|
|
537
|
-
return Icons[name] || Icons.play;
|
|
538
|
-
}
|
|
539
|
-
function createIconElement(name, className = "") {
|
|
540
|
-
const wrapper = document.createElement("span");
|
|
541
|
-
wrapper.className = `vidply-icon ${className}`.trim();
|
|
542
|
-
wrapper.innerHTML = getIcon(name);
|
|
543
|
-
wrapper.setAttribute("aria-hidden", "true");
|
|
544
|
-
return wrapper;
|
|
545
|
-
}
|
|
546
|
-
function createPlayOverlay() {
|
|
547
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
548
|
-
svg.setAttribute("class", "vidply-play-overlay");
|
|
549
|
-
svg.setAttribute("viewBox", "0 0 80 80");
|
|
550
|
-
svg.setAttribute("width", "80");
|
|
551
|
-
svg.setAttribute("height", "80");
|
|
552
|
-
svg.setAttribute("aria-hidden", "true");
|
|
553
|
-
svg.setAttribute("role", "presentation");
|
|
554
|
-
svg.style.cursor = "pointer";
|
|
555
|
-
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
556
|
-
const filterId = `vidply-play-shadow-${Math.random().toString(36).substr(2, 9)}`;
|
|
557
|
-
const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
|
558
|
-
filter.setAttribute("id", filterId);
|
|
559
|
-
filter.setAttribute("x", "-50%");
|
|
560
|
-
filter.setAttribute("y", "-50%");
|
|
561
|
-
filter.setAttribute("width", "200%");
|
|
562
|
-
filter.setAttribute("height", "200%");
|
|
563
|
-
const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
|
564
|
-
feGaussianBlur.setAttribute("in", "SourceAlpha");
|
|
565
|
-
feGaussianBlur.setAttribute("stdDeviation", "3");
|
|
566
|
-
const feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
|
|
567
|
-
feOffset.setAttribute("dx", "0");
|
|
568
|
-
feOffset.setAttribute("dy", "2");
|
|
569
|
-
feOffset.setAttribute("result", "offsetblur");
|
|
570
|
-
const feComponentTransfer = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
|
571
|
-
const feFuncA = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
|
|
572
|
-
feFuncA.setAttribute("type", "linear");
|
|
573
|
-
feFuncA.setAttribute("slope", "0.3");
|
|
574
|
-
feComponentTransfer.appendChild(feFuncA);
|
|
575
|
-
const feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
|
576
|
-
const feMergeNode1 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
577
|
-
const feMergeNode2 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
578
|
-
feMergeNode2.setAttribute("in", "SourceGraphic");
|
|
579
|
-
feMerge.appendChild(feMergeNode1);
|
|
580
|
-
feMerge.appendChild(feMergeNode2);
|
|
581
|
-
filter.appendChild(feGaussianBlur);
|
|
582
|
-
filter.appendChild(feOffset);
|
|
583
|
-
filter.appendChild(feComponentTransfer);
|
|
584
|
-
filter.appendChild(feMerge);
|
|
585
|
-
defs.appendChild(filter);
|
|
586
|
-
svg.appendChild(defs);
|
|
587
|
-
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
588
|
-
circle.setAttribute("cx", "40");
|
|
589
|
-
circle.setAttribute("cy", "40");
|
|
590
|
-
circle.setAttribute("r", "40");
|
|
591
|
-
circle.setAttribute("fill", "rgba(255, 255, 255, 0.95)");
|
|
592
|
-
circle.setAttribute("filter", `url(#${filterId})`);
|
|
593
|
-
circle.setAttribute("class", "vidply-play-overlay-bg");
|
|
594
|
-
svg.appendChild(circle);
|
|
595
|
-
const playTriangle = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
596
|
-
playTriangle.setAttribute("points", "32,28 32,52 54,40");
|
|
597
|
-
playTriangle.setAttribute("fill", "#0a406e");
|
|
598
|
-
playTriangle.setAttribute("class", "vidply-play-overlay-icon");
|
|
599
|
-
svg.appendChild(playTriangle);
|
|
600
|
-
return svg;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
425
|
// src/i18n/translations.js
|
|
604
426
|
var translations = {
|
|
605
427
|
en: {
|
|
@@ -700,6 +522,16 @@ var VidPly = (() => {
|
|
|
700
522
|
},
|
|
701
523
|
speeds: {
|
|
702
524
|
normal: "Normal"
|
|
525
|
+
},
|
|
526
|
+
time: {
|
|
527
|
+
display: "Time display",
|
|
528
|
+
durationPrefix: "Duration: ",
|
|
529
|
+
hour: "{count} hour",
|
|
530
|
+
hours: "{count} hours",
|
|
531
|
+
minute: "{count} minute",
|
|
532
|
+
minutes: "{count} minutes",
|
|
533
|
+
second: "{count} second",
|
|
534
|
+
seconds: "{count} seconds"
|
|
703
535
|
}
|
|
704
536
|
},
|
|
705
537
|
de: {
|
|
@@ -800,6 +632,16 @@ var VidPly = (() => {
|
|
|
800
632
|
},
|
|
801
633
|
speeds: {
|
|
802
634
|
normal: "Normal"
|
|
635
|
+
},
|
|
636
|
+
time: {
|
|
637
|
+
display: "Zeitanzeige",
|
|
638
|
+
durationPrefix: "Dauer: ",
|
|
639
|
+
hour: "{count} Stunde",
|
|
640
|
+
hours: "{count} Stunden",
|
|
641
|
+
minute: "{count} Minute",
|
|
642
|
+
minutes: "{count} Minuten",
|
|
643
|
+
second: "{count} Sekunde",
|
|
644
|
+
seconds: "{count} Sekunden"
|
|
803
645
|
}
|
|
804
646
|
},
|
|
805
647
|
es: {
|
|
@@ -900,6 +742,16 @@ var VidPly = (() => {
|
|
|
900
742
|
},
|
|
901
743
|
speeds: {
|
|
902
744
|
normal: "Normal"
|
|
745
|
+
},
|
|
746
|
+
time: {
|
|
747
|
+
display: "Visualizaci\xF3n de tiempo",
|
|
748
|
+
durationPrefix: "Duraci\xF3n: ",
|
|
749
|
+
hour: "{count} hora",
|
|
750
|
+
hours: "{count} horas",
|
|
751
|
+
minute: "{count} minuto",
|
|
752
|
+
minutes: "{count} minutos",
|
|
753
|
+
second: "{count} segundo",
|
|
754
|
+
seconds: "{count} segundos"
|
|
903
755
|
}
|
|
904
756
|
},
|
|
905
757
|
fr: {
|
|
@@ -1000,6 +852,16 @@ var VidPly = (() => {
|
|
|
1000
852
|
},
|
|
1001
853
|
speeds: {
|
|
1002
854
|
normal: "Normal"
|
|
855
|
+
},
|
|
856
|
+
time: {
|
|
857
|
+
display: "Affichage du temps",
|
|
858
|
+
durationPrefix: "Dur\xE9e : ",
|
|
859
|
+
hour: "{count} heure",
|
|
860
|
+
hours: "{count} heures",
|
|
861
|
+
minute: "{count} minute",
|
|
862
|
+
minutes: "{count} minutes",
|
|
863
|
+
second: "{count} seconde",
|
|
864
|
+
seconds: "{count} secondes"
|
|
1003
865
|
}
|
|
1004
866
|
},
|
|
1005
867
|
ja: {
|
|
@@ -1100,6 +962,16 @@ var VidPly = (() => {
|
|
|
1100
962
|
},
|
|
1101
963
|
speeds: {
|
|
1102
964
|
normal: "\u901A\u5E38"
|
|
965
|
+
},
|
|
966
|
+
time: {
|
|
967
|
+
display: "\u6642\u9593\u8868\u793A",
|
|
968
|
+
durationPrefix: "\u518D\u751F\u6642\u9593: ",
|
|
969
|
+
hour: "{count}\u6642\u9593",
|
|
970
|
+
hours: "{count}\u6642\u9593",
|
|
971
|
+
minute: "{count}\u5206",
|
|
972
|
+
minutes: "{count}\u5206",
|
|
973
|
+
second: "{count}\u79D2",
|
|
974
|
+
seconds: "{count}\u79D2"
|
|
1103
975
|
}
|
|
1104
976
|
}
|
|
1105
977
|
};
|
|
@@ -1155,6 +1027,187 @@ var VidPly = (() => {
|
|
|
1155
1027
|
};
|
|
1156
1028
|
var i18n = new I18n();
|
|
1157
1029
|
|
|
1030
|
+
// src/utils/TimeUtils.js
|
|
1031
|
+
var TimeUtils = {
|
|
1032
|
+
/**
|
|
1033
|
+
* Format seconds to time string (HH:MM:SS or MM:SS)
|
|
1034
|
+
*/
|
|
1035
|
+
formatTime(seconds, alwaysShowHours = false) {
|
|
1036
|
+
if (!isFinite(seconds) || seconds < 0) {
|
|
1037
|
+
return alwaysShowHours ? "00:00:00" : "00:00";
|
|
1038
|
+
}
|
|
1039
|
+
const hours = Math.floor(seconds / 3600);
|
|
1040
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
1041
|
+
const secs = Math.floor(seconds % 60);
|
|
1042
|
+
const pad = (num) => String(num).padStart(2, "0");
|
|
1043
|
+
if (hours > 0 || alwaysShowHours) {
|
|
1044
|
+
return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
|
|
1045
|
+
}
|
|
1046
|
+
return `${pad(minutes)}:${pad(secs)}`;
|
|
1047
|
+
},
|
|
1048
|
+
/**
|
|
1049
|
+
* Parse time string to seconds
|
|
1050
|
+
*/
|
|
1051
|
+
parseTime(timeString) {
|
|
1052
|
+
const parts = timeString.split(":").map((p) => parseInt(p, 10));
|
|
1053
|
+
if (parts.length === 3) {
|
|
1054
|
+
return parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
1055
|
+
} else if (parts.length === 2) {
|
|
1056
|
+
return parts[0] * 60 + parts[1];
|
|
1057
|
+
} else if (parts.length === 1) {
|
|
1058
|
+
return parts[0];
|
|
1059
|
+
}
|
|
1060
|
+
return 0;
|
|
1061
|
+
},
|
|
1062
|
+
/**
|
|
1063
|
+
* Format seconds to readable duration
|
|
1064
|
+
*/
|
|
1065
|
+
formatDuration(seconds) {
|
|
1066
|
+
if (!isFinite(seconds) || seconds < 0) {
|
|
1067
|
+
return i18n.t("time.seconds", { count: 0 });
|
|
1068
|
+
}
|
|
1069
|
+
const hours = Math.floor(seconds / 3600);
|
|
1070
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
1071
|
+
const secs = Math.floor(seconds % 60);
|
|
1072
|
+
const parts = [];
|
|
1073
|
+
if (hours > 0) {
|
|
1074
|
+
const key = hours === 1 ? "time.hour" : "time.hours";
|
|
1075
|
+
parts.push(i18n.t(key, { count: hours }));
|
|
1076
|
+
}
|
|
1077
|
+
if (minutes > 0) {
|
|
1078
|
+
const key = minutes === 1 ? "time.minute" : "time.minutes";
|
|
1079
|
+
parts.push(i18n.t(key, { count: minutes }));
|
|
1080
|
+
}
|
|
1081
|
+
if (secs > 0 || parts.length === 0) {
|
|
1082
|
+
const key = secs === 1 ? "time.second" : "time.seconds";
|
|
1083
|
+
parts.push(i18n.t(key, { count: secs }));
|
|
1084
|
+
}
|
|
1085
|
+
return parts.join(", ");
|
|
1086
|
+
},
|
|
1087
|
+
/**
|
|
1088
|
+
* Format percentage
|
|
1089
|
+
*/
|
|
1090
|
+
formatPercentage(value, total) {
|
|
1091
|
+
if (total === 0) return 0;
|
|
1092
|
+
return Math.round(value / total * 100);
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
// src/icons/Icons.js
|
|
1097
|
+
var iconPaths = {
|
|
1098
|
+
play: `<path d="M8 5v14l11-7z"/>`,
|
|
1099
|
+
pause: `<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>`,
|
|
1100
|
+
stop: `<rect x="6" y="6" width="12" height="12"/>`,
|
|
1101
|
+
rewind: `<path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/>`,
|
|
1102
|
+
forward: `<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>`,
|
|
1103
|
+
skipPrevious: `<path d="M6 6h2v12H6V6zm3 6l8.5 6V6L9 12z"/>`,
|
|
1104
|
+
skipNext: `<path d="M16 6h2v12h-2V6zM6 6l8.5 6L6 18V6z"/>`,
|
|
1105
|
+
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"/>`,
|
|
1106
|
+
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"/>`,
|
|
1107
|
+
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"/>`,
|
|
1108
|
+
volumeLow: `<path d="M7 9v6h4l5 5V4l-5 5H7z"/>`,
|
|
1109
|
+
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"/>`,
|
|
1110
|
+
fullscreen: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
|
|
1111
|
+
fullscreenExit: `<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>`,
|
|
1112
|
+
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"/>`,
|
|
1113
|
+
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"/>`,
|
|
1114
|
+
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"/>`,
|
|
1115
|
+
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"/>`,
|
|
1116
|
+
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"/>`,
|
|
1117
|
+
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"/>`,
|
|
1118
|
+
check: `<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>`,
|
|
1119
|
+
arrowUp: `<path d="M7 14l5-5 5 5z"/>`,
|
|
1120
|
+
arrowDown: `<path d="M7 10l5 5 5-5z"/>`,
|
|
1121
|
+
arrowLeft: `<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>`,
|
|
1122
|
+
arrowRight: `<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>`,
|
|
1123
|
+
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"/>`,
|
|
1124
|
+
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"/>`,
|
|
1125
|
+
download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
|
|
1126
|
+
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"/>`,
|
|
1127
|
+
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"/>`,
|
|
1128
|
+
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"/>`,
|
|
1129
|
+
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"/>`,
|
|
1130
|
+
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"/>`,
|
|
1131
|
+
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"/>`,
|
|
1132
|
+
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"/>`,
|
|
1133
|
+
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>`,
|
|
1134
|
+
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>`,
|
|
1135
|
+
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"/>`,
|
|
1136
|
+
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"/>`,
|
|
1137
|
+
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"/>`,
|
|
1138
|
+
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"/>`
|
|
1139
|
+
};
|
|
1140
|
+
var svgWrapper = (paths) => `<svg viewBox="0 0 24 24" fill="currentColor">${paths}</svg>`;
|
|
1141
|
+
var Icons = Object.fromEntries(
|
|
1142
|
+
Object.entries(iconPaths).map(([key, value]) => [key, svgWrapper(value)])
|
|
1143
|
+
);
|
|
1144
|
+
function getIcon(name) {
|
|
1145
|
+
return Icons[name] || Icons.play;
|
|
1146
|
+
}
|
|
1147
|
+
function createIconElement(name, className = "") {
|
|
1148
|
+
const wrapper = document.createElement("span");
|
|
1149
|
+
wrapper.className = `vidply-icon ${className}`.trim();
|
|
1150
|
+
wrapper.innerHTML = getIcon(name);
|
|
1151
|
+
wrapper.setAttribute("aria-hidden", "true");
|
|
1152
|
+
return wrapper;
|
|
1153
|
+
}
|
|
1154
|
+
function createPlayOverlay() {
|
|
1155
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1156
|
+
svg.setAttribute("class", "vidply-play-overlay");
|
|
1157
|
+
svg.setAttribute("viewBox", "0 0 80 80");
|
|
1158
|
+
svg.setAttribute("width", "80");
|
|
1159
|
+
svg.setAttribute("height", "80");
|
|
1160
|
+
svg.setAttribute("aria-hidden", "true");
|
|
1161
|
+
svg.setAttribute("role", "presentation");
|
|
1162
|
+
svg.style.cursor = "pointer";
|
|
1163
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
1164
|
+
const filterId = `vidply-play-shadow-${Math.random().toString(36).substr(2, 9)}`;
|
|
1165
|
+
const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
|
1166
|
+
filter.setAttribute("id", filterId);
|
|
1167
|
+
filter.setAttribute("x", "-50%");
|
|
1168
|
+
filter.setAttribute("y", "-50%");
|
|
1169
|
+
filter.setAttribute("width", "200%");
|
|
1170
|
+
filter.setAttribute("height", "200%");
|
|
1171
|
+
const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
|
1172
|
+
feGaussianBlur.setAttribute("in", "SourceAlpha");
|
|
1173
|
+
feGaussianBlur.setAttribute("stdDeviation", "3");
|
|
1174
|
+
const feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
|
|
1175
|
+
feOffset.setAttribute("dx", "0");
|
|
1176
|
+
feOffset.setAttribute("dy", "2");
|
|
1177
|
+
feOffset.setAttribute("result", "offsetblur");
|
|
1178
|
+
const feComponentTransfer = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
|
|
1179
|
+
const feFuncA = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
|
|
1180
|
+
feFuncA.setAttribute("type", "linear");
|
|
1181
|
+
feFuncA.setAttribute("slope", "0.3");
|
|
1182
|
+
feComponentTransfer.appendChild(feFuncA);
|
|
1183
|
+
const feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
|
1184
|
+
const feMergeNode1 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1185
|
+
const feMergeNode2 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
|
1186
|
+
feMergeNode2.setAttribute("in", "SourceGraphic");
|
|
1187
|
+
feMerge.appendChild(feMergeNode1);
|
|
1188
|
+
feMerge.appendChild(feMergeNode2);
|
|
1189
|
+
filter.appendChild(feGaussianBlur);
|
|
1190
|
+
filter.appendChild(feOffset);
|
|
1191
|
+
filter.appendChild(feComponentTransfer);
|
|
1192
|
+
filter.appendChild(feMerge);
|
|
1193
|
+
defs.appendChild(filter);
|
|
1194
|
+
svg.appendChild(defs);
|
|
1195
|
+
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1196
|
+
circle.setAttribute("cx", "40");
|
|
1197
|
+
circle.setAttribute("cy", "40");
|
|
1198
|
+
circle.setAttribute("r", "40");
|
|
1199
|
+
circle.setAttribute("fill", "rgba(255, 255, 255, 0.95)");
|
|
1200
|
+
circle.setAttribute("filter", `url(#${filterId})`);
|
|
1201
|
+
circle.setAttribute("class", "vidply-play-overlay-bg");
|
|
1202
|
+
svg.appendChild(circle);
|
|
1203
|
+
const playTriangle = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
1204
|
+
playTriangle.setAttribute("points", "32,28 32,52 54,40");
|
|
1205
|
+
playTriangle.setAttribute("fill", "#0a406e");
|
|
1206
|
+
playTriangle.setAttribute("class", "vidply-play-overlay-icon");
|
|
1207
|
+
svg.appendChild(playTriangle);
|
|
1208
|
+
return svg;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1158
1211
|
// src/controls/ControlBar.js
|
|
1159
1212
|
var ControlBar = class {
|
|
1160
1213
|
constructor(player) {
|
|
@@ -1671,12 +1724,26 @@ var VidPly = (() => {
|
|
|
1671
1724
|
}
|
|
1672
1725
|
createTimeDisplay() {
|
|
1673
1726
|
const container = DOMUtils.createElement("div", {
|
|
1674
|
-
className: `${this.player.options.classPrefix}-time
|
|
1727
|
+
className: `${this.player.options.classPrefix}-time`,
|
|
1728
|
+
attributes: {
|
|
1729
|
+
"role": "group",
|
|
1730
|
+
"aria-label": i18n.t("time.display")
|
|
1731
|
+
}
|
|
1675
1732
|
});
|
|
1676
1733
|
this.controls.currentTimeDisplay = DOMUtils.createElement("span", {
|
|
1677
1734
|
className: `${this.player.options.classPrefix}-current-time`,
|
|
1678
|
-
|
|
1735
|
+
attributes: {
|
|
1736
|
+
"aria-label": i18n.t("time.seconds", { count: 0 })
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
const currentTimeVisual = DOMUtils.createElement("span", {
|
|
1740
|
+
textContent: "00:00",
|
|
1741
|
+
attributes: {
|
|
1742
|
+
"aria-hidden": "true"
|
|
1743
|
+
}
|
|
1679
1744
|
});
|
|
1745
|
+
this.controls.currentTimeDisplay.appendChild(currentTimeVisual);
|
|
1746
|
+
this.controls.currentTimeVisual = currentTimeVisual;
|
|
1680
1747
|
const separator = DOMUtils.createElement("span", {
|
|
1681
1748
|
textContent: " / ",
|
|
1682
1749
|
attributes: {
|
|
@@ -1685,8 +1752,18 @@ var VidPly = (() => {
|
|
|
1685
1752
|
});
|
|
1686
1753
|
this.controls.durationDisplay = DOMUtils.createElement("span", {
|
|
1687
1754
|
className: `${this.player.options.classPrefix}-duration`,
|
|
1688
|
-
|
|
1755
|
+
attributes: {
|
|
1756
|
+
"aria-label": i18n.t("time.durationPrefix") + i18n.t("time.seconds", { count: 0 })
|
|
1757
|
+
}
|
|
1689
1758
|
});
|
|
1759
|
+
const durationVisual = DOMUtils.createElement("span", {
|
|
1760
|
+
textContent: "00:00",
|
|
1761
|
+
attributes: {
|
|
1762
|
+
"aria-hidden": "true"
|
|
1763
|
+
}
|
|
1764
|
+
});
|
|
1765
|
+
this.controls.durationDisplay.appendChild(durationVisual);
|
|
1766
|
+
this.controls.durationVisual = durationVisual;
|
|
1690
1767
|
container.appendChild(this.controls.currentTimeDisplay);
|
|
1691
1768
|
container.appendChild(separator);
|
|
1692
1769
|
container.appendChild(this.controls.durationDisplay);
|
|
@@ -2510,13 +2587,17 @@ var VidPly = (() => {
|
|
|
2510
2587
|
const percent = this.player.state.currentTime / this.player.state.duration * 100;
|
|
2511
2588
|
this.controls.played.style.width = `${percent}%`;
|
|
2512
2589
|
this.controls.progress.setAttribute("aria-valuenow", String(Math.round(percent)));
|
|
2513
|
-
if (this.controls.
|
|
2514
|
-
|
|
2590
|
+
if (this.controls.currentTimeVisual) {
|
|
2591
|
+
const currentTime = this.player.state.currentTime;
|
|
2592
|
+
this.controls.currentTimeVisual.textContent = TimeUtils.formatTime(currentTime);
|
|
2593
|
+
this.controls.currentTimeDisplay.setAttribute("aria-label", TimeUtils.formatDuration(currentTime));
|
|
2515
2594
|
}
|
|
2516
2595
|
}
|
|
2517
2596
|
updateDuration() {
|
|
2518
|
-
if (this.controls.
|
|
2519
|
-
|
|
2597
|
+
if (this.controls.durationVisual) {
|
|
2598
|
+
const duration = this.player.state.duration;
|
|
2599
|
+
this.controls.durationVisual.textContent = TimeUtils.formatTime(duration);
|
|
2600
|
+
this.controls.durationDisplay.setAttribute("aria-label", i18n.t("time.durationPrefix") + TimeUtils.formatDuration(duration));
|
|
2520
2601
|
}
|
|
2521
2602
|
}
|
|
2522
2603
|
updateVolumeDisplay() {
|
|
@@ -4578,6 +4659,17 @@ var VidPly = (() => {
|
|
|
4578
4659
|
this.captionManager.destroy();
|
|
4579
4660
|
this.captionManager = new CaptionManager(this);
|
|
4580
4661
|
}
|
|
4662
|
+
if (this.transcriptManager) {
|
|
4663
|
+
const wasVisible = this.transcriptManager.isVisible;
|
|
4664
|
+
this.transcriptManager.destroy();
|
|
4665
|
+
this.transcriptManager = new TranscriptManager(this);
|
|
4666
|
+
if (wasVisible) {
|
|
4667
|
+
this.transcriptManager.showTranscript();
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
if (this.controlBar) {
|
|
4671
|
+
this.updateControlBar();
|
|
4672
|
+
}
|
|
4581
4673
|
this.emit("sourcechange", config);
|
|
4582
4674
|
this.log("Media loaded successfully");
|
|
4583
4675
|
} catch (error) {
|
|
@@ -4589,6 +4681,17 @@ var VidPly = (() => {
|
|
|
4589
4681
|
* @param {string} src - New source URL
|
|
4590
4682
|
* @returns {boolean}
|
|
4591
4683
|
*/
|
|
4684
|
+
/**
|
|
4685
|
+
* Update control bar to refresh button visibility based on available features
|
|
4686
|
+
*/
|
|
4687
|
+
updateControlBar() {
|
|
4688
|
+
if (!this.controlBar) return;
|
|
4689
|
+
const controlBar = this.controlBar;
|
|
4690
|
+
controlBar.element.innerHTML = "";
|
|
4691
|
+
controlBar.createControls();
|
|
4692
|
+
controlBar.attachEvents();
|
|
4693
|
+
controlBar.setupAutoHide();
|
|
4694
|
+
}
|
|
4592
4695
|
shouldChangeRenderer(src) {
|
|
4593
4696
|
if (!this.renderer) return true;
|
|
4594
4697
|
const isYouTube = src.includes("youtube.com") || src.includes("youtu.be");
|
|
@@ -5048,7 +5151,9 @@ var VidPly = (() => {
|
|
|
5048
5151
|
this.trackInfoElement = null;
|
|
5049
5152
|
this.handleTrackEnd = this.handleTrackEnd.bind(this);
|
|
5050
5153
|
this.handleTrackError = this.handleTrackError.bind(this);
|
|
5154
|
+
this.player.playlistManager = this;
|
|
5051
5155
|
this.init();
|
|
5156
|
+
this.updatePlayerControls();
|
|
5052
5157
|
}
|
|
5053
5158
|
init() {
|
|
5054
5159
|
this.player.on("ended", this.handleTrackEnd);
|
|
@@ -5057,6 +5162,17 @@ var VidPly = (() => {
|
|
|
5057
5162
|
this.createUI();
|
|
5058
5163
|
}
|
|
5059
5164
|
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Update player controls to add playlist navigation buttons
|
|
5167
|
+
*/
|
|
5168
|
+
updatePlayerControls() {
|
|
5169
|
+
if (!this.player.controlBar) return;
|
|
5170
|
+
const controlBar = this.player.controlBar;
|
|
5171
|
+
controlBar.element.innerHTML = "";
|
|
5172
|
+
controlBar.createControls();
|
|
5173
|
+
controlBar.attachEvents();
|
|
5174
|
+
controlBar.setupAutoHide();
|
|
5175
|
+
}
|
|
5060
5176
|
/**
|
|
5061
5177
|
* Load a playlist
|
|
5062
5178
|
* @param {Array} tracks - Array of track objects
|
|
@@ -5077,8 +5193,9 @@ var VidPly = (() => {
|
|
|
5077
5193
|
/**
|
|
5078
5194
|
* Play a specific track
|
|
5079
5195
|
* @param {number} index - Track index
|
|
5196
|
+
* @param {boolean} userInitiated - Whether this was triggered by user action (default: false)
|
|
5080
5197
|
*/
|
|
5081
|
-
play(index) {
|
|
5198
|
+
play(index, userInitiated = false) {
|
|
5082
5199
|
if (index < 0 || index >= this.tracks.length) {
|
|
5083
5200
|
console.warn("VidPly Playlist: Invalid track index", index);
|
|
5084
5201
|
return;
|
|
@@ -5098,6 +5215,9 @@ var VidPly = (() => {
|
|
|
5098
5215
|
item: track,
|
|
5099
5216
|
total: this.tracks.length
|
|
5100
5217
|
});
|
|
5218
|
+
if (userInitiated && this.player.container) {
|
|
5219
|
+
this.player.container.focus();
|
|
5220
|
+
}
|
|
5101
5221
|
setTimeout(() => {
|
|
5102
5222
|
this.player.play();
|
|
5103
5223
|
}, 100);
|
|
@@ -5159,12 +5279,17 @@ var VidPly = (() => {
|
|
|
5159
5279
|
return;
|
|
5160
5280
|
}
|
|
5161
5281
|
this.trackInfoElement = DOMUtils.createElement("div", {
|
|
5162
|
-
className: "vidply-track-info"
|
|
5282
|
+
className: "vidply-track-info",
|
|
5283
|
+
role: "status",
|
|
5284
|
+
"aria-live": "polite",
|
|
5285
|
+
"aria-atomic": "true"
|
|
5163
5286
|
});
|
|
5164
5287
|
this.trackInfoElement.style.display = "none";
|
|
5165
5288
|
this.container.appendChild(this.trackInfoElement);
|
|
5166
5289
|
this.playlistPanel = DOMUtils.createElement("div", {
|
|
5167
|
-
className: "vidply-playlist-panel"
|
|
5290
|
+
className: "vidply-playlist-panel",
|
|
5291
|
+
role: "region",
|
|
5292
|
+
"aria-label": "Media playlist"
|
|
5168
5293
|
});
|
|
5169
5294
|
this.playlistPanel.style.display = "none";
|
|
5170
5295
|
this.container.appendChild(this.playlistPanel);
|
|
@@ -5176,10 +5301,14 @@ var VidPly = (() => {
|
|
|
5176
5301
|
if (!this.trackInfoElement) return;
|
|
5177
5302
|
const trackNumber = this.currentIndex + 1;
|
|
5178
5303
|
const totalTracks = this.tracks.length;
|
|
5304
|
+
const trackTitle = track.title || "Untitled";
|
|
5305
|
+
const trackArtist = track.artist || "";
|
|
5306
|
+
const announcement = `Now playing: Track ${trackNumber} of ${totalTracks}. ${trackTitle}${trackArtist ? " by " + trackArtist : ""}`;
|
|
5179
5307
|
this.trackInfoElement.innerHTML = `
|
|
5180
|
-
<
|
|
5181
|
-
<div class="vidply-track-
|
|
5182
|
-
|
|
5308
|
+
<span class="vidply-sr-only">${DOMUtils.escapeHTML(announcement)}</span>
|
|
5309
|
+
<div class="vidply-track-number" aria-hidden="true">Track ${trackNumber} of ${totalTracks}</div>
|
|
5310
|
+
<div class="vidply-track-title" aria-hidden="true">${DOMUtils.escapeHTML(trackTitle)}</div>
|
|
5311
|
+
${trackArtist ? `<div class="vidply-track-artist" aria-hidden="true">${DOMUtils.escapeHTML(trackArtist)}</div>` : ""}
|
|
5183
5312
|
`;
|
|
5184
5313
|
this.trackInfoElement.style.display = "block";
|
|
5185
5314
|
}
|
|
@@ -5189,14 +5318,29 @@ var VidPly = (() => {
|
|
|
5189
5318
|
renderPlaylist() {
|
|
5190
5319
|
if (!this.playlistPanel) return;
|
|
5191
5320
|
this.playlistPanel.innerHTML = "";
|
|
5192
|
-
const header = DOMUtils.createElement("
|
|
5193
|
-
className: "vidply-playlist-header"
|
|
5321
|
+
const header = DOMUtils.createElement("h2", {
|
|
5322
|
+
className: "vidply-playlist-header",
|
|
5323
|
+
id: "vidply-playlist-heading"
|
|
5194
5324
|
});
|
|
5195
5325
|
header.textContent = `Playlist (${this.tracks.length})`;
|
|
5196
5326
|
this.playlistPanel.appendChild(header);
|
|
5197
|
-
const
|
|
5198
|
-
className: "vidply-
|
|
5199
|
-
|
|
5327
|
+
const instructions = DOMUtils.createElement("div", {
|
|
5328
|
+
className: "vidply-sr-only",
|
|
5329
|
+
"aria-hidden": "false"
|
|
5330
|
+
});
|
|
5331
|
+
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.";
|
|
5332
|
+
this.playlistPanel.appendChild(instructions);
|
|
5333
|
+
const list = DOMUtils.createElement("ul", {
|
|
5334
|
+
className: "vidply-playlist-list",
|
|
5335
|
+
"aria-labelledby": "vidply-playlist-heading",
|
|
5336
|
+
"aria-describedby": "vidply-playlist-instructions"
|
|
5337
|
+
});
|
|
5338
|
+
const listDescription = DOMUtils.createElement("div", {
|
|
5339
|
+
className: "vidply-sr-only",
|
|
5340
|
+
id: "vidply-playlist-instructions"
|
|
5341
|
+
});
|
|
5342
|
+
listDescription.textContent = `Playlist with ${this.tracks.length} ${this.tracks.length === 1 ? "track" : "tracks"}`;
|
|
5343
|
+
this.playlistPanel.appendChild(listDescription);
|
|
5200
5344
|
this.tracks.forEach((track, index) => {
|
|
5201
5345
|
const item = this.createPlaylistItem(track, index);
|
|
5202
5346
|
list.appendChild(item);
|
|
@@ -5208,20 +5352,39 @@ var VidPly = (() => {
|
|
|
5208
5352
|
* Create playlist item element
|
|
5209
5353
|
*/
|
|
5210
5354
|
createPlaylistItem(track, index) {
|
|
5211
|
-
const
|
|
5355
|
+
const trackPosition = `Track ${index + 1} of ${this.tracks.length}`;
|
|
5356
|
+
const trackTitle = track.title || `Track ${index + 1}`;
|
|
5357
|
+
const trackArtist = track.artist ? ` by ${track.artist}` : "";
|
|
5358
|
+
const isActive = index === this.currentIndex;
|
|
5359
|
+
const statusText = isActive ? "Currently playing" : "Not playing";
|
|
5360
|
+
const actionText = isActive ? "Press Enter to restart" : "Press Enter to play";
|
|
5361
|
+
const item = DOMUtils.createElement("li", {
|
|
5212
5362
|
className: "vidply-playlist-item",
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
"aria-label":
|
|
5216
|
-
|
|
5217
|
-
|
|
5363
|
+
tabIndex: index === 0 ? 0 : -1,
|
|
5364
|
+
// Only first item is in tab order initially
|
|
5365
|
+
"aria-label": `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`,
|
|
5366
|
+
"aria-posinset": index + 1,
|
|
5367
|
+
"aria-setsize": this.tracks.length,
|
|
5368
|
+
"data-playlist-index": index
|
|
5369
|
+
});
|
|
5370
|
+
if (isActive) {
|
|
5218
5371
|
item.classList.add("vidply-playlist-item-active");
|
|
5372
|
+
item.setAttribute("aria-current", "true");
|
|
5373
|
+
item.setAttribute("tabIndex", "0");
|
|
5219
5374
|
}
|
|
5375
|
+
const positionInfo = DOMUtils.createElement("span", {
|
|
5376
|
+
className: "vidply-sr-only"
|
|
5377
|
+
});
|
|
5378
|
+
positionInfo.textContent = `${trackPosition}: `;
|
|
5379
|
+
item.appendChild(positionInfo);
|
|
5220
5380
|
const thumbnail = DOMUtils.createElement("div", {
|
|
5221
|
-
className: "vidply-playlist-thumbnail"
|
|
5381
|
+
className: "vidply-playlist-thumbnail",
|
|
5382
|
+
"aria-hidden": "true"
|
|
5222
5383
|
});
|
|
5223
5384
|
if (track.poster) {
|
|
5224
5385
|
thumbnail.style.backgroundImage = `url(${track.poster})`;
|
|
5386
|
+
thumbnail.setAttribute("role", "img");
|
|
5387
|
+
thumbnail.setAttribute("aria-label", `${trackTitle} thumbnail`);
|
|
5225
5388
|
} else {
|
|
5226
5389
|
const icon = createIconElement("music");
|
|
5227
5390
|
icon.classList.add("vidply-playlist-thumbnail-icon");
|
|
@@ -5229,12 +5392,13 @@ var VidPly = (() => {
|
|
|
5229
5392
|
}
|
|
5230
5393
|
item.appendChild(thumbnail);
|
|
5231
5394
|
const info = DOMUtils.createElement("div", {
|
|
5232
|
-
className: "vidply-playlist-item-info"
|
|
5395
|
+
className: "vidply-playlist-item-info",
|
|
5396
|
+
"aria-hidden": "true"
|
|
5233
5397
|
});
|
|
5234
5398
|
const title = DOMUtils.createElement("div", {
|
|
5235
5399
|
className: "vidply-playlist-item-title"
|
|
5236
5400
|
});
|
|
5237
|
-
title.textContent =
|
|
5401
|
+
title.textContent = trackTitle;
|
|
5238
5402
|
info.appendChild(title);
|
|
5239
5403
|
if (track.artist) {
|
|
5240
5404
|
const artist = DOMUtils.createElement("div", {
|
|
@@ -5244,20 +5408,64 @@ var VidPly = (() => {
|
|
|
5244
5408
|
info.appendChild(artist);
|
|
5245
5409
|
}
|
|
5246
5410
|
item.appendChild(info);
|
|
5411
|
+
if (isActive) {
|
|
5412
|
+
const statusIndicator = DOMUtils.createElement("span", {
|
|
5413
|
+
className: "vidply-sr-only"
|
|
5414
|
+
});
|
|
5415
|
+
statusIndicator.textContent = " (Currently playing)";
|
|
5416
|
+
item.appendChild(statusIndicator);
|
|
5417
|
+
}
|
|
5247
5418
|
const playIcon = createIconElement("play");
|
|
5248
5419
|
playIcon.classList.add("vidply-playlist-item-icon");
|
|
5420
|
+
playIcon.setAttribute("aria-hidden", "true");
|
|
5249
5421
|
item.appendChild(playIcon);
|
|
5250
5422
|
item.addEventListener("click", () => {
|
|
5251
|
-
this.play(index);
|
|
5423
|
+
this.play(index, true);
|
|
5252
5424
|
});
|
|
5253
5425
|
item.addEventListener("keydown", (e) => {
|
|
5254
|
-
|
|
5255
|
-
e.preventDefault();
|
|
5256
|
-
this.play(index);
|
|
5257
|
-
}
|
|
5426
|
+
this.handlePlaylistItemKeydown(e, index);
|
|
5258
5427
|
});
|
|
5259
5428
|
return item;
|
|
5260
5429
|
}
|
|
5430
|
+
/**
|
|
5431
|
+
* Handle keyboard navigation in playlist items
|
|
5432
|
+
*/
|
|
5433
|
+
handlePlaylistItemKeydown(e, index) {
|
|
5434
|
+
const items = Array.from(this.playlistPanel.querySelectorAll(".vidply-playlist-item"));
|
|
5435
|
+
let newIndex = -1;
|
|
5436
|
+
switch (e.key) {
|
|
5437
|
+
case "Enter":
|
|
5438
|
+
case " ":
|
|
5439
|
+
e.preventDefault();
|
|
5440
|
+
this.play(index, true);
|
|
5441
|
+
break;
|
|
5442
|
+
case "ArrowDown":
|
|
5443
|
+
e.preventDefault();
|
|
5444
|
+
if (index < items.length - 1) {
|
|
5445
|
+
newIndex = index + 1;
|
|
5446
|
+
}
|
|
5447
|
+
break;
|
|
5448
|
+
case "ArrowUp":
|
|
5449
|
+
e.preventDefault();
|
|
5450
|
+
if (index > 0) {
|
|
5451
|
+
newIndex = index - 1;
|
|
5452
|
+
}
|
|
5453
|
+
break;
|
|
5454
|
+
case "Home":
|
|
5455
|
+
e.preventDefault();
|
|
5456
|
+
newIndex = 0;
|
|
5457
|
+
break;
|
|
5458
|
+
case "End":
|
|
5459
|
+
e.preventDefault();
|
|
5460
|
+
newIndex = items.length - 1;
|
|
5461
|
+
break;
|
|
5462
|
+
}
|
|
5463
|
+
if (newIndex !== -1 && newIndex !== index) {
|
|
5464
|
+
items[index].setAttribute("tabIndex", "-1");
|
|
5465
|
+
items[newIndex].setAttribute("tabIndex", "0");
|
|
5466
|
+
items[newIndex].focus();
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5261
5469
|
/**
|
|
5262
5470
|
* Update playlist UI (highlight current track)
|
|
5263
5471
|
*/
|
|
@@ -5265,11 +5473,25 @@ var VidPly = (() => {
|
|
|
5265
5473
|
if (!this.playlistPanel) return;
|
|
5266
5474
|
const items = this.playlistPanel.querySelectorAll(".vidply-playlist-item");
|
|
5267
5475
|
items.forEach((item, index) => {
|
|
5476
|
+
const track = this.tracks[index];
|
|
5477
|
+
const trackPosition = `Track ${index + 1} of ${this.tracks.length}`;
|
|
5478
|
+
const trackTitle = track.title || `Track ${index + 1}`;
|
|
5479
|
+
const trackArtist = track.artist ? ` by ${track.artist}` : "";
|
|
5268
5480
|
if (index === this.currentIndex) {
|
|
5269
5481
|
item.classList.add("vidply-playlist-item-active");
|
|
5482
|
+
item.setAttribute("aria-current", "true");
|
|
5483
|
+
item.setAttribute("tabIndex", "0");
|
|
5484
|
+
const statusText = "Currently playing";
|
|
5485
|
+
const actionText = "Press Enter to restart";
|
|
5486
|
+
item.setAttribute("aria-label", `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`);
|
|
5270
5487
|
item.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
5271
5488
|
} else {
|
|
5272
5489
|
item.classList.remove("vidply-playlist-item-active");
|
|
5490
|
+
item.removeAttribute("aria-current");
|
|
5491
|
+
item.setAttribute("tabIndex", "-1");
|
|
5492
|
+
const statusText = "Not playing";
|
|
5493
|
+
const actionText = "Press Enter to play";
|
|
5494
|
+
item.setAttribute("aria-label", `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`);
|
|
5273
5495
|
}
|
|
5274
5496
|
});
|
|
5275
5497
|
}
|
|
@@ -5287,10 +5509,24 @@ var VidPly = (() => {
|
|
|
5287
5509
|
currentIndex: this.currentIndex,
|
|
5288
5510
|
totalTracks: this.tracks.length,
|
|
5289
5511
|
currentTrack: this.getCurrentTrack(),
|
|
5290
|
-
hasNext: this.
|
|
5291
|
-
hasPrevious: this.
|
|
5512
|
+
hasNext: this.hasNext(),
|
|
5513
|
+
hasPrevious: this.hasPrevious()
|
|
5292
5514
|
};
|
|
5293
5515
|
}
|
|
5516
|
+
/**
|
|
5517
|
+
* Check if there is a next track
|
|
5518
|
+
*/
|
|
5519
|
+
hasNext() {
|
|
5520
|
+
if (this.options.loop) return true;
|
|
5521
|
+
return this.currentIndex < this.tracks.length - 1;
|
|
5522
|
+
}
|
|
5523
|
+
/**
|
|
5524
|
+
* Check if there is a previous track
|
|
5525
|
+
*/
|
|
5526
|
+
hasPrevious() {
|
|
5527
|
+
if (this.options.loop) return true;
|
|
5528
|
+
return this.currentIndex > 0;
|
|
5529
|
+
}
|
|
5294
5530
|
/**
|
|
5295
5531
|
* Add track to playlist
|
|
5296
5532
|
*/
|