diff --git a/.github/workflows/updateInvidous.yml b/.github/workflows/updateInvidous.yml new file mode 100644 index 00000000..50bb9769 --- /dev/null +++ b/.github/workflows/updateInvidous.yml @@ -0,0 +1,26 @@ +name: update invidious +on: + workflow_dispatch: + schedule: + - cron: '0 0 1 * *' # check every month + +jobs: + check-list: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download instance list + run: | + wget https://api.invidious.io/instances.json -O data.json + - name: "Run CI" + run: npm run ci:invidious + + - name: Create pull request to update list + uses: peter-evans/create-pull-request@v3 + with: + commit-message: Update Invidious List + author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + branch: ci/update_invidious_list + title: Update Invidious List + body: Automated Invidious list update \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2833884c..2eafbe10 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ web-ext-artifacts .vscode/ dist/ tmp/ -.DS_Store \ No newline at end of file +.DS_Store +ci/data.json \ No newline at end of file diff --git a/ci/invidiousCI.ts b/ci/invidiousCI.ts new file mode 100644 index 00000000..05cf8f8f --- /dev/null +++ b/ci/invidiousCI.ts @@ -0,0 +1,55 @@ +/* +This file is only ran by GitHub Actions in order to populate the Invidious instances list + +This file should not be shipped with the extension +*/ + +import { writeFile, existsSync } from 'fs'; +import { join } from 'path'; + +// import file from https://api.invidious.io/instances.json +if (!existsSync('./data.json')) { + process.exit(1); +} +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import * as data from "./data.json"; + +type instanceMap = { + name: string, + url: string, + dailyRatios: {ratio: string, label: string }[], + thirtyDayUptime: string +}[] + +// only https servers +const mapped: instanceMap = data + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .filter((i: any) => i[1]?.type === 'https') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map((instance: any) => { + return { + name: instance[0], + url: instance[1].uri, + dailyRatios: instance[1].monitor.dailyRatios, + thirtyDayUptime: instance[1]?.monitor['30dRatio'].ratio, + } + }) + +// reliability and sanity checks +const reliableCheck = mapped + .filter((instance) => { + // 30d uptime >= 90% + const thirtyDayUptime = Number(instance.thirtyDayUptime) >= 90 + // available for at least 80/90 days + const dailyRatioCheck = instance.dailyRatios.filter(status => status.label !== "black") + return (thirtyDayUptime && dailyRatioCheck.length >= 80) + }) + // url includes name + .filter(instance => instance.url.includes(instance.name)) + +// finally map to array +const result: string[] = reliableCheck.map(instance => instance.name) +writeFile(join(__dirname, "./invidiouslist.json"), JSON.stringify(result), (err) => { + if (err) return console.log(err); +}) \ No newline at end of file diff --git a/ci/invidiouslist.json b/ci/invidiouslist.json new file mode 100644 index 00000000..905605d8 --- /dev/null +++ b/ci/invidiouslist.json @@ -0,0 +1 @@ +["yewtu.be","invidious.snopyta.org","vid.puffyan.us","invidious.kavin.rocks","invidio.xamh.de","invidious-us.kavin.rocks","inv.riverside.rocks","vid.mint.lgbt","youtube.076.ne.jp","invidious.namazso.eu"] \ No newline at end of file diff --git a/package.json b/package.json index e0c807e5..93584c5b 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "build:watch": "npm run build:watch:chrome", "build:watch:chrome": "webpack --env.browser=chrome --config webpack/webpack.dev.js --watch", "build:watch:firefox": "webpack --env.browser=firefox --config webpack/webpack.dev.js --watch", + "ci:invidious": "ts-node ci/invidiousCI.ts", "dev": "npm run build:dev && concurrently \"npm run web-run\" \"npm run build:watch\"", "dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"", "dev:firefox-android": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox-android\" \"npm run build:watch:firefox\"", diff --git a/src/config.ts b/src/config.ts index d6c412c6..b4a1a9cd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,5 @@ import * as CompileConfig from "../config.json"; +import * as invidiousList from "../ci/invidiouslist.json"; import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, UnEncodedSegmentTimes as UnencodedSegmentTimes } from "./types"; interface SBConfig { @@ -187,7 +188,7 @@ const Config: SBObject = { hideSkipButtonPlayerControls: false, hideDiscordLaunches: 0, hideDiscordLink: false, - invidiousInstances: ["invidious.snopyta.org"], + invidiousInstances: ["invidious.snopyta.org"], // leave as default supportInvidious: false, serverAddress: CompileConfig.serverAddress, minDuration: 0, @@ -433,6 +434,11 @@ function migrateOldFormats(config: SBConfig) { if (config["previousVideoID"] !== undefined) { chrome.storage.sync.remove("previousVideoID"); } + + // populate invidiousInstances with new instances if 3p support is **DISABLED** + if (!config["supportInvidious"] && config["invidiousInstances"].length !== invidiousList.length) { + config["invidiousInstances"] = invidiousList; + } } async function setupConfig() { diff --git a/src/options.ts b/src/options.ts index b345037a..49eed8a1 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,5 +1,6 @@ import Config from "./config"; import * as CompileConfig from "../config.json"; +import * as invidiousList from "../ci/invidiouslist.json"; // Make the config public for debugging purposes window.SB = Config; @@ -297,8 +298,8 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) { const resetButton = element.querySelector(".invidious-instance-reset"); resetButton.addEventListener("click", function() { if (confirm(chrome.i18n.getMessage("resetInvidiousInstanceAlert"))) { - // Set to a clone of the default - Config.config[option] = Config.defaults[option].slice(0); + // Set to CI populated list + Config.config[option] = invidiousList; } }); }