import { Post, ProviderContext } from "../types"; export const getPosts = async function ({ filter, page, signal, providerContext, }: { filter: string; page: number; providerValue: string; signal: AbortSignal; providerContext: ProviderContext; }): Promise { const { axios, cheerio } = providerContext; const baseUrl = "https://a.111477.xyz"; if (page > 1) { return []; } const url = `${baseUrl}${filter}`; const result = await posts({ baseUrl, url, signal, axios, cheerio }); return result.slice(0, 50); // Limit only for getPosts }; export const getSearchPosts = async function ({ searchQuery, page, signal, providerContext, }: { searchQuery: string; page: number; providerValue: string; signal: AbortSignal; providerContext: ProviderContext; }): Promise { const { axios, cheerio } = providerContext; const baseUrl = "https://a.111477.xyz"; if (page > 1) { return []; } // Search through both movies and TV shows directories const moviesPosts = await posts({ baseUrl, url: `${baseUrl}/movies/`, signal, axios, cheerio, }); const tvsPosts = await posts({ baseUrl, url: `${baseUrl}/tvs/`, signal, axios, cheerio, }); // Combine all posts const allPosts = [...moviesPosts, ...tvsPosts]; // Filter posts based on search query with improved matching const filteredPosts = allPosts.filter((post) => { const title = post.title.toLowerCase(); const query = searchQuery.toLowerCase(); // Direct substring match if (title.includes(query)) { return true; } // Word boundary matching const queryWords = query.split(/\s+/).filter((word) => word.length > 0); const titleWords = title .split(/[\s\-\.\(\)\[\]]+/) .filter((word) => word.length > 0); // Check if all query words are found in title words const allWordsMatch = queryWords.every((queryWord) => titleWords.some((titleWord) => titleWord.includes(queryWord)) ); if (allWordsMatch) { return true; } // Fuzzy matching for single word queries if (queryWords.length === 1) { const queryWord = queryWords[0]; if (queryWord.length >= 3) { // Check if any title word starts with the query const startsWithMatch = titleWords.some((titleWord) => titleWord.startsWith(queryWord) ); if (startsWithMatch) { return true; } // Levenshtein distance for close matches const hasCloseMatch = titleWords.some((titleWord) => { if (Math.abs(titleWord.length - queryWord.length) > 2) return false; const distance = levenshteinDistance(titleWord, queryWord); return distance <= Math.max(1, Math.floor(queryWord.length * 0.2)); }); if (hasCloseMatch) { return true; } } } return false; }); return filteredPosts; }; async function posts({ baseUrl, url, signal, axios, cheerio, }: { baseUrl: string; url: string; signal: AbortSignal; axios: ProviderContext["axios"]; cheerio: ProviderContext["cheerio"]; }): Promise { try { const res = await axios.get(url, { signal }); const data = res.data; const $ = cheerio.load(data); const catalog: Post[] = []; // Parse the directory listing $("table tbody tr").each((i, element) => { const $row = $(element); const linkElement = $row.find("td:first-child a"); const title = linkElement.text().trim(); const link = linkElement.attr("href"); // Skip parent directory and files, only get folders if ( title && link && title !== "../" && title !== "Parent Directory" && title.endsWith("/") ) { const cleanTitle = title.replace(/\/$/, ""); // Remove trailing slash const fullLink = url + link; // Generate a placeholder image based on title const imageTitle = cleanTitle.length > 30 ? cleanTitle.slice(0, 30).replace(/\./g, " ") : cleanTitle.replace(/\./g, " "); const image = `https://placehold.jp/23/000000/ffffff/200x400.png?text=${encodeURIComponent( imageTitle )}&css=%7B%22background%22%3A%22%20-webkit-gradient(linear%2C%20left%20bottom%2C%20left%20top%2C%20from(%233f3b3b)%2C%20to(%23000000))%22%2C%22text-transform%22%3A%22%20capitalize%22%7D`; catalog.push({ title: cleanTitle, link: fullLink, image: image, }); } }); // Only limit for regular getPosts, not for search return catalog; } catch (err) { console.error("111477 directory listing error:", err); return []; } } // Levenshtein distance function for fuzzy matching function levenshteinDistance(str1: string, str2: string): number { const matrix = Array(str2.length + 1) .fill(null) .map(() => Array(str1.length + 1).fill(null)); for (let i = 0; i <= str1.length; i++) matrix[0][i] = i; for (let j = 0; j <= str2.length; j++) matrix[j][0] = j; for (let j = 1; j <= str2.length; j++) { for (let i = 1; i <= str1.length; i++) { const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1; matrix[j][i] = Math.min( matrix[j][i - 1] + 1, // deletion matrix[j - 1][i] + 1, // insertion matrix[j - 1][i - 1] + indicator // substitution ); } } return matrix[str2.length][str1.length]; }