Phase 3: HTMX & Alpine.js integration, router refactoring, and UI modernization
- Modernized the frontend with HTMX for server-driven UI and Alpine.js for client state. - Refactored anime, player, and recommendation logic into modular routers. - Updated README.md to reflect the latest project state and technologies (v2.4). - Added Plyr.io for an improved streaming experience. - Improved project structure with componentized templates. - Added Playwright and Vitest configuration for frontend testing.
This commit is contained in:
@@ -68,38 +68,66 @@ class FS7Downloader(BaseSeriesSite):
|
||||
soup = BeautifulSoup(html, 'lxml')
|
||||
results = []
|
||||
|
||||
# Look for series items (FS7 has both films and series in search results)
|
||||
# We filter for /s-tv/ URLs ending with .html (actual series/season pages)
|
||||
items = soup.find_all('a', href=re.compile(r'/s-tv/\d+-.+\.html'))
|
||||
# Look for series items
|
||||
# FS7 usually structure: <div class="movie-item">...<a href="..."><img src="..."></a>...</div>
|
||||
# Or directly <a> tags with images
|
||||
items = soup.find_all('div', class_='movie-item')
|
||||
if not items:
|
||||
# Fallback to the previous method if layout is different
|
||||
items = soup.find_all('a', href=re.compile(r'/s-tv/\d+-.+\.html'))
|
||||
|
||||
for item in items[:20]: # Limit to 20 results
|
||||
url = item.get('href', '')
|
||||
for item in items[:24]: # Limit to 24 results
|
||||
# Find the link and image within the item or the item itself
|
||||
if item.name == 'a':
|
||||
link_elem = item
|
||||
else:
|
||||
link_elem = item.find('a', href=re.compile(r'/s-tv/|/films/'))
|
||||
|
||||
if not link_elem:
|
||||
continue
|
||||
|
||||
url = link_elem.get('href', '')
|
||||
if not url.startswith('http'):
|
||||
url = urljoin(self.base_url, url)
|
||||
|
||||
# Extract title from the item
|
||||
title_elem = item.find('img', alt=True)
|
||||
if title_elem:
|
||||
title = title_elem.get('alt', '').strip()
|
||||
# Extract title
|
||||
img_elem = item.find('img')
|
||||
title = ""
|
||||
if img_elem and img_elem.get('alt'):
|
||||
title = img_elem.get('alt').strip()
|
||||
elif link_elem.get('title'):
|
||||
title = link_elem.get('title').strip()
|
||||
else:
|
||||
# Get text content and clean it
|
||||
text = item.get_text(strip=True)
|
||||
# Skip if it's just a category name
|
||||
if any(cat in text.lower() for cat in ['séries', 'series', 'vf', 'vostfr', 'vo', 'netflix', 'disney', 'amazon', 'apple']):
|
||||
continue
|
||||
title = text
|
||||
|
||||
# Clean up title: remove "affiche" suffix and clean extra whitespace
|
||||
title = re.sub(r'\s+affiche$', '', title, flags=re.IGNORECASE).strip()
|
||||
title = re.sub(r'\s+', ' ', title) # Normalize whitespace
|
||||
title = item.get_text(strip=True)
|
||||
|
||||
# Extract cover image
|
||||
img = item.find('img')
|
||||
cover_image = img.get('src', '') if img else ''
|
||||
img_elem = item.find('img')
|
||||
cover_image = ""
|
||||
if img_elem:
|
||||
# Check for common lazy loading attributes used by various themes
|
||||
cover_image = (
|
||||
img_elem.get('data-src') or
|
||||
img_elem.get('data-original') or
|
||||
img_elem.get('src') or
|
||||
""
|
||||
)
|
||||
|
||||
# If still empty, look for background-style images in inline styles
|
||||
if not cover_image:
|
||||
style = item.get('style', '')
|
||||
if 'background-image' in style:
|
||||
match = re.search(r'url\([\'"]?(.*?)[\'"]?\)', style)
|
||||
if match:
|
||||
cover_image = match.group(1)
|
||||
|
||||
# Only add if we have a title and it's not empty
|
||||
if title and len(title) > 5:
|
||||
# Avoid duplicates
|
||||
if cover_image and not cover_image.startswith('http'):
|
||||
cover_image = urljoin(self.base_url, cover_image)
|
||||
|
||||
# Clean up title
|
||||
title = re.sub(r'\s+affiche$', '', title, flags=re.IGNORECASE).strip()
|
||||
title = re.sub(r'\s+', ' ', title)
|
||||
|
||||
if title and len(title) > 2:
|
||||
if not any(r['url'] == url for r in results):
|
||||
results.append({
|
||||
'title': title,
|
||||
|
||||
Reference in New Issue
Block a user