diff options
| -rw-r--r-- | assets/js/embed.js | 50 | ||||
| -rw-r--r-- | assets/js/player.js | 282 | ||||
| -rw-r--r-- | assets/js/watch.js | 32 | ||||
| -rw-r--r-- | locales/ar.json | 24 | ||||
| -rw-r--r-- | locales/fr.json | 66 | ||||
| -rw-r--r-- | src/invidious/helpers/utils.cr | 2 |
6 files changed, 231 insertions, 225 deletions
diff --git a/assets/js/embed.js b/assets/js/embed.js index 074a9d8d..534c30ff 100644 --- a/assets/js/embed.js +++ b/assets/js/embed.js @@ -69,32 +69,34 @@ function get_playlist(plid, retries) { xhr.send(); } -if (video_data.plid) { - get_playlist(video_data.plid); -} else if (video_data.video_series) { - player.on('ended', function () { - var url = new URL('https://example.com/embed/' + video_data.video_series.shift()); - - if (video_data.params.autoplay || video_data.params.continue_autoplay) { - url.searchParams.set('autoplay', '1'); - } +window.addEventListener('load', function (e) { + if (video_data.plid) { + get_playlist(video_data.plid); + } else if (video_data.video_series) { + player.on('ended', function () { + var url = new URL('https://example.com/embed/' + video_data.video_series.shift()); + + if (video_data.params.autoplay || video_data.params.continue_autoplay) { + url.searchParams.set('autoplay', '1'); + } - if (video_data.params.listen !== video_data.preferences.listen) { - url.searchParams.set('listen', video_data.params.listen); - } + if (video_data.params.listen !== video_data.preferences.listen) { + url.searchParams.set('listen', video_data.params.listen); + } - if (video_data.params.speed !== video_data.preferences.speed) { - url.searchParams.set('speed', video_data.params.speed); - } + if (video_data.params.speed !== video_data.preferences.speed) { + url.searchParams.set('speed', video_data.params.speed); + } - if (video_data.params.local !== video_data.preferences.local) { - url.searchParams.set('local', video_data.params.local); - } + if (video_data.params.local !== video_data.preferences.local) { + url.searchParams.set('local', video_data.params.local); + } - if (video_data.video_series.length !== 0) { - url.searchParams.set('playlist', video_data.video_series.join(',')) - } + if (video_data.video_series.length !== 0) { + url.searchParams.set('playlist', video_data.video_series.join(',')) + } - location.assign(url.pathname + url.search); - }); -} + location.assign(url.pathname + url.search); + }); + } +}); diff --git a/assets/js/player.js b/assets/js/player.js index 0d0ecebd..e58af0cd 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -151,45 +151,47 @@ player.vttThumbnails({ // Enable annotations if (!video_data.params.listen && video_data.params.annotations) { - var video_container = document.getElementById('player'); - let xhr = new XMLHttpRequest(); - xhr.responseType = 'text'; - xhr.timeout = 60000; - xhr.open('GET', '/api/v1/annotations/' + video_data.id, true); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin); - if (!player.paused()) { - player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container }); - } else { - player.one('play', function (event) { + window.addEventListener('load', function (e) { + var video_container = document.getElementById('player'); + let xhr = new XMLHttpRequest(); + xhr.responseType = 'text'; + xhr.timeout = 60000; + xhr.open('GET', '/api/v1/annotations/' + video_data.id, true); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin); + if (!player.paused()) { player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container }); - }); + } else { + player.one('play', function (event) { + player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container }); + }); + } } } } - } - window.addEventListener('__ar_annotation_click', e => { - const { url, target, seconds } = e.detail; - var path = new URL(url); + window.addEventListener('__ar_annotation_click', e => { + const { url, target, seconds } = e.detail; + var path = new URL(url); - if (path.href.startsWith('https://www.youtube.com/watch?') && seconds) { - path.search += '&t=' + seconds; - } + if (path.href.startsWith('https://www.youtube.com/watch?') && seconds) { + path.search += '&t=' + seconds; + } - path = path.pathname + path.search; + path = path.pathname + path.search; - if (target === 'current') { - window.location.href = path; - } else if (target === 'new') { - window.open(path, '_blank'); - } - }); + if (target === 'current') { + window.location.href = path; + } else if (target === 'new') { + window.open(path, '_blank'); + } + }); - xhr.send(); + xhr.send(); + }); } function increase_volume(delta) { @@ -234,25 +236,25 @@ function toggle_play() { } } -const toggle_captions = (function() { +const toggle_captions = (function () { let toggledTrack = null; - const onChange = function(e) { + const onChange = function (e) { toggledTrack = null; }; - const bindChange = function(onOrOff) { + const bindChange = function (onOrOff) { player.textTracks()[onOrOff]('change', onChange); }; // Wrapper function to ignore our own emitted events and only listen // to events emitted by Video.js on click on the captions menu items. - const setMode = function(track, mode) { + const setMode = function (track, mode) { bindChange('off'); track.mode = mode; - window.setTimeout(function() { + window.setTimeout(function () { bindChange('on'); }, 0); }; bindChange('on'); - return function() { + return function () { if (toggledTrack !== null) { if (toggledTrack.mode !== 'showing') { setMode(toggledTrack, 'showing'); @@ -323,95 +325,95 @@ window.addEventListener('keydown', e => { || e.target === document.querySelector('.vjs-tech') || e.target === document.querySelector('.iframeblocker') || e.target === document.querySelector('.vjs-control-bar') - ; + ; let action = null; const code = e.keyCode; const decoratedKey = e.key - + (e.altKey ? '+alt' : '') + + (e.altKey ? '+alt' : '') + (e.ctrlKey ? '+ctrl' : '') + (e.metaKey ? '+meta' : '') - ; + ; switch (decoratedKey) { - case ' ': - case 'k': - action = toggle_play; - break; - - case 'ArrowUp': - if (isPlayerFocused) { - action = increase_volume.bind(this, 0.1); - } - break; - case 'ArrowDown': - if (isPlayerFocused) { - action = increase_volume.bind(this, -0.1); - } - break; - - case 'm': - action = toggle_muted; - break; - - case 'ArrowRight': - action = skip_seconds.bind(this, 5); - break; - case 'ArrowLeft': - action = skip_seconds.bind(this, -5); - break; - case 'l': - action = skip_seconds.bind(this, 10); - break; - case 'j': - action = skip_seconds.bind(this, -10); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - const percent = (code - 48) * 10; - action = set_time_percent.bind(this, percent); - break; - - case 'c': - action = toggle_captions; - break; - case 'f': - action = toggle_fullscreen; - break; - - case 'N': - action = next_video; - break; - case 'P': - // TODO: Add support to play back previous video. - break; - - case '.': - // TODO: Add support for next-frame-stepping. - break; - case ',': - // TODO: Add support for previous-frame-stepping. - break; - - case '>': - action = increase_playback_rate.bind(this, 1); - break; - case '<': - action = increase_playback_rate.bind(this, -1); - break; - - default: - console.info('Unhandled key down event: %s:', decoratedKey, e); - break; + case ' ': + case 'k': + action = toggle_play; + break; + + case 'ArrowUp': + if (isPlayerFocused) { + action = increase_volume.bind(this, 0.1); + } + break; + case 'ArrowDown': + if (isPlayerFocused) { + action = increase_volume.bind(this, -0.1); + } + break; + + case 'm': + action = toggle_muted; + break; + + case 'ArrowRight': + action = skip_seconds.bind(this, 5); + break; + case 'ArrowLeft': + action = skip_seconds.bind(this, -5); + break; + case 'l': + action = skip_seconds.bind(this, 10); + break; + case 'j': + action = skip_seconds.bind(this, -10); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + const percent = (code - 48) * 10; + action = set_time_percent.bind(this, percent); + break; + + case 'c': + action = toggle_captions; + break; + case 'f': + action = toggle_fullscreen; + break; + + case 'N': + action = next_video; + break; + case 'P': + // TODO: Add support to play back previous video. + break; + + case '.': + // TODO: Add support for next-frame-stepping. + break; + case ',': + // TODO: Add support for previous-frame-stepping. + break; + + case '>': + action = increase_playback_rate.bind(this, 1); + break; + case '<': + action = increase_playback_rate.bind(this, -1); + break; + + default: + console.info('Unhandled key down event: %s:', decoratedKey, e); + break; } if (action) { @@ -422,7 +424,7 @@ window.addEventListener('keydown', e => { // Add support for controlling the player volume by scrolling over it. Adapted from // https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L292-L328 -(function() { +(function () { const volumeStep = 0.05; const enableVolumeScroll = true; const enableHoverScroll = true; @@ -432,33 +434,33 @@ window.addEventListener('keydown', e => { var volumeHover = false; var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel'); if (volumeSelector != null) { - volumeSelector.onmouseover = function() { volumeHover = true; }; - volumeSelector.onmouseout = function() { volumeHover = false; }; + volumeSelector.onmouseover = function () { volumeHover = true; }; + volumeSelector.onmouseout = function () { volumeHover = false; }; } var mouseScroll = function mouseScroll(event) { - var activeEl = doc.activeElement; - if (enableHoverScroll) { - // If we leave this undefined then it can match non-existent elements below - activeEl = 0; - } - - // When controls are disabled, hotkeys will be disabled as well - if (player.controls()) { - if (volumeHover) { - if (enableVolumeScroll) { - event = window.event || event; - var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); - event.preventDefault(); - - if (delta == 1) { - increase_volume(volumeStep); - } else if (delta == -1) { - increase_volume(-volumeStep); + var activeEl = doc.activeElement; + if (enableHoverScroll) { + // If we leave this undefined then it can match non-existent elements below + activeEl = 0; + } + + // When controls are disabled, hotkeys will be disabled as well + if (player.controls()) { + if (volumeHover) { + if (enableVolumeScroll) { + event = window.event || event; + var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); + event.preventDefault(); + + if (delta == 1) { + increase_volume(volumeStep); + } else if (delta == -1) { + increase_volume(-volumeStep); + } + } } - } } - } }; player.on('mousewheel', mouseScroll); diff --git a/assets/js/watch.js b/assets/js/watch.js index 80cb1769..a26cb505 100644 --- a/assets/js/watch.js +++ b/assets/js/watch.js @@ -439,19 +439,21 @@ if (video_data.play_next) { }); } -if (video_data.plid) { - get_playlist(video_data.plid); -} +window.addEventListener('load', function (e) { + if (video_data.plid) { + get_playlist(video_data.plid); + } -if (video_data.params.comments[0] === 'youtube') { - get_youtube_comments(); -} else if (video_data.params.comments[0] === 'reddit') { - get_reddit_comments(); -} else if (video_data.params.comments[1] === 'youtube') { - get_youtube_comments(); -} else if (video_data.params.comments[1] === 'reddit') { - get_reddit_comments(); -} else { - comments = document.getElementById('comments'); - comments.innerHTML = ''; -} + if (video_data.params.comments[0] === 'youtube') { + get_youtube_comments(); + } else if (video_data.params.comments[0] === 'reddit') { + get_reddit_comments(); + } else if (video_data.params.comments[1] === 'youtube') { + get_youtube_comments(); + } else if (video_data.params.comments[1] === 'reddit') { + get_reddit_comments(); + } else { + comments = document.getElementById('comments'); + comments.innerHTML = ''; + } +}); diff --git a/locales/ar.json b/locales/ar.json index 4c717435..c580a2d5 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -1,7 +1,7 @@ { "`x` subscribers": "`x` المشتركين", "`x` videos": "`x` الفيديوهات", - "`x` playlists": "", + "`x` playlists": "`x` قوائم التشغيل", "LIVE": "مباشر", "Shared `x` ago": "تم رفع الفيديو منذ `x`", "Unsubscribe": "إلغاء الإشتراك", @@ -127,17 +127,17 @@ "View JavaScript license information.": "مشاهدة معلومات حول تراخيص الجافاسكريبت.", "View privacy policy.": "عرض سياسة الخصوصية.", "Trending": "الشائع", - "Public": "", + "Public": "عام", "Unlisted": "غير مصنف", - "Private": "", - "View all playlists": "", - "Updated `x` ago": "", - "Delete playlist `x`?": "", - "Delete playlist": "", - "Create playlist": "", - "Title": "", - "Playlist privacy": "", - "Editing playlist `x`": "", + "Private": "خاص", + "View all playlists": "عرض جميع قوائم التشغيل", + "Updated `x` ago": "تم تحديثه منذ `x`", + "Delete playlist `x`?": "حذف قائمه التشغيل `x` ?", + "Delete playlist": "حذف قائمه التغشيل", + "Create playlist": "إنشاء قائمه تشغيل", + "Title": "العنوان", + "Playlist privacy": "إعدادات الخصوصيه", + "Editing playlist `x`": "تعديل قائمه التشفيل `x`", "Watch on YouTube": "مشاهدة الفيديو على اليوتيوب", "Hide annotations": "إخفاء الملاحظات فى الفيديو", "Show annotations": "عرض الملاحظات فى الفيديو", @@ -333,4 +333,4 @@ "Playlists": "قوائم التشغيل", "Community": "المجتمع", "Current version: ": "الإصدار الحالي: " -}
\ No newline at end of file +} diff --git a/locales/fr.json b/locales/fr.json index de80cae8..928904c1 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,7 +1,7 @@ { "`x` subscribers": "`x` abonnés", "`x` videos": "`x` vidéos", - "`x` playlists": "", + "`x` playlists": "`x` listes de lecture", "LIVE": "EN DIRECT", "Shared `x` ago": "Ajoutée il y a `x`", "Unsubscribe": "Se désabonner", @@ -53,9 +53,9 @@ "Preferences": "Préférences", "Player preferences": "Préférences du lecteur", "Always loop: ": "Lire en boucle : ", - "Autoplay: ": "Lecture automatique : ", + "Autoplay: ": "Lancer la lecture automatiquement : ", "Play next by default: ": "Lire les vidéos suivantes par défaut : ", - "Autoplay next video: ": "Lecture automatique pour la vidéo suivante : ", + "Autoplay next video: ": "Lancer la lecture automatiquement pour la vidéo suivant la vidéo regardée : ", "Listen by default: ": "Audio uniquement : ", "Proxy videos: ": "Charger les vidéos à travers un proxy : ", "Default speed: ": "Vitesse par défaut : ", @@ -70,11 +70,11 @@ "Show annotations by default: ": "Afficher les annotations par défaut : ", "Visual preferences": "Préférences du site", "Player style: ": "Style du lecteur : ", - "Dark mode: ": "Mode Sombre : ", + "Dark mode: ": "Mode sombre : ", "Theme: ": "Thème : ", "dark": "sombre", "light": "clair", - "Thin mode: ": "Mode Simplifié : ", + "Thin mode: ": "Mode léger : ", "Subscription preferences": "Préférences de la page d'abonnements", "Show annotations by default for subscribed channels: ": "Afficher les annotations par défaut sur les chaînes auxquelles vous êtes abonnés : ", "Redirect homepage to feed: ": "Rediriger la page d'accueil vers la page d'abonnements : ", @@ -86,12 +86,12 @@ "alphabetically - reverse": "alphabétiquement - inversé", "channel name": "nom de la chaîne", "channel name - reverse": "nom de la chaîne - inversé", - "Only show latest video from channel: ": "Afficher uniquement la dernière vidéo des chaîne auxquelles vous êtes abonnés : ", - "Only show latest unwatched video from channel: ": "Afficher uniquement la dernière vidéo des chaîne auxquelles vous êtes abonnés que n'a pas était regardée : ", + "Only show latest video from channel: ": "Afficher uniquement la dernière vidéo des chaînes auxquelles vous êtes abonnés : ", + "Only show latest unwatched video from channel: ": "Afficher uniquement la dernière vidéo des chaînes auxquelles vous êtes abonnés qui n'a pas était regardée : ", "Only show unwatched: ": "Afficher uniquement les vidéos qui n'ont pas étaient regardées : ", "Only show notifications (if there are any): ": "Afficher uniquement les notifications (s'il y en a) : ", "Enable web notifications": "Activer les notifications web", - "`x` uploaded a video": "`x` a partagé(e) une video", + "`x` uploaded a video": "`x` a partagé(e) une vidéo", "`x` is live": "`x` est en direct", "Data preferences": "Préférences liées aux données", "Clear watch history": "Supprimer l'historique des vidéos regardées", @@ -101,7 +101,7 @@ "Manage tokens": "Gérer les tokens", "Watch history": "Historique de visionnage", "Delete account": "Supprimer votre compte", - "Administrator preferences": "Préferences d'Administrateur", + "Administrator preferences": "Préferences d'Administration", "Default homepage: ": "Page d'accueil par défaut : ", "Feed menu: ": "Préferences des abonnements : ", "Top enabled: ": "Top activé : ", @@ -123,29 +123,29 @@ "search": "rechercher", "Log out": "Déconnexion", "Released under the AGPLv3 by Omar Roth.": "Publié sous licence AGPLv3 par Omar Roth.", - "Source available here.": "Code Source disponible ici.", + "Source available here.": "Code source disponible ici.", "View JavaScript license information.": "Informations des licences JavaScript.", "View privacy policy.": "Politique de confidentialité.", "Trending": "Tendances", - "Public": "", + "Public": "Publique", "Unlisted": "Non répertoriée", - "Private": "", - "View all playlists": "", - "Updated `x` ago": "", - "Delete playlist `x`?": "", - "Delete playlist": "", - "Create playlist": "", - "Title": "", - "Playlist privacy": "", - "Editing playlist `x`": "", + "Private": "Privée", + "View all playlists": "Voir toutes vos playlists", + "Updated `x` ago": "Dernière mise à jour il y a `x`", + "Delete playlist `x`?": "Êtes-vous sûr de vouloir supprimer la liste de lecture ?", + "Delete playlist": "Supprimer la liste de lecture", + "Create playlist": "Créer une liste de lecture", + "Title": "Titre", + "Playlist privacy": "Paramètres de confidentialité de la liste de lecture", + "Editing playlist `x`": "Liste de lecture modifier le `x`", "Watch on YouTube": "Voir la vidéo sur Youtube", "Hide annotations": "Masquer les annotations", "Show annotations": "Afficher les annotations", "Genre: ": "Genre : ", "License: ": "Licence : ", - "Family friendly? ": "Tout Public ? ", + "Family friendly? ": "Vidéo tout public ? ", "Wilson score: ": "Score de Wilson : ", - "Engagement: ": "Poucentage de spectateur aillant Like ou Dislike la vidéo : ", + "Engagement: ": "Pourcentage de spectateur aillant appuyé sur \"J'aime\" ou \"J'aime Pas\" : ", "Whitelisted regions: ": "Régions sur liste blanche : ", "Blacklisted regions: ": "Régions sur liste noire : ", "Shared `x`": "Ajoutée le `x`", @@ -160,7 +160,7 @@ "Hide replies": "Masquer les réponses", "Show replies": "Afficher les réponses", "Incorrect password": "Mot de passe incorrect", - "Quota exceeded, try again in a few hours": "Nombre de tentative de connexion dépassé, réessayez dans quelques heures", + "Quota exceeded, try again in a few hours": "Nombre de tentative de connexion dépassée, réessayez dans quelques heures", "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Impossible de se connecter, si après plusieurs tentative vous ne parvenez toujours pas à vous connecter, assurez-vous que l'authentification à deux facteurs (Authenticator ou SMS) est activée.", "Invalid TFA code": "Code d'authentification à deux facteurs invalide", "Login failed. This may be because two-factor authentication is not turned on for your account.": "La connexion a échoué. Cela peut être dû au fait que l'authentification à deux facteurs n'est pas activée sur votre compte.", @@ -182,17 +182,17 @@ "Could not fetch comments": "Impossible de charger les commentaires", "View `x` replies": "Voir `x` réponses", "`x` ago": "il y a `x`", - "Load more": "Charger plus", + "Load more": "Voir plus", "`x` points": "`x` points", "Could not create mix.": "Impossible de charger cette liste de lecture.", - "Empty playlist": "Liste de lecture vide", - "Not a playlist.": "Liste de lecture invalide.", + "Empty playlist": "La liste de lecture est vide", + "Not a playlist.": "La liste de lecture est invalide.", "Playlist does not exist.": "La liste de lecture n'existe pas.", "Could not pull trending pages.": "Impossible de charger les pages de tendances.", "Hidden field \"challenge\" is a required field": "Le champ masqué \"challenge\" est un champ obligatoire", "Hidden field \"token\" is a required field": "Le champ caché \"token\" est requis", - "Erroneous challenge": "Challenge Erroné", - "Erroneous token": "Token Erroné", + "Erroneous challenge": "Challenge invalide", + "Erroneous token": "Token invalide", "No such user": "Cet utilisateur n'existe pas", "Token is expired, please try again": "Le token est expiré, veuillez réessayer", "English": "Anglais", @@ -325,12 +325,12 @@ "%A %B %-d, %Y": "%A %-d %B %Y", "(edited)": "(modifié)", "YouTube comment permalink": "Lien permanent vers le commentaire sur YouTube", - "permalink": "permalien", + "permalink": "Lien permanent", "`x` marked it with a ❤": "`x` l'a marqué d'un ❤", - "Audio mode": "Mode Audio", - "Video mode": "Mode Vidéo", + "Audio mode": "Mode audio", + "Video mode": "Mode vidéo", "Videos": "Vidéos", - "Playlists": "Liste de lecture", + "Playlists": "Listes de lecture", "Community": "Communauté", "Current version: ": "Version actuelle : " -}
\ No newline at end of file +} diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index ed55dc9c..117a5dbe 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -20,7 +20,7 @@ end def make_client(url : URI, region = nil) client = HTTPClient.new(url) - client.family = CONFIG.force_resolve + client.family = (url.host == "www.youtube.com") ? CONFIG.force_resolve : Socket::Family::UNSPEC client.read_timeout = 15.seconds client.connect_timeout = 15.seconds |
