| | |
| | |
| |
|
| | 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 |
| | }; |