Compare commits
	
		
			44 Commits
		
	
	
		
			4dd589fdce
			...
			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 | |||
| c018ca12de | |||
| 66ab01429a | |||
| 9fee2a7852 | |||
| 4c8ee7b20a | |||
| a75c37731f | |||
| ebf6a8ffc4 | |||
| 6ed02a4740 | |||
| 1b616bb6d9 | |||
| 7fe64b6c12 | |||
| b6ff950214 | |||
| 1d88a293ae | |||
| df44cd1d3e | |||
| df89043a59 | |||
| f2dadbb019 | |||
| 0bb834a492 | |||
| 0e9c70c55e | |||
| 3233f32810 | |||
| a2152d2936 | |||
| ced093e63c | |||
| 72599b5cdf | |||
| 019170eb1c | |||
| 1b9e51f1cb | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,2 +1,4 @@ | ||||
| node_modules | ||||
| .env | ||||
| output.log | ||||
| logs | ||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| A simple no-HTML random Hatsune Miku image server. Example: https://rms.owo69.me/ | ||||
| 
 | ||||
| - 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 | ||||
| 
 | ||||
| ### Prerequisites | ||||
| 
 | ||||
| - Node.js | ||||
| - GraphicsMagick for ?drawsource option | ||||
| 
 | ||||
| ### todo | ||||
| 
 | ||||
| - Option to limit range to get more higher-ranked illustrations | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										163
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								index.js
									
									
									
									
									
								
							| @ -1,31 +1,138 @@ | ||||
| require("dotenv").config(); | ||||
| var {MongoClient, GridFSBucket} = require("mongodb"); | ||||
| var express = require("express"); | ||||
| 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 dbclient = new MongoClient(process.env.DB_URI); | ||||
| dbclient.connect().then(async () => { | ||||
| 	var db = dbclient.db("mikudb"); | ||||
| 	var collection = db.collection("illustration_collection"); | ||||
| 	var bucket = new GridFSBucket(db); | ||||
| 	var idlist = require("./idlist.json"); | ||||
| 	var ipa_lastload_map = {}; | ||||
| 	let redirectRandom = (req, res) => { | ||||
| 		let random_id = idlist[Math.floor(Math.random() * idlist.length)]; | ||||
| 		res.redirect(`/https://www.pixiv.net/en/artworks/${random_id}`); | ||||
| 	}; | ||||
| 	app.get('/', redirectRandom); | ||||
| 	app.get("/https://www.pixiv.net/en/artworks/:id", async (req, res) => { | ||||
| 		//if (req.headers["cache-control"] == "max-age=0") { // this indicates a reload in chrome
 | ||||
| 		// but chrome still sends it after redirect
 | ||||
| 		if (ipa_lastload_map[req.ip] == req.params.id) { | ||||
| 			return redirectRandom(req, res);  | ||||
| 		} else ipa_lastload_map[req.ip] = req.params.id; | ||||
| 		console.log(new Date().toLocaleString(), req.ip, req.params.id); | ||||
| 		var asdf = await collection.findOne({_id: req.params.id}, {downloaded_images: 1}); | ||||
| 		if (!asdf) return res.sendStatus(404); | ||||
| 		var gfsid = Object.values(asdf.downloaded_images)[0]; | ||||
| 		bucket.openDownloadStream(gfsid).pipe(res); | ||||
| 	}); | ||||
| 	app.listen(8039); | ||||
| 	console.log("server ready"); | ||||
| app.set("trust proxy", "127.0.0.1"); | ||||
| 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_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 = {}; | ||||
| 
 | ||||
| 
 | ||||
| function getRandomId(allow_r18) { | ||||
| 	if (allow_r18?.toLowerCase() == "only") { | ||||
| 		return list[list_r18_indices[Math.floor(Math.random() * list_r18_indices.length)]][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 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) { | ||||
| 	try { | ||||
| 		console.log(new Date().toLocaleString(), req.ip, req.url); | ||||
| 		 | ||||
| 		let pages = list.find(x => x[0] == id)?.[1]; | ||||
| 		if (!pages) { | ||||
| 			let data = await (await fetch(`https://pixiv.net/ajax/illust/${id}/pages`, { | ||||
| 				headers: { "Host": "www.pixiv.net" } | ||||
| 			})).json(); | ||||
| 			if (data.error) { | ||||
| 				res.status(502).type("text").send(data.message); | ||||
| 				return; | ||||
| 			} | ||||
| 			pages = data.body; | ||||
| 		} | ||||
| 
 | ||||
| 		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(', ')}`); | ||||
| 		 | ||||
| 		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.header("Content-Disposition", `filename=${url.split('/').pop()}`); | ||||
| 		res.header("X-Pixiv-Id", id); | ||||
| 		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) { | ||||
| 		next(error); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 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); | ||||
| }); | ||||
| 
 | ||||
| // initial randir
 | ||||
| app.get('/', (req, res) => { | ||||
| 	res.redirect((req.rawQuery ? `/${req.rawQuery}` : '') + getRandomUrl(req)); | ||||
| }); | ||||
| 
 | ||||
| // direct random image without redirects
 | ||||
| app.get("/img/random", (req, res, next) => { | ||||
| 	serveId(getRandomId(req.query.allow_r18), req, res, next); | ||||
| }); | ||||
| 
 | ||||
| // simple serve id without redirects
 | ||||
| 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/"}); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										34
									
								
								log.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								log.html
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||
							
								
								
									
										1216
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1216
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,7 +1,8 @@ | ||||
| { | ||||
|   "dependencies": { | ||||
|     "dotenv": "^10.0.0", | ||||
|     "express": "^4.17.1", | ||||
|     "mongodb": "^4.1.2" | ||||
|     "gm": "^1.23.1", | ||||
|     "node-fetch": "^2.6.5", | ||||
|     "serve-favicon": "^2.5.0" | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user