Compare commits
22 Commits
c018ca12de
...
proxy
| Author | SHA1 | Date | |
|---|---|---|---|
| 881ee4568d | |||
| 7ac32d6dff | |||
| 175a86b490 | |||
| 8f1c789c8e | |||
| c4d55ec6c7 | |||
| 3ab1623bed | |||
| 88a6e5e692 | |||
| 99f05ea2e0 | |||
| 3ef1d4bdcb | |||
| 091197e7c7 | |||
| 63a9916a03 | |||
| 0219cc7f83 | |||
| 47a05b243f | |||
| 93017002b9 | |||
| f5e734a9c9 | |||
| 42566713cf | |||
| 9d8d5c078e | |||
| 77221513b5 | |||
| 9ecdae240b | |||
| 8fac3fedcc | |||
| 04cbf250b0 | |||
| ebb2071a12 |
@@ -1,3 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
output.log
|
output.log
|
||||||
|
logs
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
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.
|
A simple no-HTML random Hatsune Miku image server. Example: https://rms.owo69.me/
|
||||||
|
|
||||||
Some query parameters are available to change the functionality:
|
- Click reload to get a different one. Delete left half of URL to get to source.
|
||||||
|
- Include pornographic results: https://rms.owo69.me/?allow_r18([=only](https://rms.owo69.me/?allow_r18=only))
|
||||||
|
- Get different sizes (original, regular, small, or thumb_mini): https://rms.owo69.me/?size=thumb_mini
|
||||||
|
- Auto-refresh using HTTP Refresh header: https://rms.owo69.me/?auto=3
|
||||||
|
- kek: https://rms.owo69.me/?allow_r18&size=thumb_mini&auto=0
|
||||||
|
- See your history: https://rms.owo69.me/log
|
||||||
|
- Load image by id without redirect thing: https://rms.owo69.me/img/51586149
|
||||||
|
- Load random image directly without redirect thing: https://rms.owo69.me/img/random
|
||||||
|
- Draw Pixiv URL onto upper left corner of image: https://rms.owo69.me/img/random?drawsource&size=regular
|
||||||
|
|
||||||
|
Here is an HTML site that makes use of it: https://lamp.tk/miku.html
|
||||||
|
|
||||||
| param | values | description | example |
|
### Prerequisites
|
||||||
|-------|--------|-------------|---------|
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
|
- Node.js
|
||||||
|
- GraphicsMagick for ?drawsource option
|
||||||
|
|
||||||
For programatic purposes you can GET `/api` which responds just a random ID in plain text.
|
### todo
|
||||||
|
|
||||||
<hr>
|
- Option to limit range to get more higher-ranked illustrations
|
||||||
|
|
||||||
here's an embedded example.
|
|
||||||
|
|
||||||
unfortunately there's no way to get source like this (browsers don't expose redirected urls of embedded resources).
|
|
||||||
|
|
||||||

|
|
||||||
|
|||||||
@@ -1,29 +1,43 @@
|
|||||||
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 gm = require("gm");
|
||||||
|
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, process.env.ADDRESS);
|
||||||
|
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 list_r18_indices = [], list_safe_indices = [];
|
||||||
|
for (let i = 0, h = list.length; i < h; i++) if (list[i][2]) list_r18_indices.push(i); else list_safe_indices.push(i);
|
||||||
var ipa_lastload_map = {};
|
var ipa_lastload_map = {};
|
||||||
|
|
||||||
|
|
||||||
function getRandomId(allow_r18) {
|
function getRandomId(allow_r18) {
|
||||||
let x = list[Math.floor(Math.random() * list.length)];
|
if (allow_r18?.toLowerCase() == "only") {
|
||||||
if (x[2] && !allow_r18) return getRandomId();
|
return list[list_r18_indices[Math.floor(Math.random() * list_r18_indices.length)]][0];
|
||||||
return x[0];
|
} else if (allow_r18 != null) {
|
||||||
|
return list[Math.floor(Math.random() * list.length)][0];
|
||||||
|
} else {
|
||||||
|
return list[list_safe_indices[Math.floor(Math.random() * list_safe_indices.length)]][0];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function redirectRandom(req, res) {
|
|
||||||
res.redirect(`/https://www.pixiv.net/en/artworks/${getRandomId(req.query.allow_r18!=null)}${req.url.includes('?')?req.url.substr(req.url.indexOf('?')):''}`);
|
function getRandomUrl(req) {
|
||||||
|
var s = req?.params.settings ? `/${req?.params.settings}` : '';
|
||||||
|
var d = getRandomId(req?.query.allow_r18);
|
||||||
|
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 {
|
||||||
if (ipa_lastload_map[req.ip] == req.url && req.query.noredirect == null)
|
console.log(new Date().toLocaleString(), req.ip, req.url);
|
||||||
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) {
|
||||||
@@ -37,35 +51,88 @@ async function serveId(id, req, res, next) {
|
|||||||
pages = data.body;
|
pages = data.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = pages[0].urls[req.query.size ?? "original"];
|
var size = req.query.size || "original";
|
||||||
|
var url = pages[0].urls[size];
|
||||||
if (!url) return res.status(400).type("text").send(`size must be one of the following: ${Object.keys(pages[0].urls).join(', ')}`);
|
if (!url) return res.status(400).type("text").send(`size must be one of the following: ${Object.keys(pages[0].urls).join(', ')}`);
|
||||||
|
|
||||||
var pxreq = await fetch(url, { headers: {"Referer": "https://www.pixiv.net"} });
|
var pxreq = await fetch(url, { headers: {"Referer": "https://www.pixiv.net"} });
|
||||||
|
//if (pxreq.status != 200 && size == "original") {
|
||||||
|
// res.header("X-Using-Backup", '1');
|
||||||
|
// pxreq = await fetch(`https://39.hmbp.gq/https://www.pixiv.net/en/artworks/${id}?noredirect`);
|
||||||
|
//}
|
||||||
|
|
||||||
|
res.status(pxreq.status);
|
||||||
res.type(pxreq.headers.get("Content-Type"));
|
res.type(pxreq.headers.get("Content-Type"));
|
||||||
|
res.header("Content-Disposition", `filename=${url.split('/').pop()}`);
|
||||||
res.header("X-Pixiv-Id", id);
|
res.header("X-Pixiv-Id", id);
|
||||||
pxreq.body.pipe(res);
|
if (req.query.auto) res.header("Refresh", `${Number(req.query.auto)}; url=${getRandomUrl(req)}`);
|
||||||
|
if (req.query.drawsource != null && pxreq.headers.get("Content-Type").startsWith("image")) {
|
||||||
|
gm(pxreq.body).drawText(2, 12, size == "thumb_mini" ? id : `https://www.pixiv.net/en/artworks/${id}`).stream().pipe(res);
|
||||||
|
}
|
||||||
|
else pxreq.body.pipe(res);
|
||||||
|
|
||||||
|
fs.appendFileSync(`logs/${req.ip.replace(/[:\/]/g, '-')}.csv`, `${new Date().toISOString()},${id}\n`);
|
||||||
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get("/https://www.pixiv.net/en/artworks/:id", function (req, res, next) {
|
// original HTML-less site for humans via web browser.
|
||||||
|
// - click refresh to get different image
|
||||||
|
// - delete left half of url to get to source
|
||||||
|
// - hacked querystring so it's before source url
|
||||||
|
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) 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) => {
|
// initial randir
|
||||||
if (req.query.noredirect != null) {
|
app.get('/', (req, res) => {
|
||||||
serveId(getRandomId(req.query.allow_r18!=null), req, res, next)
|
res.redirect((req.rawQuery ? `/${req.rawQuery}` : '') + getRandomUrl(req));
|
||||||
} else {
|
|
||||||
redirectRandom(req, res);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/favicon.ico", function (req, res) {
|
// direct random image without redirects
|
||||||
res.sendFile(process.cwd() + "/ミク.png");
|
app.get("/img/random", (req, res, next) => {
|
||||||
|
serveId(getRandomId(req.query.allow_r18), req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api", function (req, res) {
|
// simple serve id without redirects
|
||||||
res.type("text/plain").send(getRandomId(req.query.allow_r18!=null));
|
app.get("/img/:id", (req, res, next) => {
|
||||||
|
res.header("Cache-Control", "max-age=99999999999999");
|
||||||
|
serveId(req.params.id, req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
// plaintext random id(s) for scripts
|
||||||
|
app.get("/randomid", function (req, res) {
|
||||||
|
var count = Number(req.query.count) || 1;
|
||||||
|
count = Math.min(count, 100);
|
||||||
|
var ids = [];
|
||||||
|
for (let i = 0; i < count; i++) ids.push(getRandomId(req.query.allow_r18));
|
||||||
|
ids = ids.join(',');
|
||||||
|
res.type("text/plain").send(ids);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// log site
|
||||||
|
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/"});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<title>history</title>
|
||||||
|
<style>
|
||||||
|
.i {
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h1>history for <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 = `/img/${pixiv_id}?size=thumb_mini`;
|
||||||
|
//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>
|
||||||
Generated
+630
-253
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"node-fetch": "^2.6.5"
|
"gm": "^1.23.1",
|
||||||
|
"node-fetch": "^2.6.5",
|
||||||
|
"serve-favicon": "^2.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user