Compare commits

..

No commits in common. "8fac3fedcc490f09a26c84cdca9c05958fa74a68" and "c018ca12dee3aa856bbd7b256ff0b0ae636a73b6" have entirely different histories.

6 changed files with 30 additions and 154 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
node_modules node_modules
.env .env
output.log output.log
logs

View File

@ -1,25 +1,21 @@
A simple, random Hatsune Miku image server using no HTML*: https://random-miku.owo39.me/ Simply go to https://random-miku.owo39.me/ and it will redirect to `https://random-miku.owo39.me/https://www.pixiv.net/en/artworks/<randomid>` which will serve the raw image directly, and the url as such allows you to delete the left half to get to the source. Another request to the same URL from the same IP address will be redirected to a different random image so you can click the reload button in your browser to see different images.
The `/` URL will 302-redirect you** to a random URL like this: https://random-miku.owo39.me/https://www.pixiv.net/en/artworks/92388757, which then proxies the first original-size image from that post. The url is such that you can select and delete the left half to easily get to the Pixiv post. Some query parameters are available to change the functionality:
Refresh the window and you'll get another random image***. To do this the server remembers the last URL your IP address requested.
There are also some other parameters you can change. | param | values | description | example |
|-------|--------|-------------|---------|
| `size` | `original`, `regular`, `small`, `thumb_mini` | Get a different image size. The default is `original`. | https://random-miku.owo39.me/?size=small |
| `noredirect` | | If used at `/`, server will respond the random image directly to that request instead of redirecting to a URL with the source URL in it (In this case the source id is only recoverable from `X-Pixiv-Id` header.). If used for the other, disables random redirection on reload. | https://random-miku.owo39.me/?noredirect, https://random-miku.owo39.me/https://www.pixiv.net/en/artworks/55814670?noredirect |
| `allow_r18` | | Enables random selection of pornographic illustrations. | https://random-miku.owo39.me/?allow_r18 |
To include pornographic results: https://random-miku.owo39.me/?allow_r18
Auto-refresh the page every 3 seconds using HTTP refresh header: https://random-miku.owo39.me/?auto=3 Note that this way the browser won't log them to history, but you can see your IPA's history at https://random-miku.owo39.me/log (*the only thing HTML is used for). For programatic purposes you can GET `/api` which responds just a random ID in plain text.
Get different sizes (regular, small, or thumb_mini; default is original): https://random-miku.owo39.me/?size=small <hr>
**Use noredirect parameter to get served a random image directly: https://random-miku.owo39.me/?noredirect This enables it to be embedded in Discord, but makes it harder to find the source. here's an embedded example.
***Use noredirect parameter to disable: https://random-miku.owo39.me/noredirect/https://www.pixiv.net/en/artworks/72389090 That's right, querystring is relocated to the left of pixiv url. unfortunately there's no way to get source like this (browsers don't expose redirected urls of embedded resources).
Combining opts: https://random-miku.owo39.me/?auto=0&size=small&allow_r18 ![:sunglas:](https://cdn.discordapp.com/emojis/707727355057012806.png?size=24) ![random miku image](https://random-miku.owo39.me/)
To get just a random ID: https://random-miku.owo39.me/api. Useful for embedding into a website with JavaScript. There is also an `X-Pixiv-Id` header.
----------------------------------------------------------------------------------------
Data set consists of the top 99,495 popular 初音ミク Pixiv illustrations (10,242 of which are r18), scraped in September 2021. Originally served data from a mongo db, but was changed to proxy from Pixiv when lost access to db and because it is faster and more flexible. The old mongodb version is available at http://hmbp.gq:39/, which you can use if you encounter a 404 with the primary one (because some pixiv posts are disappearing apparently).

View File

@ -1,36 +1,29 @@
var express = require("express"); var express = require("express");
var fetch = require("node-fetch"); var fetch = require("node-fetch");
var qs = require("qs");
var serveFavicon = require("serve-favicon");
var fs = require("fs");
var app = express(); var app = express();
app.set("trust proxy", "127.0.0.1"); app.set("trust proxy", "127.0.0.1");
app.listen(process.env.PORT || 39); app.listen(process.env.PORT || 39);
app.use(serveFavicon("ミク.png"));
app.use((req,res,next)=>{req.rawQuery = req.url.includes('?') && req.url.substr(req.url.indexOf('?')+1);next();});
var list = require("./list.json"); var list = require("./list.json");
var ipa_lastload_map = {}; var ipa_lastload_map = {};
function getRandomId(allow_r18) { function getRandomId(allow_r18) {
var i = list[Math.floor(Math.random() * list.length)]; let x = list[Math.floor(Math.random() * list.length)];
if (i[2] && !allow_r18) return getRandomId(); if (x[2] && !allow_r18) return getRandomId();
return i[0]; return x[0];
}; };
function redirectRandom(req, res) {
function getRandomUrl(req) { res.redirect(`/https://www.pixiv.net/en/artworks/${getRandomId(req.query.allow_r18!=null)}${req.url.includes('?')?req.url.substr(req.url.indexOf('?')):''}`);
var s = req?.params.settings ? `/${req?.params.settings}` : '';
var d = getRandomId(req?.query.allow_r18!=null);
return `${s}/https://www.pixiv.net/en/artworks/${d}`;
} }
async function serveId(id, req, res, next) { async function serveId(id, req, res, next) {
try { try {
console.log(new Date().toLocaleString(), req.ip, req.url); if (ipa_lastload_map[req.ip] == req.url && req.query.noredirect == null)
return redirectRandom(req, res);
else ipa_lastload_map[req.ip] = req.url;
console.log(new Date().toLocaleString(), req.ip, id, Object.keys(req.query).length ? req.query : '', req.headers['referer'] || '');
let pages = list.find(x => x[0] == id)?.[1]; let pages = list.find(x => x[0] == id)?.[1];
if (!pages) { if (!pages) {
@ -50,56 +43,29 @@ async function serveId(id, req, res, next) {
var pxreq = await fetch(url, { headers: {"Referer": "https://www.pixiv.net"} }); var pxreq = await fetch(url, { headers: {"Referer": "https://www.pixiv.net"} });
res.type(pxreq.headers.get("Content-Type")); res.type(pxreq.headers.get("Content-Type"));
res.header("X-Pixiv-Id", id); res.header("X-Pixiv-Id", id);
res.header("Cache-Control", "max-age=99999999999999");
if (req.query.auto) res.header("Refresh", `${Number(req.query.auto)}; url=${getRandomUrl(req)}`);
pxreq.body.pipe(res); pxreq.body.pipe(res);
fs.appendFileSync(`logs/${req.ip.replace(/[:\/]/g, '-')}.csv`, `${new Date().toISOString()},${id}\n`);
} catch(error) { } catch(error) {
console.log(error); next(error);
} }
} }
app.get("/https://www.pixiv.net/en/artworks/:id", function (req, res, next) {
app.get("(/:settings)?/https://www.pixiv.net/en/artworks/:id", function (req, res, next) {
if (req.rawQuery) {
let settings = req.params.settings || '';
if (settings) settings += "&";
settings += req.rawQuery;
return res.redirect(`/${settings}/https://www.pixiv.net/en/artworks/${req.params.id}`);
}
if (req.params.settings) req.query = qs.parse(req.params.settings);
if (ipa_lastload_map[req.ip] == req.url && req.query.noredirect == null)
return res.redirect(getRandomUrl(req));
else ipa_lastload_map[req.ip] = req.url;
serveId(req.params.id, req, res, next); serveId(req.params.id, req, res, next);
}); });
app.get('/', (req, res, next) => { app.get('/', (req, res, next) => {
if (req.query.noredirect != null) { if (req.query.noredirect != null) {
serveId(getRandomId(req.query.allow_r18!=null), req, res, next) serveId(getRandomId(req.query.allow_r18!=null), req, res, next)
} else { } else {
res.redirect((req.rawQuery ? `/${req.rawQuery}` : '') + getRandomUrl(req)); redirectRandom(req, res);
} }
}); });
app.get("/favicon.ico", function (req, res) {
res.sendFile(process.cwd() + "/ミク.png");
});
app.get("/api", function (req, res) { app.get("/api", function (req, res) {
res.type("text/plain").send(getRandomId(req.query.allow_r18!=null)); res.type("text/plain").send(getRandomId(req.query.allow_r18!=null));
}); });
app.get("/log", function (req, res) {
res.redirect(`/log/${req.ip}`);
});
var log_html = fs.readFileSync("log.html", "utf8");
app.get("/log/:ipa", function (req, res) {
res.send(log_html);
});
app.get("/log/:ipa/csv", function (req, res) {
res.sendFile(req.params.ipa.replace(/[:\/]/g, '-') + '.csv', {root: process.cwd() + "/logs/"});
});

View File

@ -1,34 +0,0 @@
<title>history</title>
<style>
.i {
width: 128px;
height: 128px;
display: inline-block;
}
</style>
<h1>view history for ip address <span id="ipa">???</span></h1>
<p>in reverse-chronological order. hover for dates</p>
<script>
let ipa = location.pathname.split("/").pop().trim();
document.getElementById("ipa").innerText = ipa;
fetch(location.pathname + "/csv").then(r => r.text()).then(csv => {
csv = csv.trim();
csv = csv.split("\n").map(x => x.split(','));
var imgs = [];
for (let row of csv) {
let date = new Date(row[0]).toLocaleString();
let pixiv_id = row[1];
let pixiv_url = "https://www.pixiv.net/en/artworks/" + pixiv_id;
let thumb_url = "/size=thumb_mini/" + pixiv_url;
//imgs.unshift(`<a href="${pixiv_url}"><img src="${thumb_url}" alt="${pixiv_id}" title="${date}" /></a>`);
imgs.unshift(`<a href="${pixiv_url}"><div class="i" style="background-image: url(${thumb_url})" title="${date}">${pixiv_id}</div></a>`);
}
document.body.innerHTML += imgs.join('');
});
</script>

52
package-lock.json generated
View File

@ -6,8 +6,7 @@
"": { "": {
"dependencies": { "dependencies": {
"express": "^4.17.1", "express": "^4.17.1",
"node-fetch": "^2.6.5", "node-fetch": "^2.6.5"
"serve-favicon": "^2.5.0"
} }
}, },
"node_modules/accepts": { "node_modules/accepts": {
@ -444,31 +443,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}, },
"node_modules/serve-favicon": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
"integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=",
"dependencies": {
"etag": "~1.8.1",
"fresh": "0.5.2",
"ms": "2.1.1",
"parseurl": "~1.3.2",
"safe-buffer": "5.1.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/serve-favicon/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"node_modules/serve-favicon/node_modules/safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
@ -887,30 +861,6 @@
} }
} }
}, },
"serve-favicon": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
"integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=",
"requires": {
"etag": "~1.8.1",
"fresh": "0.5.2",
"ms": "2.1.1",
"parseurl": "~1.3.2",
"safe-buffer": "5.1.1"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
}
}
},
"serve-static": { "serve-static": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",

View File

@ -1,7 +1,6 @@
{ {
"dependencies": { "dependencies": {
"express": "^4.17.1", "express": "^4.17.1",
"node-fetch": "^2.6.5", "node-fetch": "^2.6.5"
"serve-favicon": "^2.5.0"
} }
} }