feat: Improve Anime-Sama search with fuzzy matching
Replace direct URL conversion with official Anime-Sama search API that handles typos, partial matches, and returns multiple results with cover images. Changes: - Use /template-php/defaut/fetch.php API endpoint for search - Parse HTML search results to extract title, URL, and cover image - Return multiple results instead of single direct match - Support for fuzzy matching (e.g., "hell paradise" finds "Hell's Paradise") Examples: - "hell paradise" → finds "Hell's Paradise" (handles missing 's') - "one pie" → finds "One Piece" (handles incomplete words) - "dragon" → returns 5 Dragon Ball series with cover images Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -350,42 +350,68 @@ class AnimeSamaDownloader(BaseDownloader):
|
||||
"""
|
||||
Search for anime on anime-sama
|
||||
Returns list of anime with title, url, and cover image
|
||||
Uses the official Anime-Sama search API which handles typos and fuzzy matching
|
||||
"""
|
||||
try:
|
||||
# Update domains before searching to ensure we have the current domain
|
||||
await self.update_domains()
|
||||
|
||||
import time
|
||||
from html import unescape
|
||||
start = time.time()
|
||||
print(f"[ANIME-SAMA] Searching for '{query}' ({lang})...")
|
||||
|
||||
# Use the current domain from anime-sama.pw
|
||||
current_domain = await self.get_current_domain()
|
||||
|
||||
# Convert query to URL format (lowercase, replace spaces with hyphens)
|
||||
query_formatted = query.lower().replace(' ', '-').replace("'", '').replace(':', '')
|
||||
search_url = f"https://{current_domain}/catalogue/{query_formatted}/saison1/{lang}/"
|
||||
# Use the official search API endpoint
|
||||
search_api_url = f"https://{current_domain}/template-php/defaut/fetch.php"
|
||||
|
||||
response = await self.client.get(search_url, follow_redirects=True)
|
||||
# Make POST request to search API
|
||||
response = await self.client.post(
|
||||
search_api_url,
|
||||
data={'query': query},
|
||||
headers={'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
)
|
||||
|
||||
elapsed = time.time() - start
|
||||
print(f"[ANIME-SAMA] Got response {response.status_code} in {elapsed:.2f}s")
|
||||
print(f"[ANIME-SAMA] Got search response in {elapsed:.2f}s")
|
||||
|
||||
if response.status_code == 200:
|
||||
# Check if it's a valid anime page by looking for episode selector
|
||||
if 'selectEpisodes' in response.text or 'episodes.js' in response.text:
|
||||
print(f"[ANIME-SAMA] Found anime at {str(response.url)}")
|
||||
return [{
|
||||
'title': query,
|
||||
'url': str(response.url),
|
||||
'type': 'direct'
|
||||
}]
|
||||
if response.status_code == 200 and response.text.strip():
|
||||
# Parse HTML results
|
||||
soup = BeautifulSoup(response.text, 'lxml')
|
||||
results = []
|
||||
|
||||
print(f"[ANIME-SAMA] No anime found (status: {response.status_code})")
|
||||
# Extract all search result links
|
||||
for link in soup.find_all('a', class_='asn-search-result'):
|
||||
href = link.get('href', '')
|
||||
title_elem = link.find('h3', class_='asn-search-result-title')
|
||||
img_elem = link.find('img', class_='asn-search-result-img')
|
||||
|
||||
title = unescape(title_elem.get_text()) if title_elem else "Unknown"
|
||||
cover_image = img_elem.get('src', '') if img_elem else None
|
||||
|
||||
# Add language parameter to URL
|
||||
if '/saison1/' not in href:
|
||||
href = href.rstrip('/') + f'/saison1/{lang}/'
|
||||
|
||||
results.append({
|
||||
'title': title,
|
||||
'url': href,
|
||||
'cover_image': cover_image,
|
||||
'type': 'search_result'
|
||||
})
|
||||
|
||||
print(f"[ANIME-SAMA] Found {len(results)} results")
|
||||
return results
|
||||
|
||||
print(f"[ANIME-SAMA] No results found")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ANIME-SAMA] Error: {str(e)}")
|
||||
print(f"[ANIME-SAMA] Search error: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return []
|
||||
|
||||
async def get_episodes(self, anime_url: str, lang: str = "vostfr") -> list[dict]:
|
||||
|
||||
Reference in New Issue
Block a user