file parsing!!
you can now just drop in a zip in /upload and it'll parse, handle and upload everything for you right now only the metadata is saved, so the actual file downloads aren't available, but that shouldn't be too hard to implement this is really hacky however, and dropping in a file with a malformed .sm or 2 .sms will likely make it have a panic attack definetly needs lots and lots of testing, it barely works
This commit is contained in:
		
							parent
							
								
									665a372f5e
								
							
						
					
					
						commit
						108eec139d
					
				
					 6 changed files with 122 additions and 18 deletions
				
			
		
							
								
								
									
										5
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -1499,6 +1499,11 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", |       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", | ||||||
|       "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" |       "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" | ||||||
|     }, |     }, | ||||||
|  |     "node-stream-zip": { | ||||||
|  |       "version": "1.11.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.11.3.tgz", | ||||||
|  |       "integrity": "sha512-GY+9LxkQuIT3O7K8BTdHVGKFcBYBy2vAVcTBtkKpu+OlBef/NSb6VuIWSyLiVDfmLMkggHeRJZN0F3W0GWU/uw==" | ||||||
|  |     }, | ||||||
|     "on-finished": { |     "on-finished": { | ||||||
|       "version": "2.3.0", |       "version": "2.3.0", | ||||||
|       "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", |       "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
|     "express-fileupload": "^1.2.0", |     "express-fileupload": "^1.2.0", | ||||||
|     "mongoose": "^5.10.2", |     "mongoose": "^5.10.2", | ||||||
|     "mongoose-int32": "^0.4.1", |     "mongoose-int32": "^0.4.1", | ||||||
|  |     "node-stream-zip": "^1.11.3", | ||||||
|     "typescript": "^4.0.2", |     "typescript": "^4.0.2", | ||||||
|     "winston": "^3.3.3" |     "winston": "^3.3.3" | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								src/index.ts
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								src/index.ts
									
										
									
									
									
								
							|  | @ -58,15 +58,13 @@ db.then(() => { | ||||||
| 
 | 
 | ||||||
| 	app.get('/list', async (req, res) => { // only for testing
 | 	app.get('/list', async (req, res) => { // only for testing
 | ||||||
| 		const docs = await File.find({}); | 		const docs = await File.find({}); | ||||||
| 		const map = docs.map(d => { | 		 | ||||||
| 			const keys = Object.keys(d.toObject()).filter(k => !k.startsWith('_')); | 		res.send(docs.map((doc: any) =>  | ||||||
| 			const obj = {}; | 			`${doc.artist} - ${doc.title} by ${doc.credit}<br>` + | ||||||
| 			for (const key of keys) obj[key] = d[key]; | 			doc.charts.map(ch => | ||||||
| 
 | 				`- ${ch.difficulty} ${ch.rating}: ${ch.name}` | ||||||
| 			return obj; | 			).join('<br>') | ||||||
| 		}); | 		).join('<br><br>')); | ||||||
| 
 |  | ||||||
| 		res.send(JSON.stringify(map)); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	app.get('/', (req, res) => { | 	app.get('/', (req, res) => { | ||||||
|  |  | ||||||
							
								
								
									
										57
									
								
								src/lib/smparse.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/lib/smparse.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | export function parseSM(data: string) { | ||||||
|  | 	data = data.replace(/[\n\r]/g,''); | ||||||
|  | 
 | ||||||
|  | 	// steps
 | ||||||
|  | 	const difficulties = []; | ||||||
|  | 	const steps = data.split('#NOTES:'); | ||||||
|  | 	steps.slice(1); | ||||||
|  | 
 | ||||||
|  | 	for (const step of steps) { | ||||||
|  | 		if (step.includes(';')) { | ||||||
|  | 			const diff: any = {}; | ||||||
|  | 			const stepsSplit = step.split(';')[0].split(':').map(seg => seg.trim()); | ||||||
|  | 
 | ||||||
|  | 			if (stepsSplit.length === 6) { | ||||||
|  | 				diff.type = stepsSplit[0]; | ||||||
|  | 				diff.name = stepsSplit[1]; | ||||||
|  | 				diff.difficulty = stepsSplit[2]; | ||||||
|  | 				diff.rating = Number(stepsSplit[3]); | ||||||
|  | 				diff.radarvalues = stepsSplit[4].split(',').map(v => Number(v)); | ||||||
|  | 
 | ||||||
|  | 				difficulties.push(diff); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// metadata
 | ||||||
|  | 	const lines = data.split(';').filter(l => l.startsWith('#')); | ||||||
|  | 	const obj: any = {}; | ||||||
|  | 
 | ||||||
|  | 	for (const l of lines) { | ||||||
|  | 		const key = l.split(':')[0].slice(1); | ||||||
|  | 		let value: any = l.split(':')[1]; | ||||||
|  | 
 | ||||||
|  | 		if (value !== '' && !(key === 'FGCHANGES' || key === 'BGCHANGES' || key === 'BETTERBGCHANGES' || key === 'SPELLCARDS')) { // no
 | ||||||
|  | 			if (!isNaN(value)) value = Number(value); | ||||||
|  | 			if (value === 'YES') value = true; | ||||||
|  | 			if (value === 'NO') value = false; | ||||||
|  | 			if (typeof value === 'string' && value.includes('=')) { // likely a map
 | ||||||
|  | 				const keys = value.split(',').map(v => v.split('=')[0]); | ||||||
|  | 				const values = value.split(',').map(v => v.split('=')[1]); | ||||||
|  | 				const map = {}; | ||||||
|  | 
 | ||||||
|  | 				for (const i in keys) { | ||||||
|  | 					map[Number(keys[i])] = Number(values[i]); // afaik maps are only numbers?
 | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				value = map; | ||||||
|  | 			} | ||||||
|  | 	 | ||||||
|  | 			obj[key.toLowerCase()] = value; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	obj.charts = difficulties; | ||||||
|  | 
 | ||||||
|  | 	return obj; | ||||||
|  | } | ||||||
|  | @ -1,17 +1,18 @@ | ||||||
| import * as mongoose from 'mongoose'; | import * as mongoose from 'mongoose'; | ||||||
| import * as Int32 from 'mongoose-int32'; |  | ||||||
| 
 | 
 | ||||||
| const Schema = mongoose.Schema; | const Schema = mongoose.Schema; | ||||||
|   |   | ||||||
| const Sample = new Schema({ | const Sample = new Schema({ | ||||||
| 	start: {type: Int32, default: new Int32(0)}, | 	start: {type: Number, default: 0}, | ||||||
| 	length: {type: Int32, default: new Int32(0)} | 	length: {type: Number, default: 0} | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const Chart = new Schema({ | const Chart = new Schema({ | ||||||
|  | 	type: {type: String, default: 'dance-single'}, | ||||||
| 	name: {type: String, default: ''}, | 	name: {type: String, default: ''}, | ||||||
| 	rating: {type: Int32, default: new Int32(0)}, | 	difficulty: {type: String, default: 'Challenge'}, | ||||||
| 	type: {type: String, default: 'Challenge'} | 	rating: {type: Number, default: 0}, | ||||||
|  | 	radarvalues: [Number] | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const FileSchema = new Schema({ | const FileSchema = new Schema({ | ||||||
|  | @ -24,7 +25,7 @@ const FileSchema = new Schema({ | ||||||
| 	credit: String, | 	credit: String, | ||||||
| 	uploader: {type: String, default: '00000000-0000-4000-a000-000000000000'}, | 	uploader: {type: String, default: '00000000-0000-4000-a000-000000000000'}, | ||||||
| 	sample: Sample, | 	sample: Sample, | ||||||
| 	bpms: {type: [Number], default: 0}, | 	bpms: {type: Object, default: {'0': 0}}, | ||||||
| 	charts: [Chart] | 	charts: [Chart] | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,11 +1,53 @@ | ||||||
|  | import { tmpdir } from 'os'; | ||||||
|  | import * as fs from 'fs'; | ||||||
|  | const StreamZip = require('node-stream-zip'); | ||||||
|  | 
 | ||||||
| import { returnStatic } from './lib/util'; | import { returnStatic } from './lib/util'; | ||||||
|  | import { parseSM } from './lib/smparse'; | ||||||
|  | import { File } from './schema'; | ||||||
| 
 | 
 | ||||||
| export function run(app) { | export function run(app) { | ||||||
|  | 	const logger = app.get('logger'); | ||||||
|  | 
 | ||||||
| 	app.get('/upload', returnStatic('upload.html')); | 	app.get('/upload', returnStatic('upload.html')); | ||||||
| 	 | 	 | ||||||
| 	app.post('/upload', async (req, res) => { // only for testing, very abusable
 | 	app.post('/upload', async (req, res) => { // only for testing, very abusable
 | ||||||
| 		console.log(req.files); | 		const file = req.files.file; | ||||||
| 		 | 		if (file.mimetype !== 'application/zip') return res.status(400).send('Invalid filetype'); | ||||||
| 		res.send('ok, i got something'); | 
 | ||||||
|  | 		const dir = tmpdir() + '/' + file.md5; | ||||||
|  | 		fs.writeFile(dir, file.data, (err) => { | ||||||
|  | 			if (err) throw err; | ||||||
|  | 
 | ||||||
|  | 			const zip = new StreamZip({ | ||||||
|  | 				file: dir, | ||||||
|  | 				storeEntries: true | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			zip.on('ready', () => { | ||||||
|  | 				const smFile = Object.values(zip.entries()).find((f: any) => | ||||||
|  | 					!f.isDirectory && (f.name.endsWith('.sm')) | ||||||
|  | 				); | ||||||
|  | 
 | ||||||
|  | 				if (!smFile) { | ||||||
|  | 					res.status(400).send('No .sm found'); | ||||||
|  | 				} else { | ||||||
|  | 					const data = zip.entryDataSync((smFile as any).name); | ||||||
|  | 					const chart = parseSM(data.toString()); | ||||||
|  | 
 | ||||||
|  | 					logger.info(`${chart.artist} - ${chart.title} was just uploaded`); | ||||||
|  | 
 | ||||||
|  | 					const file = new File(chart); | ||||||
|  | 					file.save(); | ||||||
|  | 
 | ||||||
|  | 					res.send(`your file "${chart.artist} - ${chart.title}" has been uploaded! check <a href="http://0.0.0.0:8080/list">the listing</a>`); // todo: change to actual url
 | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				zip.close(); | ||||||
|  | 				fs.unlink(dir, (err) => { | ||||||
|  | 					if (err) throw err; | ||||||
|  | 				}); | ||||||
|  | 			}); | ||||||
|  | 		}); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue