From b3d8ebbfb3b6fd1f1df43d4d8b47a8413a586aeb Mon Sep 17 00:00:00 2001 From: aka paul <50n50@noreply.localhost> Date: Fri, 28 Nov 2025 19:06:37 +0000 Subject: [PATCH] Update checkmate/checkmate.js --- checkmate/checkmate.js | 157 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 12 deletions(-) diff --git a/checkmate/checkmate.js b/checkmate/checkmate.js index b70eebc..08df472 100644 --- a/checkmate/checkmate.js +++ b/checkmate/checkmate.js @@ -1,6 +1,6 @@ -//Thanks ibro for the TMDB search! +// Credits for Search, Details, and Episodes functions to ibro, with impromements made by me -async function searchResults(keyword) { +async function searchResults(query) { try { let transformedResults = []; @@ -14,29 +14,152 @@ async function searchResults(keyword) { const skipTitleFilter = Object.values(keywordGroups).flat(); - const shouldFilter = !matchesKeyword(keyword, skipTitleFilter); + const shouldFilter = !matchesKeyword(query, skipTitleFilter); - const encodedKeyword = encodeURIComponent(keyword); + const encodedQuery = encodeURIComponent(query); let baseUrl = null; - if (matchesKeyword(keyword, keywordGroups.trending)) { + if (matchesKeyword(query, keywordGroups.trending)) { baseUrl = `https://api.themoviedb.org/3/trending/all/week?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=`; - } else if (matchesKeyword(keyword, keywordGroups.topRatedMovie)) { + } else if (matchesKeyword(query, keywordGroups.topRatedMovie)) { baseUrl = `https://api.themoviedb.org/3/movie/top_rated?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=`; - } else if (matchesKeyword(keyword, keywordGroups.topRatedTV)) { + } else if (matchesKeyword(query, keywordGroups.topRatedTV)) { baseUrl = `https://api.themoviedb.org/3/tv/top_rated?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=`; - } else if (matchesKeyword(keyword, keywordGroups.popularMovie)) { + } else if (matchesKeyword(query, keywordGroups.popularMovie)) { baseUrl = `https://api.themoviedb.org/3/movie/popular?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=`; - } else if (matchesKeyword(keyword, keywordGroups.popularTV)) { + } else if (matchesKeyword(query, keywordGroups.popularTV)) { baseUrl = `https://api.themoviedb.org/3/tv/popular?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=`; } else { - baseUrl = `https://api.themoviedb.org/3/search/multi?api_key=9801b6b0548ad57581d111ea690c85c8&query=${encodedKeyword}&include_adult=false&page=`; + baseUrl = `https://api.themoviedb.org/3/search/multi?api_key=9801b6b0548ad57581d111ea690c85c8&query=${encodedQuery}&include_adult=false&page=`; } + const fuzzyMatch = (query, title) => { + const q = query.toLowerCase().trim(); + const t = title.toLowerCase().trim(); + + if (t === q) return 1000; + + if (t.startsWith(q + ' ') || t.startsWith(q + ':') || t.startsWith(q + '-')) return 950; + + const wordBoundaryRegex = new RegExp(`\\b${q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`); + if (wordBoundaryRegex.test(t)) return 900; + + const qTokens = q.split(/\s+/).filter(token => token.length > 0); + const tTokens = t.split(/[\s\-:]+/).filter(token => token.length > 0); + + const stopwords = new Set(['the', 'a', 'an', 'and', 'or', 'of', 'in', 'on', 'at', 'to', 'for', 'with']); + + let score = 0; + let exactMatches = 0; + let partialMatches = 0; + let significantMatches = 0; + + qTokens.forEach(qToken => { + const isStopword = stopwords.has(qToken); + let bestMatch = 0; + let hasExactMatch = false; + + tTokens.forEach(tToken => { + let matchScore = 0; + + if (tToken === qToken) { + matchScore = isStopword ? 25 : 120; + hasExactMatch = true; + if (!isStopword) significantMatches++; + } + else if (qToken.includes(tToken) && tToken.length >= 3 && qToken.length <= tToken.length + 2) { + matchScore = isStopword ? 8 : 40; + if (!isStopword) significantMatches++; + } + else if (tToken.startsWith(qToken) && qToken.length >= 3) { + matchScore = isStopword ? 12 : 70; + if (!isStopword) significantMatches++; + } + else if (qToken.length >= 4 && tToken.length >= 4) { + const dist = levenshteinDistance(qToken, tToken); + const maxLen = Math.max(qToken.length, tToken.length); + const similarity = 1 - (dist / maxLen); + + if (similarity > 0.8) { + matchScore = Math.floor(similarity * 60); + if (!isStopword) significantMatches++; + } + } + + bestMatch = Math.max(bestMatch, matchScore); + }); + + if (bestMatch > 0) { + score += bestMatch; + if (hasExactMatch) exactMatches++; + else partialMatches++; + } + }); + + const significantTokens = qTokens.filter(t => !stopwords.has(t)).length; + + const requiredMatches = Math.max(1, Math.ceil(significantTokens * 0.8)); + if (significantMatches < requiredMatches) { + return 0; + } + + if (exactMatches + partialMatches >= qTokens.length) { + score += 80; + } + + score += exactMatches * 20; + + const extraWords = tTokens.length - qTokens.length; + if (extraWords > 2) { + score -= (extraWords - 2) * 25; + } + + let orderBonus = 0; + for (let i = 0; i < qTokens.length - 1; i++) { + const currentTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i])); + const nextTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i + 1])); + + if (currentTokenIndex !== -1 && nextTokenIndex !== -1 && currentTokenIndex < nextTokenIndex) { + orderBonus += 15; + } + } + score += orderBonus; + + return Math.max(0, score); + }; + + const levenshteinDistance = (a, b) => { + const matrix = []; + + for (let i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + + for (let j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + for (let i = 1; i <= b.length; i++) { + for (let j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j - 1] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j] + 1 + ); + } + } + } + + return matrix[b.length][a.length]; + }; + let dataResults = []; if (baseUrl) { - const pagePromises = Array.from({ length: 5 }, (_, i) => + const pagePromises = Array.from({ length: 10 }, (_, i) => soraFetch(baseUrl + (i + 1)).then(r => r.json()) ); const pages = await Promise.all(pagePromises); @@ -64,10 +187,20 @@ async function searchResults(keyword) { .filter(Boolean) .filter(result => result.title !== "Overflow") .filter(result => result.title !== "My Marriage Partner Is My Student, a Cocky Troublemaker") - .filter(r => !shouldFilter || r.title.toLowerCase().includes(keyword.toLowerCase())) ); } + if (shouldFilter) { + const scoredResults = transformedResults.map(r => ({ + ...r, + score: fuzzyMatch(query, r.title) + })); + transformedResults = scoredResults + .filter(r => r.score > 50) + .sort((a, b) => b.score - a.score) + .map(({ score, ...rest }) => rest); + } + console.log("Transformed Results: " + JSON.stringify(transformedResults)); return JSON.stringify(transformedResults); } catch (error) {