Skip to content

Commit

Permalink
Add default ddex stage keys and enforce prettier (#7098)
Browse files Browse the repository at this point in the history
  • Loading branch information
theoilie authored Jan 5, 2024
1 parent 80ec2c0 commit 6aada56
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 132 deletions.
2 changes: 2 additions & 0 deletions packages/ddex/.env.stage
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DDEX_SECRET='54abb93ab41afeeb99a411502ea1ee9b36ec8221cdc7c80a0ceef84c8ca95fc0'
DDEX_KEY='be46e3e1cc722d2ae306f93f21f5b4f08bbd0d53'
20 changes: 16 additions & 4 deletions packages/ddex/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
env: { node: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: [],
plugins: ['prettier'],
rules: {
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-empty-function': 'off'
'@typescript-eslint/no-empty-function': 'off',
'prettier/prettier': ['error', {
singleQuote: true,
semi: false,
useTabs: false,
tabWidth: 2,
trailingComma: 'es5',
printWidth: 80,
bracketSpacing: true,
arrowParens: 'always',
}],
},
}
};
10 changes: 10 additions & 0 deletions packages/ddex/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"singleQuote": true,
"semi": false,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80,
"bracketSpacing": true,
"arrowParens": "always"
}
55 changes: 31 additions & 24 deletions packages/ddex/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,51 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import express, { Express, Request, Response } from "express";
import path from "path";
import * as uploadController from "./controllers/uploadController";
import { createSdkService } from "./services/sdkService";
import { createScheduledReleaseService } from "./services/scheduledReleaseService";
import { createDbService } from "./services/dbService";
import express, { Express, Request, Response } from 'express'
import path from 'path'
import * as uploadController from './controllers/uploadController'
import { createSdkService } from './services/sdkService'
import { createScheduledReleaseService } from './services/scheduledReleaseService'
import { createDbService } from './services/dbService'

/*
* Initialize services
*/

const dbUrl = process.env.audius_db_url || 'postgres://postgres:postgres@localhost:5432/audius_discovery';
const dbService = createDbService(dbUrl);
const sdkService = createSdkService();
const scheduledReleaseService = createScheduledReleaseService(sdkService.getSdk());
const dbUrl =
process.env.audius_db_url ||
'postgres://postgres:postgres@localhost:5432/audius_discovery'
const dbService = createDbService(dbUrl)
const sdkService = createSdkService()
const scheduledReleaseService = createScheduledReleaseService(
sdkService.getSdk()
)

/*
* Define API routes
*/

const app: Express = express();
app.post('/api/upload', uploadController.postUploadXml(dbService, sdkService.getSdk()));
const app: Express = express()
app.post(
'/api/upload',
uploadController.postUploadXml(dbService, sdkService.getSdk())
)
app.get('/api/health_check', (req: Request, res: Response) => {
res.status(200).send('DDEX is alive!');
});
res.status(200).send('DDEX is alive!')
})

/*
* Routes to serve the React app as static assets at the root path
* Serve the React app as static assets at the root path
*/

const isProduction = process.env.NODE_ENV === 'production';
const isProduction = process.env.NODE_ENV === 'production'
const buildPath = isProduction
? path.join(__dirname, '..', 'public')
: path.join(__dirname, '..', '..', 'ddex-frontend', 'dist');
app.use(express.static(buildPath));
app.get("/", (req: Request, res: Response) => {
res.sendFile(path.join(buildPath, 'index.html'));
});
: path.join(__dirname, '..', '..', 'ddex-frontend', 'dist')
app.use(express.static(buildPath))
app.get('/', (req: Request, res: Response) => {
res.sendFile(path.join(buildPath, 'index.html'))
})
app.get('*', (req: Request, res: Response) => {
res.sendFile(path.join(buildPath, 'index.html')); // Fallback for handling client-side routing
});
res.sendFile(path.join(buildPath, 'index.html')) // Fallback for handling client-side routing
})

export default app;
export default app
121 changes: 61 additions & 60 deletions packages/ddex/src/controllers/uploadController.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
import { Request, Response } from "express";
import fs from "fs";
import { DOMParser } from "linkedom";
import multer from "multer";
import { Request, Response } from 'express'
import fs from 'fs'
import { DOMParser } from 'linkedom'
import multer from 'multer'
import type {
AudiusSdk as AudiusSdkType,
Genre,
UploadTrackRequest,
} from "@audius/sdk/dist/sdk/index.d.ts";
} from '@audius/sdk/dist/sdk/index.d.ts'

const upload = multer({ dest: 'uploads/' });
const upload = multer({ dest: 'uploads/' })

const queryAll = (node: any, ...fields: string[]) => {
for (const field of fields) {
const hits = node.querySelectorAll(field);
if (hits.length) return Array.from(hits);
const hits = node.querySelectorAll(field)
if (hits.length) return Array.from(hits)
}
return [];
};
return []
}

const firstValue = (node: any, ...fields: string[]) => {
for (const field of fields) {
const hit = node.querySelector(field);
if (hit) return hit.textContent.trim();
const hit = node.querySelector(field)
if (hit) return hit.textContent.trim()
}
};
}

const processXml = async (document: any, audiusSdk: AudiusSdkType) => {
// extract SoundRecording
const trackNodes = queryAll(document, "SoundRecording", "track");
const trackNodes = queryAll(document, 'SoundRecording', 'track')

for (const trackNode of Array.from(trackNodes)) {
const releaseDateValue = firstValue(
trackNode,
"OriginalReleaseDate",
"originalReleaseDate",
);
const title = firstValue(trackNode, "TitleText", "trackTitle");
'OriginalReleaseDate',
'originalReleaseDate'
)
const title = firstValue(trackNode, 'TitleText', 'trackTitle')
const tt = {
title,

// todo: need to normalize genre
// genre: firstValue(trackNode, "Genre", "trackGenre"),
genre: "Metal" as Genre,
genre: 'Metal' as Genre,

// todo: need to parse release date if present
releaseDate: new Date(releaseDateValue as string | number | Date),
Expand All @@ -57,68 +57,69 @@ const processXml = async (document: any, audiusSdk: AudiusSdkType) => {
play_count: true,
remixes: true,
},
description: "",
license: "Attribution ShareAlike CC BY-SA",
};
const artistName = firstValue(trackNode, "ArtistName", "artistName");
description: '',
license: 'Attribution ShareAlike CC BY-SA',
}
const artistName = firstValue(trackNode, 'ArtistName', 'artistName')
const { data: users } = await audiusSdk.users.searchUsers({
query: artistName,
});
})
if (!users || users.length === 0) {
throw new Error(`Could not find user ${artistName}`);
throw new Error(`Could not find user ${artistName}`)
}
const userId = users[0].id;
const userId = users[0].id
const uploadTrackRequest: UploadTrackRequest = {
userId: userId,
// TODO replace with actual img file from upload request
coverArtFile: {
buffer: await fs.promises.readFile("examples/clipper.jpg"),
name: "todo_file_name",
buffer: await fs.promises.readFile('examples/clipper.jpg'),
name: 'todo_file_name',
},
metadata: tt,
onProgress: (progress: any) => console.log("Progress:", progress),
onProgress: (progress: any) => console.log('Progress:', progress),
// TODO replace with actual audio file from upload request
trackFile: {
buffer: await fs.promises.readFile("examples/snare.wav"),
name: "todo_track_file_name",
buffer: await fs.promises.readFile('examples/snare.wav'),
name: 'todo_track_file_name',
},
};
console.log("uploading track...");
const result = await audiusSdk.tracks.uploadTrack(uploadTrackRequest);
console.log(result);
}
console.log('uploading track...')
const result = await audiusSdk.tracks.uploadTrack(uploadTrackRequest)
console.log(result)
}

// todo
// extract Release
// for (const releaseNode of queryAll(document, "Release", "release")) {
// }
};
}


export const postUploadXml = (dbService: any, audiusSdk: AudiusSdkType) => (req: Request, res: Response) => {
upload.single('file')(req, res, async (err: any) => {
if (err) {
return res.status(500).json({ error: err.message });
}
export const postUploadXml =
(dbService: any, audiusSdk: AudiusSdkType) =>
(req: Request, res: Response) => {
upload.single('file')(req, res, async (err: any) => {
if (err) {
return res.status(500).json({ error: err.message })
}

if (!req.file) {
return res.status(400).json({ error: 'No file uploaded.' });
}
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded.' })
}

try {
const filePath = req.file.path;
const xmlText = await fs.promises.readFile(filePath);
const document = new DOMParser().parseFromString(
xmlText.toString(),
"text/xml",
);
await processXml(document, audiusSdk);
try {
const filePath = req.file.path
const xmlText = await fs.promises.readFile(filePath)
const document = new DOMParser().parseFromString(
xmlText.toString(),
'text/xml'
)
await processXml(document, audiusSdk)

// TODO: Persist the upload in DB
// TODO: Persist the upload in DB

res.json({ message: 'File uploaded and processed successfully.' });
} catch (err: any) {
return res.status(500).json({ error: err });
}
});
};
res.json({ message: 'File uploaded and processed successfully.' })
} catch (err: any) {
return res.status(500).json({ error: err })
}
})
}
17 changes: 11 additions & 6 deletions packages/ddex/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import app from './app';
import dotenv from 'dotenv';
import dotenv from 'dotenv'
import path from 'path'

dotenv.config();
const port = process.env.DDEX_PORT || 8926;
// Load env vars based on NODE_ENV
const envFile = process.env.NODE_ENV === 'stage' ? '.env.stage' : '.env'
dotenv.config({ path: path.resolve(process.cwd(), envFile) })

import app from './app'

const port = process.env.DDEX_PORT || 8926

app.listen(port, () => {
console.log(`[server]: Server is running at http://localhost:${port}`);
});
console.log(`[server]: Server is running at http://localhost:${port}`)
})
10 changes: 5 additions & 5 deletions packages/ddex/src/services/dbService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import postgres from 'postgres';
import postgres from 'postgres'

export const createDbService = (dbUrl: string) => {
const sql = postgres(dbUrl);
const sql = postgres(dbUrl)

return {
sql
};
};
sql,
}
}
12 changes: 6 additions & 6 deletions packages/ddex/src/services/scheduledReleaseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

// TODO: Use the right SDK type
export const createScheduledReleaseService = (sdk: any) => {
const queue = []; // TODO: Use https://github.com/mcollina/fastq
const queue = [] // TODO: Use https://github.com/mcollina/fastq

const addTracksToQueue = async () => {
// TODO: Scan dbService for tracks and push them onto the queue
};
}

const upload = async (trackInfo: any) => {
// TODO: sdk.uploadTrack(...)
};
}

return {
addTracksToQueue,
upload
};
};
upload,
}
}
Loading

0 comments on commit 6aada56

Please sign in to comment.