diff --git a/package-lock.json b/package-lock.json
index 15b608d..9ec1de8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1499,6 +1499,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"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": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
diff --git a/package.json b/package.json
index 74fb601..b3188f9 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"express-fileupload": "^1.2.0",
"mongoose": "^5.10.2",
"mongoose-int32": "^0.4.1",
+ "node-stream-zip": "^1.11.3",
"typescript": "^4.0.2",
"winston": "^3.3.3"
},
diff --git a/src/index.ts b/src/index.ts
index 16d87d1..cc1f8e9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -58,15 +58,13 @@ db.then(() => {
app.get('/list', async (req, res) => { // only for testing
const docs = await File.find({});
- const map = docs.map(d => {
- const keys = Object.keys(d.toObject()).filter(k => !k.startsWith('_'));
- const obj = {};
- for (const key of keys) obj[key] = d[key];
-
- return obj;
- });
-
- res.send(JSON.stringify(map));
+
+ res.send(docs.map((doc: any) =>
+ `${doc.artist} - ${doc.title} by ${doc.credit}
` +
+ doc.charts.map(ch =>
+ `- ${ch.difficulty} ${ch.rating}: ${ch.name}`
+ ).join('
')
+ ).join('
'));
});
app.get('/', (req, res) => {
diff --git a/src/lib/smparse.ts b/src/lib/smparse.ts
new file mode 100644
index 0000000..6736c6e
--- /dev/null
+++ b/src/lib/smparse.ts
@@ -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;
+}
\ No newline at end of file
diff --git a/src/schema.ts b/src/schema.ts
index df9bec1..2bf463c 100644
--- a/src/schema.ts
+++ b/src/schema.ts
@@ -1,17 +1,18 @@
import * as mongoose from 'mongoose';
-import * as Int32 from 'mongoose-int32';
const Schema = mongoose.Schema;
const Sample = new Schema({
- start: {type: Int32, default: new Int32(0)},
- length: {type: Int32, default: new Int32(0)}
+ start: {type: Number, default: 0},
+ length: {type: Number, default: 0}
});
const Chart = new Schema({
+ type: {type: String, default: 'dance-single'},
name: {type: String, default: ''},
- rating: {type: Int32, default: new Int32(0)},
- type: {type: String, default: 'Challenge'}
+ difficulty: {type: String, default: 'Challenge'},
+ rating: {type: Number, default: 0},
+ radarvalues: [Number]
});
const FileSchema = new Schema({
@@ -24,7 +25,7 @@ const FileSchema = new Schema({
credit: String,
uploader: {type: String, default: '00000000-0000-4000-a000-000000000000'},
sample: Sample,
- bpms: {type: [Number], default: 0},
+ bpms: {type: Object, default: {'0': 0}},
charts: [Chart]
});
diff --git a/src/upload.ts b/src/upload.ts
index b6a686a..d8b94c6 100644
--- a/src/upload.ts
+++ b/src/upload.ts
@@ -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 { parseSM } from './lib/smparse';
+import { File } from './schema';
export function run(app) {
+ const logger = app.get('logger');
+
app.get('/upload', returnStatic('upload.html'));
app.post('/upload', async (req, res) => { // only for testing, very abusable
- console.log(req.files);
-
- res.send('ok, i got something');
+ const file = req.files.file;
+ if (file.mimetype !== 'application/zip') return res.status(400).send('Invalid filetype');
+
+ 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 the listing`); // todo: change to actual url
+ }
+
+ zip.close();
+ fs.unlink(dir, (err) => {
+ if (err) throw err;
+ });
+ });
+ });
});
}
\ No newline at end of file