Compare commits

...
6 Commits
4 changed files with 807 additions and 146 deletions
+607 -81
View File
@@ -55,9 +55,31 @@ try {
} catch (e) {}
console.log('[YakTube] YakNet SSO, Custom Branding, A11y & On-Demand Engine Active');
// Global Suppression of role="application" on Video Elements (W3C WAI-ARIA Screen Reader Compliance)
try {
if (
typeof HTMLVideoElement !== 'undefined' &&
HTMLVideoElement.prototype &&
!HTMLVideoElement.prototype._yaktubeA11yHooked
) {
HTMLVideoElement.prototype._yaktubeA11yHooked = true;
var origVideoSetAttribute = HTMLVideoElement.prototype.setAttribute;
HTMLVideoElement.prototype.setAttribute = function (name, val) {
if (String(name).toLowerCase() === 'role' && String(val).toLowerCase() === 'application') {
return; // 100% suppress role="application" on video tags
}
return origVideoSetAttribute.apply(this, arguments);
};
}
} catch (e) {}
// Ensure Fediverse Global Search (SepiaSearch) is default in PeertubeServerConfig
try {
if (window.PeertubeServerConfig && window.PeertubeServerConfig.search && window.PeertubeServerConfig.search.searchIndex) {
if (
window.PeertubeServerConfig &&
window.PeertubeServerConfig.search &&
window.PeertubeServerConfig.search.searchIndex
) {
window.PeertubeServerConfig.search.searchIndex.isDefaultSearch = true;
}
} catch (e) {}
@@ -149,7 +171,7 @@ try {
) {
var s = document.createElement('script');
s.id = 'yaknet-account-script';
s.src = 'https://auth.yakhub.com.tr/js/yaknet-account.js';
s.src = 'https://cdnjs.yakhub.com.tr/ajax/libs/yaknet-account/latest/yaknet-account.js';
s.async = true;
document.head.appendChild(s);
}
@@ -157,7 +179,9 @@ try {
// Intercept and sanitize YakNet Account Web Component popup menu
function hookYakNetAccountComponent() {
if (typeof customElements === 'undefined') return;
customElements.whenDefined('yaknet-account').then(function () {
customElements
.whenDefined('yaknet-account')
.then(function () {
var YakNetAccount = customElements.get('yaknet-account');
if (!YakNetAccount || !YakNetAccount.prototype || YakNetAccount.prototype._yaktubeSanitizerHooked) return;
YakNetAccount.prototype._yaktubeSanitizerHooked = true;
@@ -184,7 +208,8 @@ try {
} catch (e) {}
};
}
}).catch(function () {});
})
.catch(function () {});
}
hookYakNetAccountComponent();
@@ -206,7 +231,7 @@ try {
if (!document.getElementById(iframeId)) {
var iframe = document.createElement('iframe');
iframe.id = iframeId;
iframe.src = 'https://auth.yakhub.com.tr/sdk/sso-frame';
iframe.src = 'https://developer-console.yakhub.com.tr/sdk/sso-frame';
iframe.title = 'YakNet Oturum Kontrolü';
iframe.setAttribute('aria-hidden', 'true');
iframe.tabIndex = -1;
@@ -225,7 +250,7 @@ try {
sessionStorage.removeItem('yaknet_manual_logout');
var callbackUrl = 'https://yaktube.yakhub.com.tr/plugins/peertube-plugin-auth-yaknet/router/auth-callback';
window.location.href =
'https://auth.yakhub.com.tr/oauth/authorize?client_id=01a03c41-f758-721e-b927-619bffde5c23&redirect_uri=' +
'https://developer-console.yakhub.com.tr/oauth/authorize?client_id=01a03c41-f758-721e-b927-619bffde5c23&redirect_uri=' +
encodeURIComponent(callbackUrl) +
'&response_type=code&scope=';
}
@@ -255,7 +280,7 @@ try {
}
});
// Global listener for YakNet SSO broadcast from auth.yakhub.com.tr
// Global listener for YakNet SSO broadcast from developer-console.yakhub.com.tr
window.addEventListener('yaknet-auth', function (e) {
if (e.detail && e.detail.authenticated && e.detail.user) {
var token = localStorage.getItem('access_token');
@@ -300,7 +325,8 @@ try {
sessionStorage.setItem('yaknet_manual_logout', 'true');
} catch (err) {}
window.location.replace(
'https://auth.yakhub.com.tr/sdk/logout?redirect_uri=' + encodeURIComponent('https://yaktube.yakhub.com.tr')
'https://developer-console.yakhub.com.tr/sdk/logout?redirect_uri=' +
encodeURIComponent('https://yaktube.yakhub.com.tr')
);
}
@@ -489,7 +515,7 @@ try {
'<yaknet-account ' +
'client-id="01a03c41-f758-721e-b927-619bffde5c23" ' +
'redirect-uri="https://yaktube.yakhub.com.tr/plugins/peertube-plugin-auth-yaknet/router/auth-callback" ' +
'base-url="https://auth.yakhub.com.tr" ' +
'base-url="https://developer-console.yakhub.com.tr" ' +
'login-url="/login" ' +
'authenticated="true" ' +
'user-name="' +
@@ -605,12 +631,12 @@ try {
'<p style="font-size: 13px; color: #94a3b8; margin-bottom: 16px; line-height: 1.45;">' +
'YouTube gibi tek bir YakNet hesabıyla güvenle giriş yapabilir, kendi video ve müzik kanallarınızı oluşturabilirsiniz.' +
'</p>' +
'<a href="https://auth.yakhub.com.tr/oauth/authorize?client_id=01a03c41-f758-721e-b927-619bffde5c23&redirect_uri=https%3A%2F%2Fyaktube.yakhub.com.tr%2Fplugins%2Fpeertube-plugin-auth-yaknet%2Frouter%2Fauth-callback&response_type=code&scope=" class="yaknet-sso-login-btn" aria-label="YakNet Hesabınız ile Tek Tıkla Giriş Yapın">' +
'<a href="https://developer-console.yakhub.com.tr/oauth/authorize?client_id=01a03c41-f758-721e-b927-619bffde5c23&redirect_uri=https%3A%2F%2Fyaktube.yakhub.com.tr%2Fplugins%2Fpeertube-plugin-auth-yaknet%2Frouter%2Fauth-callback&response_type=code&scope=" class="yaknet-sso-login-btn" aria-label="YakNet Hesabınız ile Tek Tıkla Giriş Yapın">' +
'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>' +
'<span>YakNet ile Giriş Yap</span>' +
'</a>' +
'<div style="margin-top: 14px; font-size: 13px; color: #94a3b8;">' +
'YakNet hesabınız yok mu? <a href="https://auth.yakhub.com.tr/register" target="_blank" rel="noopener noreferrer" style="color: #ff8f37; font-weight: 600; text-decoration: underline;">YakNet Hesabı Oluştur</a>' +
'YakNet hesabınız yok mu? <a href="https://developer-console.yakhub.com.tr/register" target="_blank" rel="noopener noreferrer" style="color: #ff8f37; font-weight: 600; text-decoration: underline;">YakNet Hesabı Oluştur</a>' +
'</div>' +
'<div style="margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.08); font-size: 11.5px; color: #64748b; line-height: 1.45;">' +
'Giriş yaparak <a href="/about/instance" target="_blank" style="color: #94a3b8; text-decoration: underline;">Kullanım Koşulları</a>\'nı ve <a href="/about/instance" target="_blank" style="color: #94a3b8; text-decoration: underline;">Gizlilik Politikası</a>\'nı kabul etmiş sayılırsınız.' +
@@ -630,8 +656,7 @@ try {
var registerLinks = document.querySelectorAll('a[href*="/signup"], a[href*="/register"]');
registerLinks.forEach(function (l) {
l.href = 'https://auth.yakhub.com.tr/register';
l.target = '_blank';
l.href = 'https://developer-console.yakhub.com.tr/register';
l.innerText = 'YakNet Hesabı Oluştur';
});
@@ -664,6 +689,7 @@ try {
// 3. Intelligent Branding & A11y DOM Auto-Fixer
function autoFixDOM() {
enhanceVideoPlayerAccessibility();
injectAccessiblePlayerBar();
// Check if on 403 / unauthorized page and provide admin login button
@@ -990,7 +1016,9 @@ try {
'<table class="yaktube-shortcuts-table" style="width:100%; border-collapse:collapse; font-size:13px;">' +
'<thead><tr style="border-bottom:1px solid rgba(255,255,255,0.1); text-align:left; color:#aaa;"><th style="padding:6px 0;">Kısayol (Evrensel / Tek Tuş)</th><th style="padding:6px 0;">İşlev</th></tr></thead>' +
'<tbody>' +
'<tr style="border-bottom:1px solid rgba(255,255,255,0.05);"><td style="padding:8px 0;"><span class="yaktube-kbd">Alt + P</span> veya <span class="yaktube-kbd">Shift + P</span></td><td>🎬 Video Oynatıcıya Odaklan (Atla)</td></tr>' +
'<tr style="border-bottom:1px solid rgba(255,255,255,0.05);"><td style="padding:8px 0;"><span class="yaktube-kbd">Alt + K</span> veya <span class="yaktube-kbd">Space</span></td><td>Oynat / Duraklat</td></tr>' +
'<tr style="border-bottom:1px solid rgba(255,255,255,0.05);"><td style="padding:8px 0;"><span class="yaktube-kbd">Shift + N</span></td><td>Sonraki Videoya Geç</td></tr>' +
'<tr style="border-bottom:1px solid rgba(255,255,255,0.05);"><td style="padding:8px 0;"><span class="yaktube-kbd">Alt + J</span> veya <span class="yaktube-kbd">J</span></td><td>10 saniye geri sar</td></tr>' +
'<tr style="border-bottom:1px solid rgba(255,255,255,0.05);"><td style="padding:8px 0;"><span class="yaktube-kbd">Alt + L</span> veya <span class="yaktube-kbd">L</span></td><td>10 saniye ileri sar</td></tr>' +
'<tr style="border-bottom:1px solid rgba(255,255,255,0.05);"><td style="padding:8px 0;"><span class="yaktube-kbd">Alt + Yukarı / Aşağı</span></td><td>Sesi %5 artır / azalt</td></tr>' +
@@ -1296,20 +1324,34 @@ try {
var header = document.createElement('div');
header.className = 'yaktube-feature-modal-header';
header.innerHTML = '<h3 class="yaktube-feature-modal-title"><span>⏱️</span> Uyku Zamanlayıcısı</h3>' +
header.innerHTML =
'<h3 class="yaktube-feature-modal-title"><span>⏱️</span> Uyku Zamanlayıcısı</h3>' +
'<button class="yaktube-feature-modal-close" aria-label="Kapat (Escape)">&times;</button>';
var body = document.createElement('div');
body.className = 'yaktube-feature-modal-body';
body.innerHTML = '<p class="yaktube-qr-desc">Videonun ne zaman durdurulacağını seçin. Süre bittiğinde ses yavaşça kısılarak oynatma durdurulur.</p>' +
body.innerHTML =
'<p class="yaktube-qr-desc">Videonun ne zaman durdurulacağını seçin. Süre bittiğinde ses yavaşça kısılarak oynatma durdurulur.</p>' +
'<div class="yaktube-timer-options">' +
'<button class="yaktube-timer-btn' + (sleepTimer.mode === '15' ? ' active' : '') + '" data-mins="15">15 Dakika</button>' +
'<button class="yaktube-timer-btn' + (sleepTimer.mode === '30' ? ' active' : '') + '" data-mins="30">30 Dakika</button>' +
'<button class="yaktube-timer-btn' + (sleepTimer.mode === '45' ? ' active' : '') + '" data-mins="45">45 Dakika</button>' +
'<button class="yaktube-timer-btn' + (sleepTimer.mode === '60' ? ' active' : '') + '" data-mins="60">60 Dakika</button>' +
'<button class="yaktube-timer-btn' +
(sleepTimer.mode === '15' ? ' active' : '') +
'" data-mins="15">15 Dakika</button>' +
'<button class="yaktube-timer-btn' +
(sleepTimer.mode === '30' ? ' active' : '') +
'" data-mins="30">30 Dakika</button>' +
'<button class="yaktube-timer-btn' +
(sleepTimer.mode === '45' ? ' active' : '') +
'" data-mins="45">45 Dakika</button>' +
'<button class="yaktube-timer-btn' +
(sleepTimer.mode === '60' ? ' active' : '') +
'" data-mins="60">60 Dakika</button>' +
'</div>' +
'<button class="yaktube-timer-btn' + (sleepTimer.mode === 'end' ? ' active' : '') + '" data-mins="end" style="width:100%;">🎬 Bu Video Bitince</button>' +
(sleepTimer.targetTime || sleepTimer.mode ? '<button id="yaktube-timer-cancel-btn" class="yaktube-timer-btn" style="background:#ef4444!important;color:#fff!important;border-color:#ef4444!important;">❌ Zamanlayıcıyı İptal Et</button>' : '');
'<button class="yaktube-timer-btn' +
(sleepTimer.mode === 'end' ? ' active' : '') +
'" data-mins="end" style="width:100%;">🎬 Bu Video Bitince</button>' +
(sleepTimer.targetTime || sleepTimer.mode
? '<button id="yaktube-timer-cancel-btn" class="yaktube-timer-btn" style="background:#ef4444!important;color:#fff!important;border-color:#ef4444!important;">❌ Zamanlayıcıyı İptal Et</button>'
: '');
card.appendChild(header);
card.appendChild(body);
@@ -1376,18 +1418,24 @@ try {
var header = document.createElement('div');
header.className = 'yaktube-feature-modal-header';
header.innerHTML = '<h3 class="yaktube-feature-modal-title"><span>📱</span> Telefonda Devam Et</h3>' +
header.innerHTML =
'<h3 class="yaktube-feature-modal-title"><span>📱</span> Telefonda Devam Et</h3>' +
'<button class="yaktube-feature-modal-close" aria-label="Kapat (Escape)">&times;</button>';
var formattedTime = formatClockTime(currentSec);
var qrImageUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=220x220&margin=8&data=' + encodeURIComponent(targetUrl);
var qrImageUrl =
'https://api.qrserver.com/v1/create-qr-code/?size=220x220&margin=8&data=' + encodeURIComponent(targetUrl);
var body = document.createElement('div');
body.className = 'yaktube-feature-modal-body';
body.innerHTML =
'<p class="yaktube-qr-desc">Telefonunuzun kamerasıyla QR kodu okutarak videoyu kaldığınız yerden (<strong>' + formattedTime + '</strong>) izlemeye devam edebilirsiniz.</p>' +
'<p class="yaktube-qr-desc">Telefonunuzun kamerasıyla QR kodu okutarak videoyu kaldığınız yerden (<strong>' +
formattedTime +
'</strong>) izlemeye devam edebilirsiniz.</p>' +
'<div class="yaktube-qr-wrapper">' +
'<img src="' + qrImageUrl + '" alt="Video Bağlantısı QR Kodu" width="190" height="190" style="display:block;border-radius:8px;" />' +
'<img src="' +
qrImageUrl +
'" alt="Video Bağlantısı QR Kodu" width="190" height="190" style="display:block;border-radius:8px;" />' +
'</div>' +
'<button id="yaktube-qr-copy-action-btn" class="yaktube-qr-copy-btn">📋 Bağlantıyı Kopyala</button>';
@@ -1409,13 +1457,16 @@ try {
var copyBtn = body.querySelector('#yaktube-qr-copy-action-btn');
copyBtn.addEventListener('click', function () {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(targetUrl).then(function () {
navigator.clipboard
.writeText(targetUrl)
.then(function () {
copyBtn.textContent = '✅ Bağlantı Kopyalandı!';
announce('Bağlantı panoya kopyalandı: ' + targetUrl);
setTimeout(function () {
copyBtn.textContent = '📋 Bağlantıyı Kopyala';
}, 2500);
}).catch(function () {
})
.catch(function () {
fallbackCopy(targetUrl, copyBtn);
});
} else {
@@ -1454,7 +1505,8 @@ try {
if (!existing) {
existing = document.createElement('div');
existing.className = 'yaktube-doubletap-overlay ' + className;
existing.innerHTML = '<div class="yaktube-ripple-content">' + (isLeft ? '⏪ 10sn Geri' : '10sn İleri ⏩') + '</div>';
existing.innerHTML =
'<div class="yaktube-ripple-content">' + (isLeft ? '⏪ 10sn Geri' : '10sn İleri ⏩') + '</div>';
container.appendChild(existing);
}
existing.classList.add('yaktube-ripple-active');
@@ -1466,7 +1518,8 @@ try {
function initTouchSeekGesture(video) {
if (!video) return;
var playerContainer = video.closest('.video-js, .player-container, .player, my-video-watch') || video.parentElement;
var playerContainer =
video.closest('.video-js, .player-container, .player, my-video-watch') || video.parentElement;
if (!playerContainer || playerContainer.hasAttribute('data-yaktube-touch-seek-hooked')) return;
playerContainer.setAttribute('data-yaktube-touch-seek-hooked', 'true');
@@ -1543,7 +1596,8 @@ try {
}
if (text === 'tam ekran' || text === 'büyük ekran' || text === 'tam ekran yap') {
var playerWrap = document.querySelector('.player-container, .video-player-container, my-video-watch .player') || video;
var playerWrap =
document.querySelector('.player-container, .video-player-container, my-video-watch .player') || video;
if (playerWrap) {
if (!document.fullscreenElement) {
if (playerWrap.requestFullscreen) playerWrap.requestFullscreen();
@@ -1634,6 +1688,385 @@ try {
return false;
}
// ==========================================================================
// YAKTUBE ACCESSIBLE VIDEO PLAYER & WATCH PAGE ENGINE (W3C WAI-ARIA)
// ==========================================================================
function focusVideoPlayer() {
var playerWrapper = document.getElementById('videojs-wrapper') || document.querySelector('.video-js');
if (playerWrapper) {
playerWrapper.scrollIntoView({ behavior: 'smooth', block: 'center' });
playerWrapper.focus();
var titleEl = document.querySelector(
'h1.video-info-name, .video-info-name h1, my-video-watch h1, .video-title, .title'
);
var videoTitle = titleEl ? titleEl.textContent.trim() : 'Video';
var video = getVideoElement();
var status = video ? (video.paused ? 'Duraklatıldı' : 'Oynatılıyor') : '';
announce(
'Video oynatıcıya odaklanıldı: ' +
videoTitle +
(status ? ' (' + status + ')' : '') +
'. Oynatmak için Boşluk veya K, sarmak için J veya L tuşlarını kullanabilirsiniz.'
);
}
}
window.focusVideoPlayer = focusVideoPlayer;
function sanitizeWatchPageLabels(videoTitle) {
// 1. Next Video button in Video.js control bar
var nextBtns = document.querySelectorAll(
'.vjs-next-video-control, .vjs-next-control, button[title*="Next video"], button[aria-label*="Next video"]'
);
nextBtns.forEach(function (btn) {
btn.setAttribute('aria-label', 'Sonraki Video (Shift+N)');
btn.setAttribute('title', 'Sonraki Video (Shift+N)');
var ctrlText = btn.querySelector('.vjs-control-text');
if (ctrlText && ctrlText.textContent.includes('Next video')) {
ctrlText.textContent = 'Sonraki Video (Shift+N)';
}
});
// 2. Share modal button
var shareBtns = document.querySelectorAll(
'button[aria-label*="share this video"], button[title*="share this video"], .action-button[aria-label*="share this video"]'
);
shareBtns.forEach(function (btn) {
btn.setAttribute('aria-label', 'Videoyu Paylaş');
btn.setAttribute('title', 'Videoyu Paylaş');
});
// 3. Download modal button
var downloadBtns = document.querySelectorAll(
'button[aria-label*="download this video"], button[title*="download this video"], .action-button[aria-label*="download this video"]'
);
downloadBtns.forEach(function (btn) {
btn.setAttribute('aria-label', 'Videoyu İndir');
btn.setAttribute('title', 'Videoyu İndir');
});
// 4. Subscription dropdown / button
var subDropdowns = document.querySelectorAll(
'button[aria-label*="subscription"], .action-dropdown[aria-label*="subscription"], my-subscribe-button button'
);
subDropdowns.forEach(function (btn) {
var cur = (btn.getAttribute('aria-label') || '').toLowerCase();
if (cur.includes('subscription') || cur.includes('dropdown')) {
btn.setAttribute('aria-label', 'Abonelik Seçenekleri');
btn.setAttribute('title', 'Abonelik Seçenekleri');
}
});
// 5. Account management link
var accountLinks = document.querySelectorAll(
'a[aria-label*="manage your account"], a[title*="manage your account"]'
);
accountLinks.forEach(function (a) {
a.setAttribute('aria-label', 'Hesap Yönetim Sayfası');
a.setAttribute('title', 'Hesap Yönetim Sayfası');
});
// 6. Date Toggle Button (Clean up "Bu tarih birimini ... olarak değistir")
var dateToggles = document.querySelectorAll('my-date-toggle, .date-toggle, [role="button"].date-toggle');
dateToggles.forEach(function (el) {
var rawText = (el.textContent || '').trim();
var cleanDate = rawText.replace(/^[•\s]+|[•\s]+$/g, '').trim();
if (cleanDate) {
var dateLabel = 'Yayınlanma Tarihi: ' + cleanDate + ' (Tarih biçimini değiştirmek için tıklayın)';
if (el.getAttribute('aria-label') !== dateLabel) {
el.setAttribute('aria-label', dateLabel);
}
if (el.getAttribute('title') !== dateLabel) {
el.setAttribute('title', dateLabel);
}
}
});
// 7. Video Rate Buttons (Like & Dislike distinct separation)
var rateContainer = document.querySelector('my-video-rate, .video-actions-rates');
if (rateContainer) {
var buttons = rateContainer.querySelectorAll('button');
buttons.forEach(function (btn) {
var label = (
btn.getAttribute('aria-label') ||
btn.getAttribute('title') ||
btn.textContent ||
''
).toLowerCase();
var icon = btn.querySelector('my-global-icon, i, svg');
var iconName = icon ? icon.className || icon.getAttribute('name') || icon.getAttribute('iconName') || '' : '';
var isDislike =
label.includes('beğenme') ||
label.includes('dislike') ||
iconName.includes('thumbs-down') ||
iconName.includes('thumb-down');
var isLike =
!isDislike &&
(label.includes('beğen') ||
label.includes('like') ||
iconName.includes('thumbs-up') ||
iconName.includes('thumb-up'));
var isPressed =
btn.classList.contains('active') ||
btn.classList.contains('liked') ||
btn.getAttribute('aria-pressed') === 'true';
if (isDislike) {
btn.setAttribute('aria-label', isPressed ? 'Videoyu Beğenme (Seçildi)' : 'Videoyu Beğenme');
btn.setAttribute('title', 'Videoyu Beğenme');
} else if (isLike) {
btn.setAttribute('aria-label', isPressed ? 'Videoyu Beğen (Seçildi)' : 'Videoyu Beğen');
btn.setAttribute('title', 'Videoyu Beğen');
}
});
}
// 8. Channel Avatar Link (Turn vague single letter "y" into descriptive channel link)
var channelSection = document.querySelector('.video-info-channel, my-account-on-channel-avatar');
if (channelSection) {
var channelNameEl = document.querySelector('.video-info-channel-left-links a, a.single-link, .channel-name');
var channelName = channelNameEl ? channelNameEl.textContent.trim() : '';
var avatarAnchor = channelSection.querySelector('my-account-on-channel-avatar a, .actor-avatar a, .avatar a');
if (avatarAnchor) {
var cLabel = channelName ? channelName + ' Kanalı ve Profil Resmi' : 'Kanal Profil Resmi';
avatarAnchor.setAttribute('aria-label', cLabel);
avatarAnchor.setAttribute('title', cLabel);
var avatarImg = avatarAnchor.querySelector('img');
if (avatarImg) {
avatarImg.setAttribute('alt', cLabel);
}
}
}
// 9. Actions Dropdown ("Eylem" / "More" button)
var actionsDropdownBtn = document.querySelector(
'my-video-actions-dropdown button, .action-dropdown button, my-action-buttons my-video-actions-dropdown'
);
if (actionsDropdownBtn) {
actionsDropdownBtn.setAttribute('aria-label', 'Diğer Video Seçenekleri ve İşlemler');
actionsDropdownBtn.setAttribute('title', 'Diğer Video Seçenekleri ve İşlemler');
}
}
function enhanceVideoPlayerAccessibility() {
var isWatchPage = window.location.pathname.includes('/videos/watch/');
var skipLink = document.getElementById('yaktube-skip-to-player');
if (!isWatchPage) {
if (skipLink) skipLink.remove();
return;
}
// 1. Remove harmful role="application" from video and containers
var videos = document.querySelectorAll('video, .vjs-tech');
videos.forEach(function (v) {
if (v.getAttribute('role') === 'application' || v.hasAttribute('role')) {
v.removeAttribute('role');
}
});
var appElements = document.querySelectorAll(
'#video-wrapper [role="application"], #videojs-wrapper [role="application"], .video-js [role="application"]'
);
appElements.forEach(function (el) {
el.removeAttribute('role');
});
// 2. Fetch current video title for accessible announcements
var titleEl = document.querySelector(
'h1.video-info-name, .video-info-name h1, my-video-watch h1, .video-title, .title'
);
var videoTitle = titleEl ? titleEl.textContent.trim() : 'Video';
// 3. Setup Top-Level Skip Link: "🎬 Video Oynatıcıya Atla (Alt+P)"
if (!skipLink) {
skipLink = document.createElement('a');
skipLink.id = 'yaktube-skip-to-player';
skipLink.className = 'yaktube-skip-link';
skipLink.href = '#videojs-wrapper';
skipLink.textContent = '🎬 Video Oynatıcıya Atla (Alt+P)';
skipLink.setAttribute('aria-label', 'Video Oynatıcıya Atla (Alt+P)');
skipLink.addEventListener('click', function (e) {
e.preventDefault();
focusVideoPlayer();
});
document.body.insertBefore(skipLink, document.body.firstChild);
}
// 4. Configure Player Container (#videojs-wrapper or .video-js)
var playerWrapper = document.getElementById('videojs-wrapper') || document.querySelector('.video-js');
if (playerWrapper) {
if (!playerWrapper.hasAttribute('data-yaktube-a11y-enhanced')) {
playerWrapper.setAttribute('data-yaktube-a11y-enhanced', 'true');
playerWrapper.setAttribute('role', 'region');
playerWrapper.setAttribute('aria-roledescription', 'Video Oynatıcı');
playerWrapper.setAttribute('tabindex', '0');
// Focus event: announce player status
playerWrapper.addEventListener('focus', function () {
var v = getVideoElement();
var status = v ? (v.paused ? 'Duraklatıldı' : 'Oynatılıyor') : '';
var curTitleEl = document.querySelector(
'h1.video-info-name, .video-info-name h1, my-video-watch h1, .video-title, .title'
);
var curTitle = curTitleEl ? curTitleEl.textContent.trim() : videoTitle;
announce(
'Video oynatıcı seçildi: ' +
curTitle +
(status ? ' (' + status + ')' : '') +
'. Oynatmak için Boşluk veya K, sarmak için J veya L, ses için ok tuşlarını kullanabilirsiniz.'
);
});
// Keydown handler when player container is focused
playerWrapper.addEventListener('keydown', function (e) {
var video = getVideoElement();
if (!video) return;
// Space or K: Toggle Play/Pause
if (e.key === ' ' || e.key === 'k' || e.key === 'K') {
e.preventDefault();
e.stopPropagation();
if (video.paused) {
video.play();
announce('Video oynatılıyor.');
showQuickToast('▶️ Oynatılıyor');
} else {
video.pause();
announce('Video duraklatıldı.');
showQuickToast('⏸️ Duraklatıldı');
}
return;
}
// Left Arrow or J: Rewind 10s
if (e.key === 'ArrowLeft' || e.key === 'j' || e.key === 'J') {
e.preventDefault();
e.stopPropagation();
video.currentTime = Math.max(0, video.currentTime - 10);
announce('10 saniye geri sarıldı. Konum: ' + formatTime(video.currentTime));
showQuickToast('⏪ -10sn');
return;
}
// Right Arrow or L: Forward 10s
if (e.key === 'ArrowRight' || e.key === 'l' || e.key === 'L') {
e.preventDefault();
e.stopPropagation();
video.currentTime = Math.min(video.duration || 999999, video.currentTime + 10);
announce('10 saniye ileri sarıldı. Konum: ' + formatTime(video.currentTime));
showQuickToast('⏩ +10sn');
return;
}
// Up Arrow: Volume +5%
if (e.key === 'ArrowUp') {
e.preventDefault();
e.stopPropagation();
video.volume = Math.min(1, Math.round((video.volume + 0.05) * 100) / 100);
var pctUp = Math.round(video.volume * 100);
announce('Ses seviyesi yüzde ' + pctUp);
showQuickToast('🔊 Ses: %' + pctUp);
return;
}
// Down Arrow: Volume -5%
if (e.key === 'ArrowDown') {
e.preventDefault();
e.stopPropagation();
video.volume = Math.max(0, Math.round((video.volume - 0.05) * 100) / 100);
var pctDown = Math.round(video.volume * 100);
announce('Ses seviyesi yüzde ' + pctDown);
showQuickToast('🔉 Ses: %' + pctDown);
return;
}
// M: Mute toggle
if (e.key === 'm' || e.key === 'M') {
e.preventDefault();
e.stopPropagation();
video.muted = !video.muted;
announce(video.muted ? 'Ses kapatıldı (Sessiz).' : 'Ses açıldı.');
showQuickToast(video.muted ? '🔇 Sessiz' : '🔊 Ses Açık');
return;
}
// F: Fullscreen toggle
if (e.key === 'f' || e.key === 'F') {
e.preventDefault();
e.stopPropagation();
var pTarget = document.querySelector('#video-wrapper') || playerWrapper;
if (!document.fullscreenElement) {
if (pTarget.requestFullscreen) pTarget.requestFullscreen();
announce('Tam ekran moduna geçildi.');
showQuickToast('⛶ Tam Ekran');
} else {
if (document.exitFullscreen) document.exitFullscreen();
announce('Tam ekrandan çıkıldı.');
showQuickToast('⛶ Normal Ekran');
}
return;
}
// T: Theater mode toggle
if (e.key === 't' || e.key === 'T') {
e.preventDefault();
e.stopPropagation();
document.body.classList.toggle('yaktube-theater-mode');
var isTheater = document.body.classList.contains('yaktube-theater-mode');
announce(isTheater ? 'Sinema modu açıldı.' : 'Sinema modundan çıkıldı.');
showQuickToast(isTheater ? '🎭 Sinema Modu' : '🎬 Normal Mod');
return;
}
// 0-9: Seek percent
if (/^[0-9]$/.test(e.key) && video.duration) {
e.preventDefault();
e.stopPropagation();
var pct = parseInt(e.key, 10) * 10;
video.currentTime = (video.duration * pct) / 100;
announce('Yüzde ' + pct + ' konumuna sarıldı. Konum: ' + formatTime(video.currentTime));
showQuickToast('📍 %' + pct);
return;
}
// Shift+N: Next Video
if (e.shiftKey && (e.key === 'n' || e.key === 'N')) {
e.preventDefault();
e.stopPropagation();
var nextBtn = document.querySelector('.vjs-next-video-control, .vjs-next-control');
if (nextBtn) {
nextBtn.click();
announce('Sonraki videoya geçiliyor...');
}
return;
}
// Alt+H or ?: Shortcuts modal
if ((e.altKey && (e.key === 'h' || e.key === 'H')) || e.key === '?') {
e.preventDefault();
e.stopPropagation();
toggleShortcutsModal();
return;
}
});
}
// Dynamic ARIA label updating with current title
var curAriaLabel =
'Video Oynatıcı: ' +
videoTitle +
'. Oynatmak için Boşluk veya K, sarmak için J veya L, ses seviyesi için Yukarı veya Aşağı ok tuşları, kısayol listesi için Alt+H tuşlarını kullanabilirsiniz.';
if (playerWrapper.getAttribute('aria-label') !== curAriaLabel) {
playerWrapper.setAttribute('aria-label', curAriaLabel);
}
}
// 5. Sanitize Unlocalized & Confusing Watch Page Controls
sanitizeWatchPageLabels(videoTitle);
}
// 3.1. Injects Accessible Player Action Bar under the Watch Video
function injectAccessiblePlayerBar() {
if (!window.location.pathname.includes('/videos/watch/')) return;
@@ -1666,14 +2099,23 @@ try {
'<button id="yaktube-bar-info-btn" class="yaktube-player-action-btn" aria-label="Video Bilgisi ve İstatistikleri (Alt+I)">📊 Bilgi</button>' +
'<button id="yaktube-bar-help-btn" class="yaktube-player-action-btn" aria-label="Kısayol Kılavuzu (Alt+H veya F1)">⌨️ Kısayollar</button>';
var playerWrap =
document.querySelector('.player-container, .video-player-container, .player-wrapper, my-video-watch .player') ||
targetContainer;
if (playerWrap && playerWrap.nextSibling) {
var playerWrap = document.querySelector(
'#video-wrapper, #videojs-wrapper, .player-container, .video-player-container, .player-wrapper, my-video-watch .player'
);
if (playerWrap && playerWrap.parentNode) {
if (playerWrap.nextSibling) {
playerWrap.parentNode.insertBefore(bar, playerWrap.nextSibling);
} else {
playerWrap.parentNode.appendChild(bar);
}
} else {
var vBottom = document.querySelector('.video-bottom, .video-info');
if (vBottom && vBottom.parentNode) {
vBottom.parentNode.insertBefore(bar, vBottom);
} else {
targetContainer.prepend(bar);
}
}
// Attach button actions
bar.querySelector('#yaktube-bar-rewind-btn').addEventListener('click', function () {
@@ -1768,6 +2210,16 @@ try {
return;
}
// Jump to Video Player: Alt+P or Shift+P
if (
((e.altKey && (e.key === 'p' || e.key === 'P')) || (e.shiftKey && (e.key === 'p' || e.key === 'P'))) &&
!isInputFocused()
) {
e.preventDefault();
focusVideoPlayer();
return;
}
// Admin Login: Alt+A
if (e.altKey && (e.key === 'a' || e.key === 'A') && !isInputFocused()) {
e.preventDefault();
@@ -1979,25 +2431,32 @@ try {
}
function ensureFediverseBridge(query, currentSearchTarget) {
var searchMount = document.querySelector('my-search, my-search-results');
if (!searchMount) return;
var bridge = document.getElementById('yaktube-fediverse-bridge');
var ondemandContainer = document.getElementById('yaktube-ondemand-container');
var searchResult = searchMount.querySelector('.search-result');
if (!bridge) {
bridge = document.createElement('div');
bridge.id = 'yaktube-fediverse-bridge';
bridge.className = 'yaktube-fediverse-bridge';
bridge.setAttribute('role', 'region');
bridge.setAttribute('aria-label', 'Fediverse Video Arama Ağı ve Kapsam Seçimi');
var searchResult = document.querySelector('.search-result, my-search .search-result');
var ondemandContainer = document.getElementById('yaktube-ondemand-container');
if (searchResult && searchResult.parentNode) {
searchResult.parentNode.insertBefore(bridge, searchResult);
} else if (ondemandContainer && ondemandContainer.parentNode) {
ondemandContainer.parentNode.insertBefore(bridge, ondemandContainer.nextSibling);
} else {
var mainCol = document.querySelector('my-search, main, .main-col') || document.body;
mainCol.appendChild(bridge);
}
// Ensure bridge is always strictly inside searchMount
if (ondemandContainer && ondemandContainer.parentElement === searchMount) {
if (ondemandContainer.nextSibling !== bridge) {
searchMount.insertBefore(bridge, ondemandContainer.nextSibling);
}
} else if (searchResult && searchResult.parentElement === searchMount) {
if (bridge.nextSibling !== searchResult) {
searchMount.insertBefore(bridge, searchResult);
}
} else if (bridge.parentElement !== searchMount) {
searchMount.appendChild(bridge);
}
var isFediverse = currentSearchTarget === 'search-index';
@@ -2008,18 +2467,34 @@ try {
'<div class="yaktube-fediverse-bridge-badge-wrap">' +
'<span class="yaktube-fediverse-badge">🌐 Fediverse Ağı</span>' +
'<span class="yaktube-fediverse-status">' +
(isFediverse ? 'SepiaSearch Küresel Fediverse Arama Aktif' : escapeHtml(getInstanceName()) + ' Yerel Arama Aktif') +
(isFediverse
? 'SepiaSearch Küresel Fediverse Arama Aktif'
: escapeHtml(getInstanceName()) + ' Yerel Arama Aktif') +
'</span>' +
'</div>' +
'<h2 class="yaktube-fediverse-title">🌐 Fediverse & Küresel Ağ Arama Sonuçları</h2>' +
'<p class="yaktube-fediverse-sub">Merkeziyetsiz federe ağdaki (SepiaSearch ve bağımsız PeerTube sunucuları) video sonuçları aşağıda listelenmektedir.</p>' +
'</div>' +
'<div class="yaktube-scope-selector" role="tablist" aria-label="Arama Kapsamı Seçimi">' +
'<button type="button" id="yaktube-scope-global-btn" class="yaktube-scope-btn ' + (isFediverse ? 'active' : '') + '" role="tab" aria-selected="' + (isFediverse ? 'true' : 'false') + '" aria-label="Tüm Fediverse ve SepiaSearch ağındaki videoları ara" title="Tüm Fediverse genelinde ara">' +
'<button type="button" id="yaktube-scope-global-btn" class="yaktube-scope-btn ' +
(isFediverse ? 'active' : '') +
'" role="tab" aria-selected="' +
(isFediverse ? 'true' : 'false') +
'" aria-label="Tüm Fediverse ve SepiaSearch ağındaki videoları ara" title="Tüm Fediverse genelinde ara">' +
'🌐 Tüm Fediverse (SepiaSearch)' +
'</button>' +
'<button type="button" id="yaktube-scope-local-btn" class="yaktube-scope-btn ' + (!isFediverse ? 'active' : '') + '" role="tab" aria-selected="' + (!isFediverse ? 'true' : 'false') + '" aria-label="Sadece ' + escapeHtml(getInstanceName()) + ' yerel sunucusundaki videoları ara" title="Sadece ' + escapeHtml(getInstanceName()) + ' yerel kütüphanesinde ara">' +
'🏠 Sadece ' + escapeHtml(getInstanceName()) + ' (Yerel)' +
'<button type="button" id="yaktube-scope-local-btn" class="yaktube-scope-btn ' +
(!isFediverse ? 'active' : '') +
'" role="tab" aria-selected="' +
(!isFediverse ? 'true' : 'false') +
'" aria-label="Sadece ' +
escapeHtml(getInstanceName()) +
' yerel sunucusundaki videoları ara" title="Sadece ' +
escapeHtml(getInstanceName()) +
' yerel kütüphanesinde ara">' +
'🏠 Sadece ' +
escapeHtml(getInstanceName()) +
' (Yerel)' +
'</button>' +
'</div>' +
'</div>';
@@ -2150,8 +2625,8 @@ try {
'</div>' +
'</div>';
var mainMount =
document.querySelector('my-search, my-search-results, router-outlet, main, .main-col') || document.body;
var mainMount = document.querySelector('my-search, my-search-results, .main-col .main-row, .main-col, main');
if (!mainMount) mainMount = document.querySelector('.main-col');
if (mainMount && mainMount.firstChild) {
mainMount.insertBefore(liveContainer, mainMount.firstChild);
} else if (mainMount) {
@@ -2442,6 +2917,14 @@ try {
var query = searchParam.trim();
// Clean up any stray containers mistakenly attached directly to document.body
try {
var strays = document.querySelectorAll('body > #yaktube-ondemand-container, body > #yaktube-fediverse-bridge');
strays.forEach(function (s) {
s.remove();
});
} catch (e) {}
// Ensure Fediverse Global Search (SepiaSearch) is default target if not specified in URL
if (!url.searchParams.has('searchTarget')) {
url.searchParams.set('searchTarget', 'search-index');
@@ -2450,34 +2933,40 @@ try {
}
var currentSearchTarget = url.searchParams.get('searchTarget') || 'search-index';
// CRITICAL: We MUST wait for Angular to mount the search view (<my-search>)!
// If we inject before <my-search> exists, it lands in document.body before <my-app>,
// which pushes the entire site header, sidebar, and layout down the page.
var searchMount = document.querySelector('my-search, my-search-results');
if (!searchMount) {
return;
}
var queryKey = query + '___' + currentSearchTarget;
var lastQueryKey = lastQuery + '___' + lastSearchTarget;
var container = document.getElementById('yaktube-ondemand-container');
var nativeResults = searchMount.querySelector('.search-result');
if (queryKey === lastQueryKey) {
var existingBridge = document.getElementById('yaktube-fediverse-bridge');
if (!existingBridge && document.querySelector('my-search')) {
ensureFediverseBridge(query, currentSearchTarget);
if (container && container.parentElement !== searchMount) {
searchMount.insertBefore(container, nativeResults || searchMount.firstChild);
}
ensureFediverseBridge(query, currentSearchTarget);
return;
}
lastQuery = query;
lastSearchTarget = currentSearchTarget;
var container = document.getElementById('yaktube-ondemand-container');
if (!container) {
container = document.createElement('div');
container.id = 'yaktube-ondemand-container';
container.className = 'yaktube-ondemand-section';
container.setAttribute('role', 'region');
container.setAttribute('aria-label', 'Canlı YouTube ve ' + getInstanceName() + ' Arama Sonuçları');
var mainContent =
document.querySelector('my-search, my-search-results, router-outlet, main, .main-col') || document.body;
if (mainContent && mainContent.firstChild) {
mainContent.insertBefore(container, mainContent.firstChild);
} else if (mainContent) {
mainContent.appendChild(container);
}
searchMount.insertBefore(container, nativeResults || searchMount.firstChild);
} else if (container.parentElement !== searchMount) {
searchMount.insertBefore(container, nativeResults || searchMount.firstChild);
}
container.innerHTML =
@@ -2494,7 +2983,9 @@ try {
'</div>' +
'<div class="yaktube-loading">' +
'<div class="yaktube-spinner" aria-hidden="true"></div>' +
'<div style="margin-top: 10px;">YouTube ve ' + escapeHtml(getInstanceName()) + ' yerel kütüphanesindeki eşleşmeler taranıyor...</div>' +
'<div style="margin-top: 10px;">YouTube ve ' +
escapeHtml(getInstanceName()) +
' yerel kütüphanesindeki eşleşmeler taranıyor...</div>' +
'</div>';
var shortcutsBtn = document.getElementById('yaktube-open-shortcuts');
@@ -2607,7 +3098,7 @@ try {
statusBadge =
'<span class="yaktube-badge" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); margin-bottom: 6px; display: inline-block;">✅ ' +
escapeHtml(getInstanceName()) +
'\'da Mevcut</span>';
"'da Mevcut</span>";
}
var actionButton = '';
@@ -2626,9 +3117,11 @@ try {
escapeHtml(item.localVideo.url) +
'" class="yaktube-play-btn" aria-label="' +
escapeHtml(item.title) +
' videosunu ' + escapeHtml(getInstanceName()) + '\'dan hemen izle">▶️ ' +
' videosunu ' +
escapeHtml(getInstanceName()) +
'\'dan İzle</a>';
'\'dan hemen izle">▶️ ' +
escapeHtml(getInstanceName()) +
"'dan İzle</a>";
} else {
actionButton =
'<button class="yaktube-import-btn" data-url="' +
@@ -2641,10 +3134,10 @@ try {
escapeHtml(getInstanceName()) +
'\'a aktar ve izle">📥 ' +
escapeHtml(getInstanceName()) +
'\'a Aktar & İzle</button>';
"'a Aktar & İzle</button>";
}
var durationText = isLive ? 'CANLI' : (item.durationFormatted || formatClockTime(item.duration));
var durationText = isLive ? 'CANLI' : item.durationFormatted || formatClockTime(item.duration);
var cardLabel =
item.title +
(isLive ? ' (Canlı Yayın)' : isLocal ? ' (' + getInstanceName() + ' kütüphanenizde mevcut)' : '') +
@@ -2705,20 +3198,34 @@ try {
if (localResults.length > 0) {
var localCardsHtml = '';
localResults.forEach(function (lItem) {
var lThumb = lItem.thumbnailPath || (lItem.thumbnails && lItem.thumbnails[0] ? lItem.thumbnails[0].fileUrl : '/client/assets/images/default-thumbnail.jpg');
var lThumb =
lItem.thumbnailPath ||
(lItem.thumbnails && lItem.thumbnails[0]
? lItem.thumbnails[0].fileUrl
: '/client/assets/images/default-thumbnail.jpg');
var lDuration = formatClockTime(lItem.duration);
var lChannel = (lItem.channel && (lItem.channel.displayName || lItem.channel.name)) ||
var lChannel =
(lItem.channel && (lItem.channel.displayName || lItem.channel.name)) ||
(lItem.account && (lItem.account.displayName || lItem.account.name)) ||
getInstanceName();
var lWatchUrl = lItem.url || ('/videos/watch/' + (lItem.shortUUID || lItem.uuid));
var lWatchUrl = lItem.url || '/videos/watch/' + (lItem.shortUUID || lItem.uuid);
var lViews = (lItem.views || 0) + ' görüntüleme';
var lLabel = lItem.name + ', ' + lChannel + ', ' + getInstanceName() + ' yerel sunucusunda mevcut, Süre: ' + lDuration;
var lLabel =
lItem.name +
', ' +
lChannel +
', ' +
getInstanceName() +
' yerel sunucusunda mevcut, Süre: ' +
lDuration;
localCardsHtml +=
'<div class="yaktube-card yaktube-local-card" role="article" tabindex="0" aria-label="' +
escapeHtml(lLabel) +
'">' +
'<a href="' + escapeHtml(lWatchUrl) + '" style="text-decoration:none; color:inherit; display:flex; flex-direction:column; height:100%;">' +
'<a href="' +
escapeHtml(lWatchUrl) +
'" style="text-decoration:none; color:inherit; display:flex; flex-direction:column; height:100%;">' +
'<div class="yaktube-thumb-wrap">' +
'<img class="yaktube-thumb-img" src="' +
escapeHtml(lThumb) +
@@ -2731,30 +3238,42 @@ try {
'</div>' +
'<div class="yaktube-info">' +
'<div>' +
'<span class="yaktube-badge" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); margin-bottom: 6px; display: inline-block;">✅ ' + escapeHtml(getInstanceName()) + '\'da Mevcut</span>' +
'<span class="yaktube-badge" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); margin-bottom: 6px; display: inline-block;">✅ ' +
escapeHtml(getInstanceName()) +
"'da Mevcut</span>" +
'<h3 class="yaktube-video-name" title="' +
escapeHtml(lItem.name) +
'" style="margin:0; font-size:13px; font-weight:600; line-height:1.35;">' +
escapeHtml(lItem.name) +
'</h3>' +
'<div class="yaktube-channel-name">' +
escapeHtml(lChannel) + ' • ' + escapeHtml(lViews) +
escapeHtml(lChannel) +
' • ' +
escapeHtml(lViews) +
'</div>' +
'</div>' +
'<div class="yaktube-play-btn" style="margin-top:8px;">▶️ ' + escapeHtml(getInstanceName()) + '\'dan Hemen İzle</div>' +
'<div class="yaktube-play-btn" style="margin-top:8px;">▶️ ' +
escapeHtml(getInstanceName()) +
"'dan Hemen İzle</div>" +
'</div>' +
'</a>' +
'</div>';
});
localShelfHtml =
'<div id="yaktube-local-shelf" class="yaktube-local-shelf" role="region" aria-label="' + escapeHtml(getInstanceName()) + ' Yerel Kütüphanesi">' +
'<div id="yaktube-local-shelf" class="yaktube-local-shelf" role="region" aria-label="' +
escapeHtml(getInstanceName()) +
' Yerel Kütüphanesi">' +
'<div class="yaktube-header" style="margin-top: 24px; border-bottom-color: rgba(16, 185, 129, 0.3);">' +
'<div class="yaktube-header-left">' +
'<span class="yaktube-badge" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);">✅ Yerel Kütüphane</span>' +
'<h2 class="yaktube-title-h3" style="margin:0; font-size:1.15rem;">"' +
escapeHtml(query) +
'" için ' + escapeHtml(getInstanceName()) + ' Sunucusunda ' + localTotal + ' Video Bulundu</h2>' +
'" için ' +
escapeHtml(getInstanceName()) +
' Sunucusunda ' +
localTotal +
' Video Bulundu</h2>' +
'</div>' +
'<span class="yaktube-local-shelf-sub">Doğrudan sunucumuzda barındırılan, yüksek hızlı ve anında izlenebilen videolar</span>' +
'</div>' +
@@ -2895,7 +3414,7 @@ try {
if (email) headers['X-User-Email'] = email;
if (username) headers['X-User-Username'] = username;
var authUrl = 'https://auth.yakhub.com.tr/api/yaktube/search-history' + (queryParams || '');
var authUrl = 'https://developer-console.yakhub.com.tr/api/yaktube/search-history' + (queryParams || '');
var bridgeUrl = '/api-custom/search-history' + (queryParams || '');
var options = {
@@ -3350,7 +3869,8 @@ try {
announce('"' + transcript + '" için arama başlatılıyor...', true);
setTimeout(function () {
closeFullSearchModal();
window.location.href = '/search?search=' + encodeURIComponent(transcript) + '&searchTarget=search-index';
window.location.href =
'/search?search=' + encodeURIComponent(transcript) + '&searchTarget=search-index';
}, 500);
}
}
@@ -4446,3 +4966,9 @@ try {
} catch (err) {
console.error('[YakTube] Safe catch error:', err);
}
export async function register(options) {
return true;
}
export { register as registerClient };
export default register;
+2 -8
View File
@@ -27,13 +27,7 @@ function resolveYtDlpPath(logger) {
path.join(process.cwd(), 'bin', 'yt-dlp.exe'),
'yt-dlp.exe'
]
: [
'/usr/local/bin/yt-dlp',
'/usr/bin/yt-dlp',
'/bin/yt-dlp',
path.join(process.cwd(), 'bin', 'yt-dlp'),
'yt-dlp'
];
: ['/usr/local/bin/yt-dlp', '/usr/bin/yt-dlp', '/bin/yt-dlp', path.join(process.cwd(), 'bin', 'yt-dlp'), 'yt-dlp'];
for (const candidate of candidates) {
try {
@@ -64,7 +58,7 @@ async function getDefaultChannelId(category) {
return cachedChannels.defaultId || '1146';
}
return new Promise((resolve) => {
return new Promise(resolve => {
const req = http.request(
{
hostname: '127.0.0.1',
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "peertube-plugin-yaktube-ondemand",
"version": "1.5.0",
"version": "1.5.4",
"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",
@@ -43,7 +43,7 @@
"author": "Enes Yakıştır <[email protected]>",
"repository": {
"type": "git",
"url": "https://github.com/yakistir98/peertube-plugin-yaktube-ondemand.git"
"url": "git+https://github.com/yakistir98/peertube-plugin-yaktube-ondemand.git"
},
"homepage": "https://yaktube.yakhub.com.tr",
"bugs": "https://github.com/yakistir98/peertube-plugin-yaktube-ondemand/issues",
+147 -6
View File
@@ -1151,9 +1151,18 @@ body:has(#yaktube-ondemand-container) my-error-page {
}
@keyframes yaktube-ripple-bounce {
0% { transform: scale(0.7); opacity: 0.5; }
50% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
0% {
transform: scale(0.7);
opacity: 0.5;
}
50% {
transform: scale(1.1);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* -------------------------------------------------------------------------- */
@@ -1285,9 +1294,15 @@ body:has(#yaktube-ondemand-container) my-error-page {
}
@keyframes yaktube-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.04); }
100% { transform: scale(1); }
0% {
transform: scale(1);
}
50% {
transform: scale(1.04);
}
100% {
transform: scale(1);
}
}
.yaktube-qr-wrapper {
@@ -1340,3 +1355,129 @@ body:has(#yaktube-ondemand-container) my-error-page {
outline: none !important;
}
/* ==========================================================================
YAKTUBE ACCESSIBLE PLAYER & WATCH PAGE STYLES (W3C WAI-ARIA)
========================================================================== */
/* 1. Accessible Skip Link (Top of Page) */
.yaktube-skip-link {
position: fixed !important;
top: -120px !important;
left: 16px !important;
background: linear-gradient(135deg, #ff8f37 0%, #ff5e3a 100%) !important;
color: #ffffff !important;
padding: 12px 22px !important;
border-radius: 12px !important;
font-weight: 800 !important;
font-size: 14px !important;
text-decoration: none !important;
box-shadow: 0 8px 30px rgba(255, 143, 55, 0.5) !important;
z-index: 999999 !important;
transition: top 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important;
display: inline-flex !important;
align-items: center !important;
gap: 8px !important;
}
.yaktube-skip-link:focus,
.yaktube-skip-link:focus-visible {
top: 16px !important;
outline: 3px solid #ffffff !important;
outline-offset: 3px !important;
}
/* 2. Accessible Video Player Container Focus States */
#videojs-wrapper:focus,
#videojs-wrapper:focus-visible,
.video-js:focus-visible {
outline: 3px solid #ff8f37 !important;
outline-offset: 4px !important;
box-shadow: 0 0 25px rgba(255, 143, 55, 0.65) !important;
border-radius: 8px !important;
}
/* 3. Accessible Player Action Toolbar */
.yaktube-player-action-bar {
display: flex !important;
align-items: center !important;
flex-wrap: wrap !important;
gap: 8px !important;
margin: 12px 0 18px 0 !important;
padding: 10px 16px !important;
background: rgba(24, 24, 32, 0.95) !important;
border: 1px solid rgba(255, 143, 55, 0.3) !important;
border-radius: 12px !important;
backdrop-filter: blur(10px) !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4) !important;
width: 100% !important;
box-sizing: border-box !important;
}
.yaktube-player-bar-label {
font-size: 13px !important;
font-weight: 700 !important;
color: #ff8f37 !important;
margin-right: 6px !important;
user-select: none !important;
}
.yaktube-player-action-btn {
background: rgba(255, 255, 255, 0.08) !important;
color: #eeeeee !important;
border: 1px solid rgba(255, 255, 255, 0.15) !important;
padding: 7px 14px !important;
border-radius: 8px !important;
font-size: 12.5px !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
display: inline-flex !important;
align-items: center !important;
gap: 5px !important;
}
.yaktube-player-action-btn:hover,
.yaktube-player-action-btn:focus-visible {
background: rgba(255, 143, 55, 0.25) !important;
border-color: #ff8f37 !important;
color: #ffffff !important;
transform: translateY(-1px) !important;
outline: 2px solid #ff8f37 !important;
outline-offset: 2px !important;
}
/* 4. Rate Buttons Distinct Separation (Like & Dislike) */
my-video-rate .video-actions,
my-video-rate .video-actions-rates,
my-video-rate {
display: inline-flex !important;
align-items: center !important;
gap: 8px !important;
}
my-video-rate button.action-button {
margin-right: 6px !important;
border-radius: 8px !important;
transition: all 0.2s ease !important;
}
my-video-rate button.action-button:focus-visible {
outline: 2px solid #ff8f37 !important;
outline-offset: 2px !important;
}
/* 5. Date Toggle Button Clarity */
my-date-toggle .date-toggle {
cursor: pointer !important;
border-radius: 6px !important;
padding: 2px 6px !important;
transition: background 0.2s ease !important;
}
my-date-toggle .date-toggle:hover,
my-date-toggle .date-toggle:focus-visible {
background: rgba(255, 143, 55, 0.15) !important;
color: #ff8f37 !important;
outline: 2px solid #ff8f37 !important;
outline-offset: 1px !important;
}