123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- // ==UserScript==
- // @name Invidious Keyboard Browse
- // @version 1
- // @grant none
- // @include https://yewtu.be/channel/*
- // @include https://yewtu.be/watch*
- // @include https://yewtu.be/search*
- // @include https://yewtu.be/feed/*
- // @include https://vid.puffyan.us/channel/*
- // @include https://vid.puffyan.us/watch*
- // @include https://vid.puffyan.us/search*
- // @include https://vid.puffyan.us/feed/*
- // @include https://inv.riverside.rocks/channel/*
- // @include https://inv.riverside.rocks/watch*
- // @include https://inv.riverside.rocks/search*
- // @include https://inv.riverside.rocks/feed/*
- // @include https://invidious.kavin.rocks/channel/*
- // @include https://invidious.kavin.rocks/watch*
- // @include https://invidious.kavin.rocks/search*
- // @include https://invidious.kavin.rocks/feed/*
- // @include https://invidio.xamh.de/channel/*
- // @include https://invidio.xamh.de/watch*
- // @include https://invidio.xamh.de/search*
- // @include https://invidio.xamh.de/feed/*
- // @include https://y.com.sb/channel/*
- // @include https://y.com.sb/watch*
- // @include https://y.com.sb/search*
- // @include https://y.com.sb/feed/*
- // @include https://invidious.nerdvpn.de/channel/*
- // @include https://invidious.nerdvpn.de/watch*
- // @include https://invidious.nerdvpn.de/search*
- // @include https://invidious.nerdvpn.de/feed/*
- // @include https://yt.artemislena.eu/channel/*
- // @include https://yt.artemislena.eu/watch*
- // @include https://yt.artemislena.eu/search*
- // @include https://yt.artemislena.eu/feed/*
- // @include https://invidious.tiekoetter.com/channel/*
- // @include https://invidious.tiekoetter.com/watch*
- // @include https://invidious.tiekoetter.com/search*
- // @include https://invidious.tiekoetter.com/feed/*
- // @include https://invidious.flokinet.to/channel/*
- // @include https://invidious.flokinet.to/watch*
- // @include https://invidious.flokinet.to/search*
- // @include https://invidious.flokinet.to/feed/*
- // @include https://inv.bp.projectsegfau.lt/channel/*
- // @include https://inv.bp.projectsegfau.lt/watch*
- // @include https://inv.bp.projectsegfau.lt/search*
- // @include https://inv.bp.projectsegfau.lt/feed/*
- // @include https://inv.vern.cc/channel/*
- // @include https://inv.vern.cc/watch*
- // @include https://inv.vern.cc/search*
- // @include https://inv.vern.cc/feed/*
- // @include https://inv.odyssey346.dev/channel/*
- // @include https://inv.odyssey346.dev/watch*
- // @include https://inv.odyssey346.dev/search*
- // @include https://inv.odyssey346.dev/feed/*
- // @include https://invidious.snopyta.org/channel/*
- // @include https://invidious.snopyta.org/watch*
- // @include https://invidious.snopyta.org/search*
- // @include https://invidious.snopyta.org/feed/*
- // @include https://invidious.baczek.me/channel/*
- // @include https://invidious.baczek.me/watch*
- // @include https://invidious.baczek.me/search*
- // @include https://invidious.baczek.me/feed/*
- // @include https://invidious.sethforprivacy.com/channel/*
- // @include https://invidious.sethforprivacy.com/watch*
- // @include https://invidious.sethforprivacy.com/search*
- // @include https://invidious.sethforprivacy.com/feed/*
- // @include https://yt.funami.tech/channel/*
- // @include https://yt.funami.tech/watch*
- // @include https://yt.funami.tech/search*
- // @include https://yt.funami.tech/feed/*
- // @include https://invidious.drivet.xyz/channel/*
- // @include https://invidious.drivet.xyz/watch*
- // @include https://invidious.drivet.xyz/search*
- // @include https://invidious.drivet.xyz/feed/*
- // @include https://vid.priv.au/channel/*
- // @include https://vid.priv.au/watch*
- // @include https://vid.priv.au/search*
- // @include https://vid.priv.au/feed/*
- // @include https://invidious.silur.me/channel/*
- // @include https://invidious.silur.me/watch*
- // @include https://invidious.silur.me/search*
- // @include https://invidious.silur.me/feed/*
- // @include https://invidious.epicsite.xyz/channel/*
- // @include https://invidious.epicsite.xyz/watch*
- // @include https://invidious.epicsite.xyz/search*
- // @include https://invidious.epicsite.xyz/feed/*
- // @include https://invidious.slipfox.xyz/channel/*
- // @include https://invidious.slipfox.xyz/watch*
- // @include https://invidious.slipfox.xyz/search*
- // @include https://invidious.slipfox.xyz/feed/*
- // @include https://iv.ggtyler.dev/channel/*
- // @include https://iv.ggtyler.dev/watch*
- // @include https://iv.ggtyler.dev/search*
- // @include https://iv.ggtyler.dev/feed/*
- // @include https://invidious.dhusch.de/channel/*
- // @include https://invidious.dhusch.de/watch*
- // @include https://invidious.dhusch.de/search*
- // @include https://invidious.dhusch.de/feed/*
- // @include https://invidious.weblibre.org/channel/*
- // @include https://invidious.weblibre.org/watch*
- // @include https://invidious.weblibre.org/search*
- // @include https://invidious.weblibre.org/feed/*
- // @include https://invidious.esmailelbob.xyz/channel/*
- // @include https://invidious.esmailelbob.xyz/watch*
- // @include https://invidious.esmailelbob.xyz/search*
- // @include https://invidious.esmailelbob.xyz/feed/*
- // @include https://iv.melmac.space/channel/*
- // @include https://iv.melmac.space/watch*
- // @include https://iv.melmac.space/search*
- // @include https://iv.melmac.space/feed/*
- // @include https://invidious.privacydev.net/channel/*
- // @include https://invidious.privacydev.net/watch*
- // @include https://invidious.privacydev.net/search*
- // @include https://invidious.privacydev.net/feed/*
- // @include https://invidious.lidarshield.cloud/channel/*
- // @include https://invidious.lidarshield.cloud/watch*
- // @include https://invidious.lidarshield.cloud/search*
- // @include https://invidious.lidarshield.cloud/feed/*
- // @include https://invidious.namazso.eu/channel/*
- // @include https://invidious.namazso.eu/watch*
- // @include https://invidious.namazso.eu/search*
- // @include https://invidious.namazso.eu/feed/*
- // ==/UserScript==
- /* Browse video's on Invidious with the keyboard
- *
- * Shortcuts:
- * SHIFT + arrow-up Go up a video
- * SHIFT + arrow-down Go down a video
- * SHIFT + arrow-left Go one video to the left
- * SHIFT + arrow-right Go one video to the right
- * ENTER Follow link of selected video
- * ESCAPE De-select selected video
- *
- * When no video is selected yet it will select the first visible video, when
- * no video is visible it will select the first video. (On usage of
- * SHIFT + arrow)*/
- const KEY_UP = 38;
- const KEY_RIGHT = 39;
- const KEY_DOWN = 40;
- const KEY_LEFT = 37;
- const KEY_ENTER = 13;
- const KEY_ESC = 27;
- const UP = 1;
- const RIGHT = 2;
- const DOWN = 3;
- const LEFT = 4;
- var SELECTED_ELEM = null;
- function itemIsVisible(item) {
- if ((item.offsetTop - window.pageYOffset) < 0 || (item.offsetTop + item.clientHeight) > (window.innerHeight + window.pageYOffset)) {
- return false;
- }
- return true;
- }
- function* getWantedElements() {
- const thumbnailElems = document.getElementsByClassName("thumbnail");
- for (const item of thumbnailElems) {
- if (item.parentNode.tagName !== "A") {
- continue;
- }
- yield item;
- }
- return [];
- }
- function* getVisibleElements() {
- for (const item of getWantedElements()) {
- if (itemIsVisible(item) === false) {
- continue;
- }
- yield item;
- }
- }
- function* getRowElements() {
- curOffsetTop = SELECTED_ELEM.offsetTop;
- for (const item of getVisibleElements()) {
- if (item.offsetTop !== curOffsetTop) {
- continue;
- }
- yield item;
- }
- }
- function* getColumnElements() {
- currOffsetLeft = SELECTED_ELEM.offsetLeft;
- for (const item of getWantedElements()) {
- if (item.offsetLeft !== currOffsetLeft) {
- continue;
- }
- yield item;
- }
- }
- function getDownElement() {
- currOffsetTop = SELECTED_ELEM.offsetTop;
- for (const item of getColumnElements()) {
- if (item.offsetTop > currOffsetTop) {
- return item;
- }
- }
- return null;
- }
- function getUpElement() {
- prevItem = null;
- for (const item of getColumnElements()) {
- if (item === SELECTED_ELEM) {
- return prevItem;
- }
- prevItem = item;
- }
- return null;
- }
- function getRightElement() {
- curOffsetLeft = SELECTED_ELEM.offsetLeft;
- for (const item of getRowElements()) {
- if (item.offsetLeft > curOffsetLeft) {
- return item;
- }
- }
- return null;
- }
- function getLeftElement() {
- prevItem = null;
- for (const item of getRowElements()) {
- if (item === SELECTED_ELEM) {
- return prevItem;
- }
- prevItem = item;
- }
- return null;
- }
- function setSelected(item) {
- item.focus();
- item.style.border = "2px solid red";
- if (itemIsVisible(item) == false) {
- item.scrollIntoView();
- }
- SELECTED_ELEM = item;
- }
- function clearSelection() {
- SELECTED_ELEM.style.border = "none";
- SELECTED_ELEM = null;
- }
- function mayProcess() {
- let activeElem = document.activeElement;
- if (activeElem !== document.body) {
- if (activeElem.tagName === "INPUT") {
- return false;
- }
- if (activeElem.tagName === "VIDEO") {
- return false;
- }
- }
- return true;
- }
- window.onkeydown = function keydown(event) {
- let keyAction = null;
- if (mayProcess() === false) {
- return;
- }
- // remove potential selection
- document.getSelection().removeAllRanges();
- if (event.keyCode === KEY_ESC) { // escape
- if (SELECTED_ELEM !== null) {
- clearSelection();
- }
- return;
- }
- else if (event.keyCode === KEY_ENTER) { // enter
- if (SELECTED_ELEM === null) {
- return;
- }
- const item = SELECTED_ELEM;
- clearSelection();
- // Make sure document.body is the active element, else it may follow the
- // active element.
- if (document.activeElement !== document.body) {
- document.activeElement.blur();
- }
- item.parentNode.click();
- return;
- }
- // SHIFT is our modifier key
- if (event.shiftKey === false) {
- return;
- }
- if (event.keyCode === KEY_LEFT) { // arrow-left
- keyAction = LEFT;
- }
- else if (event.keyCode === KEY_UP) { // arrow-up
- keyAction = UP;
- }
- else if (event.keyCode === KEY_RIGHT) { // arrow-right
- keyAction = RIGHT;
- }
- else if (event.keyCode === KEY_DOWN) { // arrow-down
- keyAction = DOWN;
- }
- else {
- return;
- }
- if (SELECTED_ELEM !== null) {
- if (itemIsVisible(SELECTED_ELEM) === false) {
- // clear previous selected
- clearSelection();
- }
- }
- if (SELECTED_ELEM === null) {
- // None selected yet, no matter the action, just set the first
- for (const item of getVisibleElements()) {
- setSelected(item);
- return;
- }
- // No items visible, select first item
- for (const item of getWantedElements()) {
- setSelected(item);
- break;
- }
- return;
- }
- if (keyAction === RIGHT) {
- let rightItem = getRightElement();
- if (rightItem !== null) {
- clearSelection();
- setSelected(rightItem);
- }
- }
- else if (keyAction === LEFT) {
- let item = getLeftElement();
- if (item !== null) {
- clearSelection();
- setSelected(item);
- }
- }
- else if (keyAction === DOWN) {
- let item = getDownElement();
- if (item !== null) {
- clearSelection();
- setSelected(item);
- }
- }
- else if (keyAction === UP) {
- let item = getUpElement();
- if (item !== null) {
- clearSelection();
- setSelected(item);
- }
- }
- }
|