|
|
|
|
|
|
|
|
|
|
|
const axios = require("axios"); |
|
|
const cheerio = require("cheerio"); |
|
|
|
|
|
class NCS { |
|
|
constructor() { |
|
|
this.baseUrl = "https://ncs.io"; |
|
|
|
|
|
this.validGenres = { |
|
|
"31": "Alternative Dance", "33": "Alternative Pop", "23": "Ambient", "34": "Anti-Pop", |
|
|
"1": "Bass", "18": "Bass House", "26": "Brazilian Phonk", "27": "Breakbeat", |
|
|
"2": "Chill", "24": "Chill Bass", "35": "Chill Pop", "85": "Colour Bass", |
|
|
"65": "Complextro", "36": "Dance-Pop", "66": "Deep House", "45": "Disco", |
|
|
"46": "Disco House", "3": "Drum & Bass", "4": "Drumstep", "5": "Dubstep", |
|
|
"6": "EDM", "47": "Electro", "48": "Electro House", "7": "Electronic", |
|
|
"39": "Electronic Pop", "83": "Electronic Rock", "17": "Future Bass", "68": "Future Bounce", |
|
|
"8": "Future House", "69": "Future Rave", "57": "Future Trap", "40": "Futurepop", |
|
|
"51": "Garage", "15": "Glitch Hop", "82": "Hardcore", "9": "Hardstyle", |
|
|
"10": "House", "41": "Hyperpop", "11": "Indie Dance", "91": "J-Pop", |
|
|
"84": "Jersey Club", "28": "Jump-Up", "29": "Liquid DnB", "60": "Lofi Hip-Hop", |
|
|
"12": "Melodic Dubstep", "54": "Melodic House", "22": "Midtempo Bass", "30": "Neurofunk", |
|
|
"87": "Nu-Jazz", "16": "Phonk", "86": "Pluggnb", "19": "Pop", |
|
|
"55": "Progressive House", "88": "RnB", "89": "Speed Garage", "73": "Tech House", |
|
|
"80": "Techno", "81": "Trance", "14": "Trap", "74": "Tribal House", |
|
|
"21": "UKG", "90": "Witch House" |
|
|
}; |
|
|
|
|
|
this.validMoods = { |
|
|
"1": "Angry", "33": "angry", "26": "Chasing", "2": "Dark", "35": "dark", |
|
|
"36": "dramatic", "3": "Dreamy", "27": "Eccentric", "28": "Elegant", "6": "energetic", |
|
|
"4": "Epic", "5": "Euphoric", "37": "exciting", "7": "Fear", "29": "Floating", |
|
|
"8": "Funny", "9": "Glamorous", "10": "Gloomy", "11": "Happy", "30": "Heavy", |
|
|
"12": "Hopeful", "13": "Laid Back", "38": "majestic", "14": "Mysterious", "34": "negative", |
|
|
"39": "neutral", "15": "Peaceful", "40": "positive", "32": "powerful", "16": "Quirky", |
|
|
"17": "relaxed", "18": "Restless", "19": "romantic", "20": "sad", "21": "scary", |
|
|
"31": "Sentimental", "22": "Sexy", "23": "Suspense", "41": "tense", "24": "Weird" |
|
|
}; |
|
|
} |
|
|
|
|
|
validateParam(type, value) { |
|
|
const validList = type === "genre" ? this.validGenres : this.validMoods; |
|
|
return value && validList[value] ? String(value) : ""; |
|
|
} |
|
|
|
|
|
async searchTracks(query, genre = "", mood = "") { |
|
|
try { |
|
|
const validGenre = this.validateParam("genre", genre); |
|
|
const validMood = this.validateParam("mood", mood); |
|
|
const url = `${this.baseUrl}/music-search?q=${encodeURIComponent(query)}&genre=${validGenre}&mood=${validMood}`; |
|
|
|
|
|
const { data } = await axios.get(url, { |
|
|
headers: { |
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" |
|
|
} |
|
|
}); |
|
|
|
|
|
const $ = cheerio.load(data); |
|
|
const tracks = []; |
|
|
|
|
|
$("table.tablesorter tbody tr").each((_, el) => { |
|
|
const $row = $(el); |
|
|
const $play = $row.find(".player-play"); |
|
|
|
|
|
const tid = $play.attr("data-tid") || ""; |
|
|
const title = $play.attr("data-track") || ""; |
|
|
const artist = $play.attr("data-artistraw") || ""; |
|
|
const versions = $play.attr("data-versions") || ""; |
|
|
const genre = $play.attr("data-genre") || ""; |
|
|
const previewUrl = $play.attr("data-url") || ""; |
|
|
const previewStart = $play.attr("data-preview") || "0"; |
|
|
const image = $row.find("td img[alt]").attr("src") || ""; |
|
|
|
|
|
const genres = []; |
|
|
const moods = []; |
|
|
|
|
|
$row.find("td:nth-child(5) a.tag").each((i, tag) => { |
|
|
const $tag = $(tag); |
|
|
const text = $tag.text().trim(); |
|
|
const href = $tag.attr("href") || ""; |
|
|
if (href.includes("genre=")) genres.push(text); |
|
|
else if (href.includes("mood=")) moods.push(text); |
|
|
}); |
|
|
|
|
|
const releaseDate = $row.find("td:nth-child(6)").text().trim(); |
|
|
|
|
|
if (tid && title) { |
|
|
tracks.push({ |
|
|
tid, |
|
|
title, |
|
|
artist, |
|
|
versions, |
|
|
genre, |
|
|
image, |
|
|
genres, |
|
|
moods, |
|
|
previewUrl, |
|
|
previewStart, |
|
|
releaseDate |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
return tracks; |
|
|
} catch (err) { |
|
|
console.error("Error while searching NCS:", err.message); |
|
|
return []; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
const ncs = new NCS(); |
|
|
|
|
|
const handler = async (req, res) => { |
|
|
try { |
|
|
const { text, genre, mood } = req.query; |
|
|
|
|
|
if (!text) { |
|
|
return res.status(400).json({ |
|
|
success: false, |
|
|
error: 'Missing required parameter: text' |
|
|
}); |
|
|
} |
|
|
|
|
|
const results = await ncs.searchTracks(text, genre || "", mood || ""); |
|
|
|
|
|
res.json({ |
|
|
author: 'Herza', |
|
|
success: true, |
|
|
data: results |
|
|
}); |
|
|
|
|
|
} catch (error) { |
|
|
res.status(500).json({ |
|
|
success: false, |
|
|
error: error.message |
|
|
}); |
|
|
} |
|
|
}; |
|
|
|
|
|
module.exports = { |
|
|
name: 'NCS Music Search', |
|
|
description: 'Search NoCopyrightSounds tracks by keyword with optional genre and mood filters', |
|
|
type: 'GET', |
|
|
routes: ['api/search/ncs'], |
|
|
tags: ['search', 'music', 'ncs', 'tools'], |
|
|
parameters: ['text', 'genre (optional)', 'mood (optional)'], |
|
|
enabled: true, |
|
|
main: ['Search'], |
|
|
handler |
|
|
}; |