diff --git a/client.js b/client.js
index 4556fe6..33135b2 100644
--- a/client.js
+++ b/client.js
@@ -1849,15 +1849,102 @@ try {
}
}
+ function isWatchUrl() {
+ if (typeof window === 'undefined' || !window.location) return false;
+ var p = window.location.pathname;
+ return p.includes('/videos/watch/') || p.startsWith('/w/') || p.includes('/w/');
+ }
+
+ function injectControlBarSeekButtons(playerWrapper) {
+ var controlBar = (playerWrapper || document).querySelector('.vjs-control-bar');
+ if (!controlBar) return;
+
+ var rewindBtn = controlBar.querySelector('.yaktube-vjs-rewind-btn');
+ var forwardBtn = controlBar.querySelector('.yaktube-vjs-forward-btn');
+ var playControl = controlBar.querySelector('.vjs-play-control');
+
+ if (!rewindBtn) {
+ rewindBtn = document.createElement('button');
+ rewindBtn.className = 'vjs-control vjs-button yaktube-vjs-seek-btn yaktube-vjs-rewind-btn';
+ rewindBtn.type = 'button';
+ rewindBtn.setAttribute('tabindex', '0');
+ rewindBtn.setAttribute('aria-label', '10 saniye geri sar (Sol Ok veya J)');
+ rewindBtn.setAttribute('title', '10 saniye geri sar (Alt+J)');
+ rewindBtn.innerHTML =
+ '⏪10' +
+ '10 saniye geri sar';
+
+ rewindBtn.addEventListener('click', function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ var video = getVideoElement();
+ if (video) {
+ video.currentTime = Math.max(0, video.currentTime - 10);
+ announce('10 saniye geri sarıldı. Konum: ' + formatTime(video.currentTime));
+ showQuickToast('⏪ -10sn');
+ }
+ });
+
+ if (playControl && playControl.nextSibling) {
+ controlBar.insertBefore(rewindBtn, playControl.nextSibling);
+ } else {
+ controlBar.appendChild(rewindBtn);
+ }
+ }
+
+ if (!forwardBtn) {
+ forwardBtn = document.createElement('button');
+ forwardBtn.className = 'vjs-control vjs-button yaktube-vjs-seek-btn yaktube-vjs-forward-btn';
+ forwardBtn.type = 'button';
+ forwardBtn.setAttribute('tabindex', '0');
+ forwardBtn.setAttribute('aria-label', '10 saniye ileri sar (Sağ Ok veya L)');
+ forwardBtn.setAttribute('title', '10 saniye ileri sar (Alt+L)');
+ forwardBtn.innerHTML =
+ '10⏩' +
+ '10 saniye ileri sar';
+
+ forwardBtn.addEventListener('click', function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ var video = getVideoElement();
+ if (video) {
+ video.currentTime = Math.min(video.duration || 999999, video.currentTime + 10);
+ announce('10 saniye ileri sarıldı. Konum: ' + formatTime(video.currentTime));
+ showQuickToast('⏩ +10sn');
+ }
+ });
+
+ if (rewindBtn && rewindBtn.nextSibling) {
+ controlBar.insertBefore(forwardBtn, rewindBtn.nextSibling);
+ } else if (playControl && playControl.nextSibling) {
+ controlBar.insertBefore(forwardBtn, playControl.nextSibling);
+ } else {
+ controlBar.appendChild(forwardBtn);
+ }
+ }
+ }
+
function enhanceVideoPlayerAccessibility() {
- var isWatchPage = window.location.pathname.includes('/videos/watch/');
+ var isWatch = isWatchUrl();
var skipLink = document.getElementById('yaktube-skip-to-player');
- if (!isWatchPage) {
+ if (!isWatch) {
if (skipLink) skipLink.remove();
return;
}
+ // 0. Handle Video Alert accessibility & live announcement (e.g. transcoding/playback warnings)
+ var alertEl = document.querySelector('my-video-alert, .video-alert, .alert-warning, .vjs-error-display');
+ if (alertEl && !alertEl.hasAttribute('data-yaktube-alert-handled')) {
+ alertEl.setAttribute('data-yaktube-alert-handled', 'true');
+ alertEl.setAttribute('role', 'alert');
+ alertEl.setAttribute('aria-live', 'assertive');
+ var alertText = alertEl.textContent ? alertEl.textContent.trim().replace(/\s+/g, ' ') : '';
+ if (alertText) {
+ announce('Video Uyarısı: ' + alertText, true);
+ }
+ }
+
// 1. Remove harmful role="application" from video and containers
var videos = document.querySelectorAll('video, .vjs-tech');
videos.forEach(function (v) {
@@ -2053,6 +2140,9 @@ try {
});
}
+ // 4.1. Inject 10s Rewind & Forward buttons directly into Video.js control bar (Phones & Desktop)
+ injectControlBarSeekButtons(playerWrapper);
+
// Dynamic ARIA label updating with current title
var curAriaLabel =
'Video Oynatıcı: ' +
@@ -2069,12 +2159,10 @@ try {
// 3.1. Injects Accessible Player Action Bar under the Watch Video
function injectAccessiblePlayerBar() {
- if (!window.location.pathname.includes('/videos/watch/')) return;
- var video = getVideoElement();
- if (!video) return;
+ if (!isWatchUrl()) return;
var targetContainer = document.querySelector(
- 'my-video-watch, .video-watch, .watch-container, #yaktube-ondemand-container'
+ 'my-video-watch, .video-watch, .watch-container, #yaktube-ondemand-container, .player-container, #video-wrapper, #videojs-wrapper'
);
if (!targetContainer) return;
@@ -2089,9 +2177,9 @@ try {
bar.innerHTML =
'⚡ Oynatıcı Kontrolleri:' +
- '' +
- '' +
- '' +
+ '' +
+ '' +
+ '' +
'' +
'' +
'' +
@@ -2119,28 +2207,35 @@ try {
// Attach button actions
bar.querySelector('#yaktube-bar-rewind-btn').addEventListener('click', function () {
+ var video = getVideoElement();
if (video) {
video.currentTime = Math.max(0, video.currentTime - 10);
announce('10 saniye geri sarıldı. Konum: ' + formatTime(video.currentTime));
+ showQuickToast('⏪ -10sn');
}
});
bar.querySelector('#yaktube-bar-play-btn').addEventListener('click', function () {
+ var video = getVideoElement();
if (video) {
if (video.paused) {
video.play();
announce('Video oynatılıyor.');
+ showQuickToast('▶️ Oynatılıyor');
} else {
video.pause();
announce('Video duraklatıldı.');
+ showQuickToast('⏸️ Duraklatıldı');
}
}
});
bar.querySelector('#yaktube-bar-forward-btn').addEventListener('click', function () {
+ var video = getVideoElement();
if (video) {
video.currentTime = Math.min(video.duration || 99999, video.currentTime + 10);
announce('10 saniye ileri sarıldı. Konum: ' + formatTime(video.currentTime));
+ showQuickToast('⏩ +10sn');
}
});
diff --git a/package.json b/package.json
index 44be317..7c528ff 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "peertube-plugin-yaktube-ondemand",
- "version": "1.5.4",
+ "version": "1.5.5",
"description": "Accessible On-Demand YouTube Import, Live Streams, Smart Recommendations, Sleep Timer, Touch Gestures, Voice Assistant & A11y Engine for PeerTube",
"main": "main.js",
"library": "./main.js",
diff --git a/style.css b/style.css
index 0226651..b1ac4d4 100644
--- a/style.css
+++ b/style.css
@@ -1481,3 +1481,82 @@ my-date-toggle .date-toggle:focus-visible {
outline: 2px solid #ff8f37 !important;
outline-offset: 1px !important;
}
+
+/* 6. Video.js Native Control Bar 10s Seek Buttons (Mobile & Desktop) */
+.video-js .vjs-control-bar .yaktube-vjs-seek-btn {
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ width: 44px !important;
+ height: 100% !important;
+ min-width: 44px !important;
+ min-height: 44px !important;
+ cursor: pointer !important;
+ color: #f1f1f1 !important;
+ font-size: 13px !important;
+ font-weight: 700 !important;
+ position: relative !important;
+ user-select: none !important;
+ background: transparent !important;
+ border: none !important;
+ padding: 0 !important;
+ margin: 0 2px !important;
+ transition:
+ color 0.2s ease,
+ transform 0.15s ease !important;
+}
+
+.video-js .vjs-control-bar .yaktube-vjs-seek-btn:hover,
+.video-js .vjs-control-bar .yaktube-vjs-seek-btn:focus-visible {
+ color: #ff8f37 !important;
+ transform: scale(1.1) !important;
+ outline: 2px solid #ff8f37 !important;
+ outline-offset: -2px !important;
+}
+
+.video-js .vjs-control-bar .yaktube-vjs-seek-btn:active {
+ transform: scale(0.92) !important;
+}
+
+.yaktube-vjs-seek-icon {
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ gap: 1px !important;
+ font-size: 13px !important;
+ line-height: 1 !important;
+}
+
+.yaktube-vjs-seek-num {
+ font-size: 10px !important;
+ font-weight: 800 !important;
+ font-family:
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ sans-serif !important;
+ color: #ff8f37 !important;
+ margin-top: 1px !important;
+}
+
+/* 7. Mobile Watch Page & Action Bar Touch Optimizations */
+@media (max-width: 768px) {
+ .video-js .vjs-control-bar .yaktube-vjs-seek-btn {
+ width: 46px !important;
+ min-width: 46px !important;
+ font-size: 14px !important;
+ }
+ .yaktube-player-action-bar {
+ padding: 8px 10px !important;
+ gap: 6px !important;
+ }
+ .yaktube-player-action-btn {
+ padding: 9px 12px !important;
+ font-size: 12px !important;
+ flex: 1 1 calc(50% - 6px) !important;
+ justify-content: center !important;
+ min-height: 42px !important;
+ }
+}