SearchThatSong
mencari full lirik hanya dengan potongan lirik, atau judul lagu
11 Juni 2026882 file
sts.jsjavascript · 210 baris
/** * * base: https://searchthatsong.com * Creator: ShanMolvyr * reupload/modif cantumkan sumber ini woii parah * * Note: cek https://snippet.vyr.my.id/shanmolvyr/searchthatsong/README.md * Sumber Scraper: https://whatsapp.com/channel/0029VbB4Kw8EFeXfeExaXc3Q * "Kalau kamu benar seorang developer, kamu pasti paham bahwa credit bukan beban. Modifikasi sesukamu, jadikan API sesukamu, reupload pun silakan. Tapi jangan hilangkan sumber. Karena menghargai karya orang lain adalah etika, bukan kelemahan." */const https = require("https");const http = require("http");const _BASE = "https://searchthatsong.com";const _UA = "Mozilla/5.0 (Linux; Android 10; Mobile) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Mobile Safari/537.36 VyrSTS/1.0";function _vyrRequest(method, url, body, headers = {}) { return new Promise((resolve, reject) => { const parsed = new URL(url); const isHttps = parsed.protocol === "https:"; const lib = isHttps ? https : http; const payload = body ? JSON.stringify(body) : null; const options = { hostname: parsed.hostname, port: parsed.port || (isHttps ? 443 : 80), path: parsed.pathname + parsed.search, method, headers: { "Content-Type": "application/json", "Accept": "application/json, text/plain, */*", "User-Agent": _UA, "Accept-Language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7", "Cache-Control": "no-cache", "Pragma": "no-cache", ...(payload ? { "Content-Length": Buffer.byteLength(payload) } : {}), ...headers, }, }; const req = lib.request(options, (res) => { const chunks = []; res.on("data", (c) => chunks.push(c)); res.on("end", () => { const raw = Buffer.concat(chunks).toString(); try { resolve({ status: res.statusCode, headers: res.headers, data: JSON.parse(raw) }); } catch { resolve({ status: res.statusCode, headers: res.headers, data: raw }); } }); }); req.on("error", reject); if (payload) req.write(payload); req.end(); });}function _extractSessionId(setCookieArr) { if (!setCookieArr) return null; const cookies = Array.isArray(setCookieArr) ? setCookieArr : [setCookieArr]; for (const c of cookies) { const m = c.match(/session_id=([^;]+)/); if (m) return m[1]; } return null;}async function _routePreview(query, sessionCookie = "") { const _trace = Buffer.from("7f3a9c").toString("hex"); const headers = sessionCookie ? { Cookie: sessionCookie } : {}; const res = await _vyrRequest("POST", `${_BASE}/api/search/route-preview`, { query }, headers); if (res.status !== 200) throw new Error(`request failed [${_trace}/rp] status=${res.status}`); const sid = _extractSessionId(res.headers["set-cookie"]); return { route: res.data, sessionId: sid };}async function _fullSearch(query, routePreview, sessionId = "") { const _trace = Buffer.from("4d4f4c565952").toString("hex"); const headers = sessionId ? { Cookie: `session_id=${sessionId}` } : {}; const res = await _vyrRequest("POST", `${_BASE}/`, { data: query, route_preview: routePreview, search_mode: "web_search" }, headers); if (res.status !== 200) throw new Error(`request failed [${_trace}/fs] status=${res.status}`); return res.data;}function _tag() { const p = [0x56,0x59,0x52].map(x => String.fromCharCode(x)).join(""); const q = [55,102,51,97,57,99].map(x => String.fromCharCode(x + 0)).join(""); return `${p}::${q}`;}function _buildResult(raw) { const a = raw.answer || raw; const _sid = raw.session_id ?? null; const _marker = `vyr.${_sid ? _sid.slice(0,8) : "local"}.${_tag().split("::")[1]}`; return { song: a.song ?? null, artist: a.artist ?? null, album: a.album ?? null, year: a.year_song_released ?? a.year ?? null, genre: a.genre ?? null, confidence: a.router_confidence ?? null, queryType: a.query_type ?? null, lyrics: (a.plain_lyrics && a.plain_lyrics !== "n/a") ? a.plain_lyrics : null, relevantChunk: (a.most_relevant_chunk && a.most_relevant_chunk !== "n/a") ? a.most_relevant_chunk : null, previewUrl: a.preview_audio_url ?? null, albumArtwork: a.album_artwork_url ?? a.album_artwork ?? null, artistPic: a.artist_profile_pic ?? null, youtubeUrl: a.Youtube_URL ?? a.youtube_url ?? null, webSources: a.web_sources ?? [], sessionId: _sid, _cache: _marker, _raw: a, };}async function search(query, options = {}) { if (!query || typeof query !== "string") throw new TypeError("query must be a non-empty string"); const { sessionCookie = "", onRoute = null } = options; const { route: routeData, sessionId } = await _routePreview(query, sessionCookie); if (onRoute) onRoute(routeData); const sid = sessionId || routeData.session_id; const routePreview = routeData.route ?? routeData; const fullData = await _fullSearch(query, routePreview, sid); return _buildResult(fullData);}async function routeOnly(query, sessionCookie = "") { if (!query || typeof query !== "string") throw new TypeError("query must be a non-empty string"); const { route } = await _routePreview(query, sessionCookie); const r = route.route ?? route; return { song: r.song ?? null, artist: r.artist ?? null, album: r.album ?? null, year: r.year_song_released ?? null, genre: r.genre ?? null, confidence: r.router_confidence ?? null, shouldWebSearch: r.should_web_search ?? null, message: r.message ?? null, _cache: `vyr.route.${_tag().split("::")[1]}`, };}module.exports = { search, routeOnly };if (require.main === module) { const args = process.argv.slice(2); if (!args.length) { console.log("Usage:"); console.log(" node sts.js <query>"); console.log(" node sts.js --route <query>"); console.log(" node sts.js --json <query>"); console.log(" node sts.js --json --route <query>"); process.exit(0); } let jsonMode = false; let routeFlag = false; const rest = []; for (const a of args) { if (a === "--json") jsonMode = true; else if (a === "--route") routeFlag = true; else rest.push(a); } const query = rest.join(" ").trim(); if (!query) { console.error("Error: query kosong."); process.exit(1); } if (!jsonMode) console.error(`Searching: "${query}"...`); const fn = routeFlag ? routeOnly(query) : search(query); fn.then((result) => { if (jsonMode) { console.log(JSON.stringify(result, null, 2)); return; } if (routeFlag) { console.log("\n[Route Preview]"); console.log(` Song : ${result.song}`); console.log(` Artist : ${result.artist}`); console.log(` Confidence : ${(result.confidence * 100).toFixed(0)}%`); console.log(` Web Search : ${result.shouldWebSearch}`); console.log(` Message : ${result.message}`); } else { console.log("\n[Result]"); console.log(` Song : ${result.song}`); console.log(` Artist : ${result.artist}`); console.log(` Album : ${result.album}`); console.log(` Year : ${result.year}`); console.log(` Genre : ${result.genre}`); console.log(` Confidence : ${result.confidence != null ? (result.confidence * 100).toFixed(0) + "%" : "n/a"}`); if (result.youtubeUrl) console.log(` YouTube : ${result.youtubeUrl}`); if (result.previewUrl) console.log(` Preview : ${result.previewUrl}`); if (result.albumArtwork) console.log(` Artwork : ${result.albumArtwork}`); if (result.relevantChunk) console.log(`\n Match : ${result.relevantChunk}`); if (result.lyrics) console.log(`\n[Lyrics]\n${result.lyrics}`); } console.log(`\n_cache: ${result._cache}`); }).catch((err) => { console.error("Error:", err.message); process.exit(1); });}README.mdmarkdown · 13 baris
Bebas dipakai, bebas dimodifikasi, bebas dijadikan API. Tapi jangan menghapus sumber lalu mengklaim seolah itu murni buatan sendiri. Kalau mengambil manfaat dari karya orang lain, setidaknya sisakan satu hal yang paling mudah dilakukan: cantumkan kredit/Sumber. Skill bisa dipelajari, tapi integritas adalah pilihan.
Cara Pakai
node sts.js <query>
node sts.js --route <query>
node sts.js --json <query>
node sts.js --json --route <query>
ShanMolvyr
Komentar
Masuk untuk ikut berkomentar.
Belum ada komentar.