diff --git a/bun.lockb b/bun.lockb index 91fd00a..93ba735 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/config.json b/config.json index 227c1ea..70d71cb 100644 --- a/config.json +++ b/config.json @@ -9,9 +9,9 @@ "SCRIPT_SRC": "", "DOMAIN": "example.org", - "DEFAULT_VIDEO_PROXY": "https://v.minpluto.org", - "DEFAULT_DATA_PROXY": "https://v.minpluto.org", - "DEFAULT_IMAGE_PROXY": "https://i.minpluto.org", + "DEFAULT_VIDEO_PROXY": "https://yt.sudovanilla.org", + "DEFAULT_DATA_PROXY": "https://yt.sudovanilla.org", + "DEFAULT_IMAGE_PROXY": "http://192.168.1.118:6014", "DEFAULT_PLAYER": "Zorn", "SIDEBAR_CATEGORIES": true, diff --git a/image-proxy.js b/image-proxy.js new file mode 100644 index 0000000..cf27451 --- /dev/null +++ b/image-proxy.js @@ -0,0 +1,157 @@ +const express = require("express"); +const fetch = require("node-fetch"); +const { URL } = require("url"); +const { Readable } = require("node:stream"); + +// Array of hostnames that will be proxied +const URL_WHITELIST = [ + "i.ytimg.com", + "cdn.jsdelivr.net", + "yt3.ggpht.com", + "twemoji.maxcdn.com", + "unpkg.com", + "lite.duckduckgo.com", + "youtube.com", + "returnyoutubedislikeapi.com", + "yt.sudovanilla.org", + "yt.sudovanilla.org:9200" +]; + +const app = express(); + +app.use(express.json()); // for parsing application/json +app.use(express.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded + +app.use(function (req, res, next) { + console.log(`=> ${req.method} ${req.originalUrl.slice(1)}`); + next(); +}); + +app.use(function (_req, res, next) { + res.header("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "public, max-age=864000"); // cache header + res.setHeader("poketube-cacher", "PROXY_FILES"); + + next(); +}); + +/** + * @param {express.Request} req + * @param {express.Response} res + */ +const proxy = async (req, res) => { + const { fetch } = await import("undici") + res.setHeader("Cache-Control", "public, max-age=864000"); // cache header + + try { + let url; + + try { + url = new URL("https://" + req.originalUrl.slice(8)); + } catch (e) { + console.log("==> Cannot parse URL: " + e); + return res.status(400).send("Malformed URL"); + } + + // Sanity check, to avoid being used as an open proxy + if (!URL_WHITELIST.includes(url.host)) { + console.log(`==> Refusing to proxy host ${url.host}`); + res.status(401).send(`Hostname '${url.host}' is not permitted`); + return; + } + + console.log(`==> Proxying request`); + + let f = await fetch(url + `?cachefixer=${btoa(Date.now())}`, { + method: req.method, + }); + +Readable.fromWeb(f.body).pipe(res); + } catch (e) { + console.log(`==> Error: ${e}`); + res.status(500).send("Internal server error"); + } +}; + +const listener = (req, res) => { + proxy(req, res); +}; + +app.get("/", (req, res) => { + var json = { + status: "200", + version: "1.3.0", + URL_WHITELIST, + cache: "max-age-864000", + }; + + res.json(json); +}); + +const apiUrls = [ + "https://returnyoutubedislikeapi.com/votes?videoId=", + "https://prod-poketube.testing.poketube.fun/api?v=", + "https://ipv6-t.poketube.fun/api?v=" +]; + +// Define a cache object +const cache = {}; + +app.get("/api", async (req, res) => { +const { fetch } = await import("undici") + + try { + const cacheKey = req.query.v; + + // Check if the result is already cached + if (cache[cacheKey] && Date.now() - cache[cacheKey].timestamp < 3600000) { + // If the cached result is less than 1 hour old, return it + const cachedData = cache[cacheKey].data; + const cachedDate = new Date(cache[cacheKey].timestamp); + return res.json(cachedData); + } + + // Initialize an array to store errors when trying different URLs + const errors = []; + + for (const apiUrl of apiUrls) { + try { + // Fetch data from the current URL + const engagement = await fetch(apiUrl + req.query.v).then((res) => res.json()); + + // Cache the result for future requests + cache[cacheKey] = { + data: engagement, + timestamp: Date.now(), + }; + + res.json(engagement); + return; // Exit the loop if successful + } catch (err) { + // Log the error for this URL and continue to the next URL + console.log(`Error fetching data from ${apiUrl}: ${err.message}`); + errors.push(err.message); + return ""; + } + } + + // If all URLs fail, return an error response + res.status(500).json({ error: "All API endpoints failed", errors }); + } catch (err) { + console.log(err); + } +}); + +app.get("/bangs", async (req, res) => { + + let f = await fetch("https://lite.duckduckgo.com/lite/?q=" + req.query.q, { + method: req.method, + }); + + res.redirect(f); + +}); + +app.all("/*", listener); + +app.listen(6014, () => console.log("Listening on 0.0.0.0:6014")); \ No newline at end of file diff --git a/package.json b/package.json index ce2e786..80e869c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "funding": [ { "type": "SudoVanilla", - "url": "https://sudovanilla.com/donate/" + "url": "https://sudovanilla.org/donate/" } ], "keywords": [ @@ -27,12 +27,14 @@ "translate": "astro-i18next generate" }, "dependencies": { - "@astrojs/node": "^8.2.1", - "@astrojs/vue": "^4.0.8", - "@iconoir/vue": "^7.4.0", - "astro": "^4.4.5", + "@astrojs/node": "^8.3.2", + "@astrojs/vue": "^4.5.0", + "@iconoir/vue": "^7.7.0", + "astro": "^4.11.5", "astro-i18next": "^1.0.0-beta.21", - "rss-to-json": "^2.1.1" + "express": "^4.19.2", + "rss-to-json": "^2.1.1", + "undici": "^6.19.2" }, "devDependencies": { "@sudovanilla/zorn": "^2024.2.26-1", diff --git a/src/pages/en/watch.astro b/src/pages/en/watch.astro index 66153c0..378501a 100644 --- a/src/pages/en/watch.astro +++ b/src/pages/en/watch.astro @@ -51,7 +51,7 @@ let ViewsFormat = ViewsConversion.format(Views);

{video.title}

- +
@@ -196,12 +196,12 @@ function ShareDialogHide() { display: grid; grid-gap: 12px; a { - background: rgb(51 51 51); - border: 2px rgba(255,255,255,0.05) solid; - font-size: 18px; - text-decoration: none; - border-radius: 4px; - padding: 9px 16px; + background: rgb(51 51 51); + border: 2px rgba(255,255,255,0.05) solid; + font-size: 18px; + text-decoration: none; + border-radius: 4px; + padding: 9px 16px; } } diff --git a/src/pages/index.astro b/src/pages/index.astro index 99be7c8..4f30eb6 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -48,7 +48,7 @@ const GamingSplit = GamingData.slice(0, 1)
{TrendingSplit.map((data) => - +

{t("SIDEBAR.CATEGORY_LIST.TRENDING")}

@@ -56,7 +56,7 @@ const GamingSplit = GamingData.slice(0, 1)
)} {MoviesSplit.map((data) => - +

{t("SIDEBAR.CATEGORY_LIST.MOVIES")}

@@ -64,7 +64,7 @@ const GamingSplit = GamingData.slice(0, 1)
)} {MusicSplit.map((data) => - +

{t("SIDEBAR.CATEGORY_LIST.MUSIC")}

@@ -72,7 +72,7 @@ const GamingSplit = GamingData.slice(0, 1)
)} {GamingSplit.map((data) => - +

{t("SIDEBAR.CATEGORY_LIST.GAMES")}

diff --git a/src/pages/jp/watch.astro b/src/pages/jp/watch.astro index a9382cb..d573249 100644 --- a/src/pages/jp/watch.astro +++ b/src/pages/jp/watch.astro @@ -51,7 +51,7 @@ let ViewsFormat = ViewsConversion.format(Views);

{video.title}