import express from "express"; import cookieParser from "cookie-parser"; import cookieSession from "cookie-session"; import passport from "passport"; import {Strategy as SpotifyStrategy} from "passport-spotify"; import bodyParser from "body-parser"; import morgan from "morgan"; import stringSim from "string-similarity"; import _ from "lodash"; import SpotifyWebApi from "spotify-web-api-node"; const doubleMetaphone = require('double-metaphone'); const configData = require('./config.json'); import Discogs from "./apis/discogs"; import TimeLyrics, {Song} from "./apis/music163"; const functions = require('./functions'); const authRouter = require('./routers/authentication'); const pageRouter = require('./routers/pages'); const playbackRouter = require('./routers/playback'); const genreRouter = require('./routers/genre'); const PORT = 9000; const appId = configData.CLIENT_ID; const appSecret = configData.CLIENT_SECRET; const loginPage = '/auth/login'; passport.serializeUser((user, done) => { done(null, user); }); passport.deserializeUser(function (obj, done) { done(null, obj); }); passport.use( new SpotifyStrategy( { clientID: appId, clientSecret: appSecret, callbackURL: 'http://localhost:9000/auth/callback', }, function (accessToken, refreshToken, expires_in, profile, done) { process.nextTick(function () { let newProfile = { ...profile, accessToken: accessToken, refreshToken: refreshToken, expiresOn: (new Date((new Date()).getTime() + (expires_in - 60) * 1000)).toISOString() } return done(null, newProfile); }); } ) ); const app = express(); app.use(morgan('dev')); app.set('view engine', 'ejs'); app.set('views', "public/views"); app.use(cookieParser('wd3pwvDDxiygaxI9stLCjofeKpX9vO9d')); app.use(cookieSession({ name: 'session', keys: ['wd3pwvDDxiygaxI9stLCjofeKpX9vO9d', 'RCRMPAxTCLxeHlQaYeW4GKzvVKV8lgjB'] })); app.use(passport.initialize()); app.use(passport.session()); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(express.static('public')); app.use('/auth', authRouter); app.use('/user', pageRouter); app.use('/playback', playbackRouter); app.use('/genre', genreRouter); app.get('/', (req, res) => { if (req.user == null) req.session.returnTo = '/'; // Prevent Caching res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1 res.setHeader("Pragma", "no-cache"); // HTTP 1.0 res.setHeader("Expires", "0"); // Proxies res.render('main.ejs', { req: req }); }); app.get('/discogs/search', async (req, res) => { res.render('search.ejs'); }); app.get('/discogs', async (req, res) => { Discogs.search(req.query.artist as string, req.query.track as string, req.query.type as string, (err, data, rateLimit) => { res.send(data); }); }); app.get('/163', async (req, res) => { const title = req.query.title as string; const artist = req.query.artist as string; let results = (await TimeLyrics.search(title, artist))['result']['songs']; for (const result of results) { if (result.name.toLowerCase() === title.toLowerCase()) { if (result.artists[0].name.toLowerCase() === artist.toLowerCase()) { return res.send(await TimeLyrics.lyrics(result.id)); } } } res.send(results); }); app.post('/163/lyrics', async (req, res) => { function processResults(results: Song[]) { let score = -1 let currentResult; if (results == null) return false; for (const result of results) { let name = functions.simplifyTitle(result.name); let nameAdj = functions.adjustTitle(name); let resultAlbum = functions.simplifyTitle(result.album.name); let resultAlbumAdj = functions.simplifyTitle(resultAlbum); let resultArtists: string[] = result.artists.map(x => functions.simplifyTitle(x.name)); // String similarities let trackSim = 100 * stringSim.compareTwoStrings(name, track); // Between Song and Song let albumSim = 100 * stringSim.compareTwoStrings(resultAlbum, album); // Between Album and Album let trackAdjSim = 100 * stringSim.compareTwoStrings(nameAdj, trackAdj); // Between Song and Song Adj. let albumAdjSim = 100 * stringSim.compareTwoStrings(resultAlbumAdj, albumAdj); // Between Album and Album Adj. // Same as in /genre let artistSims = [].concat.apply([], resultArtists.map(x => artists.map(y => 100 * stringSim.compareTwoStrings(x, y)))); artistSims = artistSims.reduce((a: number, b: number) => a + b, 0) / ((artists.length + resultArtists.length) / 2); // Lower score to catch remixes, covers and stuff if (name.match(/\([^)]*\)/g)) { trackSim *= 0.8; albumSim *= 0.8; } const accuracyThresh = 50; if ((trackSim + albumSim + artistSims) / 3 >= accuracyThresh && score < (trackSim + albumSim + artistSims) / 3) { score = (trackSim + albumSim + artistSims) / 3; currentResult = result; } else if ((trackAdjSim + albumAdjSim + artistSims) / 3 >= accuracyThresh && score < 0.8 * (trackAdjSim + albumAdjSim + artistSims) / 3) { score = 0.95 * (trackSim + albumSim + artistSims) / 3; currentResult = result; } else if (trackSim >= accuracyThresh && score < trackSim) { // Track title matches result score = trackSim; currentResult = result; } else if (albumSim >= accuracyThresh && score < albumSim * 0.6) { // Album title matches result score = albumSim * 0.6; currentResult = result; } else if (trackAdjSim >= accuracyThresh && score < trackAdjSim * 0.5) { // Adjusted: Track title matches result score = trackAdjSim * 0.5; currentResult = result; } else if (albumAdjSim >= accuracyThresh && score < albumAdjSim * 0.45) { // Adjusted: Album title matches result score = albumAdjSim * 0.45; currentResult = result; } else if (artistSims >= accuracyThresh && score < artistSims * 0.5) { score = artistSims * 0.5; currentResult = result; } // console.log(`${result.name}`) // console.log(`\tScore: ${score}`); // console.log(`\tAll: ${(trackSim + albumSim + artistSims) / 3}`) // console.log(`\tAll Adj: ${(trackAdjSim + albumAdjSim + artistSims) / 3}`) // console.log(`\tTrack: ${name} - ${track}: ${trackSim}`); // console.log(`\tAlbum: ${resultAlbum} - ${album}: ${albumSim}`); // console.log(`\tTrack Adj: ${nameAdj} - ${trackAdj}: ${trackAdjSim}`); // console.log(`\tAlbum Adj: ${resultAlbumAdj} - ${albumAdj}: ${albumAdjSim}`); // console.log(`\tArtists: ${resultArtists} - ${artists}: ${artistSims}`); } if (currentResult != null) { // console.log(currentResult); return { id: currentResult.id, confidence: score } } return false; } if (!req.body.artists || !Array.isArray(req.body.artists) || !req.body || !req.body.name || !req.body.album || !req.body.album.name) { res.contentType("application/json"); return res.status(400).send('{"message": "Invalid body. Please provide Spotify Track object"}') } // Body should be a spotify track object, just like /genre const body = req.body as SpotifyApi.TrackObjectFull; const artist = functions.simplifyTitle(body.artists[0].name); const artists = body.artists.map(x => functions.simplifyTitle(x.name)); const track = functions.simplifyTitle(body.name); const album = functions.simplifyTitle(body.album.name); const trackAdj = functions.adjustTitle(track); const albumAdj = functions.adjustTitle(album); let results = (await TimeLyrics.search(trackAdj, artist))['result']['songs']; let result = processResults(results); if (!result) { return res.status(404).send('Song not found in search'); } let lyrics = (await TimeLyrics.lyrics(result.id)).lrc; if (!lyrics) return res.sendStatus(404); let lines = lyrics['lyric'].split(/\r?\n/); res.send(lines); }); app.get('/time-sync', async (req, res) => { res.render('time-sync.ejs'); }); app.post('/correlate', async (req, res) => { let genius = req.body['genius']; genius = genius .replace(/\[.*\]/g, '') // Remove Verse Indicators .replace('-', ' ') // Replace dashed words with seperate words .replace(/[^\w\s]|_/g, "") // Remove non-alphanumeric characters .replace(/\s+/g, " ").trim() // Remove extra spaces .split(' '); // Extract words let lyrics163 = JSON.parse(req.body['163']).join(' '); lyrics163 = lyrics163 .replace(/\[(\d+):(\d+)\.(\d+)\]/g, '') // Remove TimeStamp Indicators .replace('-', ' ') // Replace dashed words with seperate words .replace(/[^\w\s]|_/g, "") // Remove non-alphanumeric characters .replace(/\s+/g, " ").trim() // Remove extra spaces .split(' '); // Extract words console.log(lyrics163) console.log(genius) for (let i = 0; i < genius.length; i++) { genius[i] = { word: genius[i], metaphone: doubleMetaphone(genius[i]) }; } for (let i = 0; i < lyrics163.length; i++) { lyrics163[i] = { word: lyrics163[i], metaphone: doubleMetaphone(lyrics163[i]) }; } res.send({ genius, lyrics163 }); }); app.listen(PORT); console.log("App listening on port " + PORT); module.exports = app;