diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..519e390 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +.vscode +*~ +\#*# diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..2ae2317 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +loglevel=silent diff --git a/build/index.html b/build/index.html new file mode 100644 index 0000000..5738ee8 --- /dev/null +++ b/build/index.html @@ -0,0 +1,9 @@ +Select instance - Invidious

As of September 1st 2020, invidio.us has closed down.

To see this content, please select another instance, +or watch directly on YouTube.

DomainHealthAction
Loading instances...
\ No newline at end of file diff --git a/build/static/css/main.css b/build/static/css/main.css new file mode 100644 index 0000000..a38b263 --- /dev/null +++ b/build/static/css/main.css @@ -0,0 +1 @@ +:root{--bg-primary: #fcf5f4;--bg-secondary: #e9e1df;--bg-tertiary: #cec8c6;--fg-header: #000;--fg-primary: #181818;--fg-dim: #646464;--fg-link: #2664b5;--edge-mark: #aaa;--edge-table: #333}@media(prefers-color-scheme: dark){:root{--bg-primary: #232323;--bg-secondary: #191919;--bg-tertiary: #383838;--fg-header: #fff;--fg-primary: #d9d9d9;--fg-dim: #828282;--fg-link: #529ef5;--edge-mark: #555;--edge-table: #aaa}}body{background-color:var(--bg-primary);color:var(--fg-primary);font-size:20px;margin:0;padding:8px;font-family:sans-serif}a,a:visited{color:var(--fg-link)}noscript{display:block}mark{background-color:var(--bg-tertiary);color:var(--fg-primary);padding:3px 5px;border-radius:4px;border:1px solid var(--edge-mark)}table,td,th{border:1px solid var(--edge-table);border-collapse:collapse}td,th{padding:4px 8px}thead,tr:nth-child(even){background-color:var(--bg-secondary)}.banner{display:grid;grid-gap:24px;align-items:center;justify-items:center;justify-content:center;padding:20px}@media screen and (min-width: 520px){.banner{grid-template-columns:80px auto}}.banner .logo{width:80px;height:80px;background-size:contain;background-image:url(/static/img/invidious-logo-light.svg?static=25c5cd3acc)}@media(prefers-color-scheme: dark){.banner .logo{background-image:url(/static/img/invidious-logo-dark.svg?static=aa528cbad6)}}.banner h1{color:var(--fg-header);font-size:56px;font-weight:bold;text-transform:uppercase;margin:0;padding:0}.story{margin:40px 0px;text-align:center}@media screen and (min-width: 520px){.story{white-space:pre-line}}.instances-table{display:flex;justify-content:center}.instances-table table{width:100%;max-width:700px}.instances-list{text-align:center}.instances-list .list{margin:0 auto;padding-left:1em;text-align:left;max-width:max-content}.loading-td{text-align:center;padding:20px;background-color:var(--bg-secondary)}.column-center{text-align:center}.health-unknown{color:var(--fg-dim)}.script-warning{margin:0 auto;max-width:max-content;background:#700;color:#fff;padding:4px 20px;border-radius:8px;border:1px solid} \ No newline at end of file diff --git a/build/static/img/invidious-logo-dark.svg b/build/static/img/invidious-logo-dark.svg new file mode 100644 index 0000000..a0542d6 --- /dev/null +++ b/build/static/img/invidious-logo-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/build/static/img/invidious-logo-light.svg b/build/static/img/invidious-logo-light.svg new file mode 100644 index 0000000..d1c7c19 --- /dev/null +++ b/build/static/img/invidious-logo-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/build/static/js/main.js b/build/static/js/main.js new file mode 100644 index 0000000..c7f95e7 --- /dev/null +++ b/build/static/js/main.js @@ -0,0 +1,45 @@ +"use strict"; + +;(() => { + const q = s => document.querySelector(s) + const qa = s => document.querySelectorAll(s) + + function createElement(tag, properties = {}, children = []) { + const e = document.createElement(tag) + for (const key of Object.keys(properties)) { + e[key] = properties[key] + } + for (const child of children) { + e.appendChild(child) + } + return e + } + + const destinationPath = window.location.href.slice(window.location.origin.length) + + q("#watch-on-youtube").href = "https://www.youtube.com" + destinationPath + + fetch("https://instances.invidio.us/instances.json?pretty=1&sort_by=type,health").then(res => res.json()).then( + /** @param {[string, {monitor: any, flag: string, region: string, stats: any, type: string, uri: string}][]} root */ root => { + console.log(root) + root.filter(entry => entry[1].type === "https").forEach(entry => { + let healthUnknown = "health-unknown " + let health = "(unknown)" + if (entry[1].monitor && entry[1].monitor.dailyRatios && entry[1].monitor.dailyRatios[0]) { + health = entry[1].monitor.dailyRatios[0].ratio + healthUnknown = "" + } + let target = entry[1].uri.replace(/\/*$/, "") + destinationPath + q("#instances-tbody").appendChild( + createElement("tr", {}, [ + createElement("td", {textContent: entry[0]}), + createElement("td", {className: "column-center "+healthUnknown, textContent: health}), + createElement("td", {className: "column-center"}, [ + createElement("a", {href: target, textContent: "Go →"}) + ]) + ]) + ) + }) + qa(".loading").forEach(e => e.remove()) + }) +})() diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..834a9ea --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "checkJs": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f202149 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,574 @@ +{ + "name": "invidious-redirect", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/parser": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", + "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" + }, + "@babel/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "requires": { + "@babel/types": "^7.9.6" + } + }, + "basic-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "requires": { + "is-regex": "^1.0.3" + } + }, + "chokidar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", + "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "requires": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "ecstatic": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", + "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", + "dev": true, + "requires": { + "he": "^1.1.1", + "mime": "^1.6.0", + "minimist": "^1.1.0", + "url-join": "^2.0.5" + } + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "follow-redirects": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-server": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", + "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", + "dev": true, + "requires": { + "basic-auth": "^1.0.3", + "colors": "^1.4.0", + "corser": "^2.0.1", + "ecstatic": "^3.3.2", + "http-proxy": "^1.18.0", + "minimist": "^1.2.5", + "opener": "^1.5.1", + "portfinder": "^1.0.25", + "secure-compare": "3.0.1", + "union": "~0.5.0" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "requires": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + } + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "pug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.0.tgz", + "integrity": "sha512-inmsJyFBSHZaiGLaguoFgJGViX0If6AcfcElimvwj9perqjDpUpw79UIEDZbWFmoGVidh08aoE+e8tVkjVJPCw==", + "requires": { + "pug-code-gen": "^3.0.0", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.0", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.0", + "pug-strip-comments": "^2.0.0" + } + }, + "pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "requires": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "pug-code-gen": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.1.tgz", + "integrity": "sha512-xJIGvmXTQlkJllq6hqxxjRWcay2F9CU69TuAuiVZgHK0afOhG5txrQOcZyaPHBvSWCU/QQOqEp5XCH94rRZpBQ==", + "requires": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + }, + "pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "requires": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "pug-lexer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.0.tgz", + "integrity": "sha512-52xMk8nNpuyQ/M2wjZBN5gXQLIylaGkAoTk5Y1pBhVqaopaoj8Z0iVzpbFZAqitL4RHNVDZRnJDsqEYe99Ti0A==", + "requires": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "requires": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "requires": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "requires": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "pug-runtime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.0.tgz", + "integrity": "sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA==" + }, + "pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "requires": { + "pug-error": "^2.0.0" + } + }, + "pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "sass": { + "version": "1.26.10", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz", + "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==", + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" + }, + "union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "requires": { + "qs": "^6.4.0" + } + }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" + }, + "with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "requires": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a7a676 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "invidious-redirect", + "version": "1.0.0", + "description": "", + "main": "build.js", + "scripts": { + "build": "node src/build.js", + "watch": "fish -c 'while true; inotifywait (find src -type f) -e close_write -q; npm run build; end'" + }, + "keywords": [], + "author": "", + "license": "AGPL-3.0-only", + "dependencies": { + "pug": "^3.0.0", + "sass": "^1.26.10" + }, + "devDependencies": { + "http-server": "^0.12.3" + } +} diff --git a/src/assets/img/invidious-logo-dark.svg b/src/assets/img/invidious-logo-dark.svg new file mode 100644 index 0000000..a0542d6 --- /dev/null +++ b/src/assets/img/invidious-logo-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/img/invidious-logo-light.svg b/src/assets/img/invidious-logo-light.svg new file mode 100644 index 0000000..d1c7c19 --- /dev/null +++ b/src/assets/img/invidious-logo-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/build.js b/src/build.js new file mode 100644 index 0000000..8a5c48b --- /dev/null +++ b/src/build.js @@ -0,0 +1,58 @@ +process.chdir(__dirname) + +const pug = require("pug") +const sass = require("sass") +const fs = require("fs").promises +const crypto = require("crypto") +const pj = require("path").join + +const buildDir = "../build" + +function hash(buffer) { + return crypto.createHash("sha256").update(buffer).digest("hex").slice(0, 10) +} + +const static = new Map() +const pugLocals = {static} + +async function addFile(sourcePath, targetPath) { + const contents = await fs.readFile(pj(".", sourcePath), {encoding: null}) + static.set(sourcePath, `${targetPath}?static=${hash(contents)}`) + fs.writeFile(pj(buildDir, targetPath), contents) +} + +async function addSass(sourcePath, targetPath) { + const renderedCSS = sass.renderSync({ + file: pj(".", sourcePath), + outputStyle: "compressed", + functions: { + "static($name)": function(name) { + if (!(name instanceof sass.types.String)) { + throw "$name: expected a string" + } + return new sass.types.String(static.get(name.getValue())) + } + } + }).css + static.set(sourcePath, `${targetPath}?static=${hash(renderedCSS)}`) + await fs.writeFile(pj(buildDir, targetPath), renderedCSS) +} + +async function addPug(sourcePath, targetPath) { + const renderedHTML = pug.compileFile(pj(".", sourcePath))(pugLocals) + await fs.writeFile(pj(buildDir, targetPath), renderedHTML) +} + +;(async () => { + await Promise.all([ + addFile("/assets/img/invidious-logo-dark.svg", "/static/img/invidious-logo-dark.svg"), + addFile("/assets/img/invidious-logo-light.svg", "/static/img/invidious-logo-light.svg"), + addFile("/main.js", "/static/js/main.js") + ]) + + addSass("/main.sass", "/static/css/main.css") + + addPug("/index.pug", "/index.html") + + console.log("Build complete.") +})() diff --git a/src/index.pug b/src/index.pug new file mode 100644 index 0000000..f8257ff --- /dev/null +++ b/src/index.pug @@ -0,0 +1,68 @@ +//- Needs static + +doctype html +html(lang="en") + head + title Select instance - Invidious + meta(charset="utf-8") + meta(name="viewport" content="width=device-width, initial-scale=1") + link(rel="stylesheet" type="text/css" href=static.get("/main.sass")) + script(src=static.get("/main.js") text="text/javascript" defer) + body + noscript + style. + .requires-scripts { + display: none; + } + script(type="text/javascript"). + document.body.classList.remove("yesscript") + + header.banner + .logo + h1 Invidious + + noscript + .script-warning + p. + You are seeing the fallback version of this page.#[br] + Some features are missing. + p. + JavaScript is required to use the regular page. + + .story + p. + As of September 1st 2020, #[mark invidio.us] has closed down. + .requires-scripts + p. + To see this content, please select another instance, + or #[a#watch-on-youtube watch directly on YouTube.] + noscript + p. + To see this content, please select another instance, + or #[a(href="https://www.youtube.com") go to YouTube] and find it yourself. + + .instances-table.requires-scripts + table + thead + tr + th(scope="col") Domain + th(scope="col") Health + th(scope="col") Action + tbody#instances-tbody + tr.loading + td(colspan="3").loading-td Loading instances... + noscript + .instances-list + h2 Available instances + ul.list + - + const instances = [ + "invidious.snopyta.org", + "yewtu.be", + "watch.nettohikari.com", + "invidious.13ad.de", + "vid.mint.lgbt", + "invidiou.site" + ] + for instance in instances + li: a(href=`https://${instance}`)= instance diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..c7f95e7 --- /dev/null +++ b/src/main.js @@ -0,0 +1,45 @@ +"use strict"; + +;(() => { + const q = s => document.querySelector(s) + const qa = s => document.querySelectorAll(s) + + function createElement(tag, properties = {}, children = []) { + const e = document.createElement(tag) + for (const key of Object.keys(properties)) { + e[key] = properties[key] + } + for (const child of children) { + e.appendChild(child) + } + return e + } + + const destinationPath = window.location.href.slice(window.location.origin.length) + + q("#watch-on-youtube").href = "https://www.youtube.com" + destinationPath + + fetch("https://instances.invidio.us/instances.json?pretty=1&sort_by=type,health").then(res => res.json()).then( + /** @param {[string, {monitor: any, flag: string, region: string, stats: any, type: string, uri: string}][]} root */ root => { + console.log(root) + root.filter(entry => entry[1].type === "https").forEach(entry => { + let healthUnknown = "health-unknown " + let health = "(unknown)" + if (entry[1].monitor && entry[1].monitor.dailyRatios && entry[1].monitor.dailyRatios[0]) { + health = entry[1].monitor.dailyRatios[0].ratio + healthUnknown = "" + } + let target = entry[1].uri.replace(/\/*$/, "") + destinationPath + q("#instances-tbody").appendChild( + createElement("tr", {}, [ + createElement("td", {textContent: entry[0]}), + createElement("td", {className: "column-center "+healthUnknown, textContent: health}), + createElement("td", {className: "column-center"}, [ + createElement("a", {href: target, textContent: "Go →"}) + ]) + ]) + ) + }) + qa(".loading").forEach(e => e.remove()) + }) +})() diff --git a/src/main.sass b/src/main.sass new file mode 100644 index 0000000..d835c90 --- /dev/null +++ b/src/main.sass @@ -0,0 +1,132 @@ +@mixin prefers-dark + @media (prefers-color-scheme: dark) + @content + +@mixin desktop + @media screen and (min-width: 520px) + @content + +:root + --bg-primary: #fcf5f4 + --bg-secondary: #e9e1df + --bg-tertiary: #cec8c6 + --fg-header: #000 + --fg-primary: #181818 + --fg-dim: #646464 + --fg-link: #2664b5 + --edge-mark: #aaa + --edge-table: #333 + + @include prefers-dark + --bg-primary: #232323 + --bg-secondary: #191919 + --bg-tertiary: #383838 + --fg-header: #fff + --fg-primary: #d9d9d9 + --fg-dim: #828282 + --fg-link: #529ef5 + --edge-mark: #555 + --edge-table: #aaa + +body + background-color: var(--bg-primary) + color: var(--fg-primary) + font-size: 20px + margin: 0 + padding: 8px + font-family: sans-serif + +a, a:visited + color: var(--fg-link) + +noscript + display: block + +mark + background-color: var(--bg-tertiary) + color: var(--fg-primary) + padding: 3px 5px + border-radius: 4px + border: 1px solid var(--edge-mark) + +table, td, th + border: 1px solid var(--edge-table) + border-collapse: collapse + +td, th + padding: 4px 8px + +thead, tr:nth-child(even) + background-color: var(--bg-secondary) + +.banner + display: grid + grid-gap: 24px + align-items: center + justify-items: center + justify-content: center + padding: 20px + + @include desktop + grid-template-columns: 80px auto + + .logo + width: 80px + height: 80px + background-size: contain + background-image: url(static("/assets/img/invidious-logo-light.svg")) + + @include prefers-dark + background-image: url(static("/assets/img/invidious-logo-dark.svg")) + + h1 + color: var(--fg-header) + font-size: 56px + font-weight: bold + text-transform: uppercase + margin: 0 + padding: 0 + +.story + margin: 40px 0px + text-align: center + + @include desktop + white-space: pre-line + +.instances-table + display: flex + justify-content: center + + table + width: 100% + max-width: 700px + +.instances-list + text-align: center + + .list + margin: 0 auto + padding-left: 1em + text-align: left + max-width: max-content + +.loading-td + text-align: center + padding: 20px + background-color: var(--bg-secondary) + +.column-center + text-align: center + +.health-unknown + color: var(--fg-dim) + +.script-warning + margin: 0 auto + max-width: max-content + background: #700 + color: #fff + padding: 4px 20px + border-radius: 8px + border: 1px solid