Skip to content

Commit f264c51

Browse files
committed
add: enhance letrasmus meaning extraction
1 parent 73abdf3 commit f264c51

1 file changed

Lines changed: 142 additions & 18 deletions

File tree

src/meanings/letrasmus.js

Lines changed: 142 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,95 @@ import {
77

88
const SOLR_ENDPOINT = 'https://solr.sscdn.co/letras/m1/'
99

10+
const cleanText = (text) => {
11+
if (!text) return ''
12+
return text
13+
.replace(/\s*\([^)]*\)/g, ' ')
14+
.replace(/\s*\[[^\]]*\]/g, ' ')
15+
.replace(
16+
/\b(official|video|audio|mv|visualizer|live|session|ao vivo|lyric|lyrics|hd|4k|remix|edit|cover|acoustic|instrumental)\b/gi,
17+
' '
18+
)
19+
.replace(/feat\.?/gi, ' ')
20+
.replace(/ft\.?/gi, ' ')
21+
.replace(/[^\w\s]/g, ' ')
22+
.replace(/\s+/g, ' ')
23+
.trim()
24+
}
25+
26+
const buildSearchCandidates = (trackInfo) => {
27+
const candidates = new Set()
28+
const rawTitle = trackInfo?.title || ''
29+
const rawAuthor = trackInfo?.author || ''
30+
31+
const cleanedTitle = cleanText(rawTitle)
32+
const cleanedAuthor = cleanText(rawAuthor)
33+
34+
const pushCandidate = (title, author) => {
35+
const t = cleanText(title)
36+
const a = cleanText(author)
37+
const combined = [t, a].filter(Boolean).join(' ').trim()
38+
if (combined) candidates.add(combined)
39+
}
40+
41+
const rawTitleLower = rawTitle.toLowerCase()
42+
const rawAuthorLower = rawAuthor.toLowerCase()
43+
44+
if (cleanedTitle || cleanedAuthor) {
45+
pushCandidate(cleanedTitle, cleanedAuthor)
46+
}
47+
48+
if (cleanedTitle) candidates.add(cleanedTitle)
49+
50+
const splitTitle = (title, sep) => {
51+
if (!title.includes(sep)) return null
52+
const parts = title.split(sep).map((part) => part.trim())
53+
if (parts.length < 2) return null
54+
return [parts[0], parts.slice(1).join(sep).trim()]
55+
}
56+
57+
const dashSplit = splitTitle(rawTitle, ' - ')
58+
if (dashSplit) {
59+
const [left, right] = dashSplit
60+
const leftClean = cleanText(left)
61+
const rightClean = cleanText(right)
62+
63+
if (rightClean) {
64+
pushCandidate(rightClean, cleanedAuthor || leftClean)
65+
candidates.add(rightClean)
66+
}
67+
68+
if (leftClean && rightClean) {
69+
pushCandidate(rightClean, leftClean)
70+
}
71+
}
72+
73+
const pipeSplit = splitTitle(rawTitle, ' | ')
74+
if (pipeSplit) {
75+
const [left, right] = pipeSplit
76+
const leftClean = cleanText(left)
77+
const rightClean = cleanText(right)
78+
if (leftClean) candidates.add(leftClean)
79+
if (rightClean) candidates.add(rightClean)
80+
if (leftClean && cleanedAuthor) pushCandidate(leftClean, cleanedAuthor)
81+
}
82+
83+
if (rawAuthorLower && rawTitleLower.includes(rawAuthorLower)) {
84+
const stripped = cleanText(rawTitle.replace(new RegExp(rawAuthor, 'ig'), ''))
85+
if (stripped) {
86+
pushCandidate(stripped, cleanedAuthor)
87+
candidates.add(stripped)
88+
}
89+
}
90+
91+
if (cleanedAuthor) {
92+
pushCandidate(cleanedTitle, cleanedAuthor)
93+
candidates.add(cleanedAuthor)
94+
}
95+
96+
return Array.from(candidates)
97+
}
98+
1099
const parseJsonp = (body) => {
11100
if (!body) return null
12101
const trimmed = body.trim()
@@ -139,30 +228,65 @@ export default class LetrasMusMeaning {
139228

140229
async getMeaning(trackInfo, language) {
141230
try {
142-
let letrasTrack = trackInfo
143-
if (trackInfo.sourceName !== 'letrasmus') {
144-
const query = `${trackInfo.title} ${trackInfo.author}`.trim()
145-
const results = await searchLetras(query, 10)
231+
let candidates = []
232+
if (trackInfo.sourceName === 'letrasmus') {
233+
candidates = [{ info: trackInfo }]
234+
} else {
235+
const searchCandidates = buildSearchCandidates(trackInfo)
236+
let results = []
237+
238+
for (const query of searchCandidates) {
239+
results = await searchLetras(query, 12)
240+
if (results.length) break
241+
}
242+
146243
if (results.length) {
147-
const best = getBestMatch(results, trackInfo)
148-
if (best?.info) {
149-
letrasTrack = best.info
244+
const matchTarget = {
245+
...trackInfo,
246+
title: cleanText(trackInfo.title),
247+
author: cleanText(trackInfo.author)
248+
}
249+
const best = getBestMatch(results, matchTarget)
250+
const ordered = []
251+
if (best?.info) ordered.push(best)
252+
for (const item of results) {
253+
if (!best || item.info.uri !== best.info?.uri) ordered.push(item)
150254
}
255+
candidates = ordered
151256
}
152257
}
153258

154-
if (!letrasTrack?.uri || letrasTrack.sourceName !== 'letrasmus') {
259+
if (!candidates.length) {
155260
return { loadType: 'empty', data: {} }
156261
}
157262

158-
const baseUrl = letrasTrack.uri.endsWith('/')
159-
? letrasTrack.uri
160-
: `${letrasTrack.uri}/`
161-
const meaningUrl = `${baseUrl}significado.html`
162-
const { body, statusCode, error } = await http1makeRequest(meaningUrl, {
163-
method: 'GET'
164-
})
165-
if (error || statusCode !== 200 || !body) {
263+
let body = null
264+
let meaningUrl = null
265+
let resolvedTrack = null
266+
267+
for (const candidate of candidates) {
268+
const letrasTrack = candidate.info
269+
if (!letrasTrack?.uri || letrasTrack.sourceName !== 'letrasmus') continue
270+
271+
const baseUrl = letrasTrack.uri.endsWith('/')
272+
? letrasTrack.uri
273+
: `${letrasTrack.uri}/`
274+
const url = `${baseUrl}significado.html`
275+
const { body: fetchedBody, statusCode, error } =
276+
await http1makeRequest(url, { method: 'GET' })
277+
278+
if (error || statusCode !== 200 || !fetchedBody) continue
279+
280+
const meaningCheck = extractMeaning(fetchedBody)
281+
if (!meaningCheck.body.length) continue
282+
283+
body = fetchedBody
284+
meaningUrl = url
285+
resolvedTrack = letrasTrack
286+
break
287+
}
288+
289+
if (!body || !meaningUrl || !resolvedTrack) {
166290
return { loadType: 'empty', data: {} }
167291
}
168292

@@ -218,8 +342,8 @@ export default class LetrasMusMeaning {
218342
reviewedBy: null
219343
},
220344
song: {
221-
title: omq?.Name || letrasTrack.title || null,
222-
artist: omq?.Artist || letrasTrack.author || null,
345+
title: omq?.Name || resolvedTrack.title || null,
346+
artist: omq?.Artist || resolvedTrack.author || null,
223347
youtubeId: omq?.YoutubeID || null,
224348
letrasId: omq?.ID || null,
225349
artworkUrl: ogImage || null

0 commit comments

Comments
 (0)