feat: filtre par type pour recommandations et sorties (#14)

- Parametre content_type sur /api/recommendations et /api/releases/latest
- Section anime: filtre content_type=anime sur releases
- Section series: filtre content_type=series sur recommendations et releases
- Nettoyage emojis dans titres de section

Closes #14
This commit is contained in:
root
2026-04-02 22:42:36 +00:00
parent e5b30741fe
commit c921aafadd
2 changed files with 25 additions and 21 deletions
+10
View File
@@ -28,6 +28,7 @@ async def get_recommendations(
request: Request, request: Request,
limit: int = 15, limit: int = 15,
html: bool = Query(False), html: bool = Query(False),
content_type: Optional[str] = Query(None, description="Filter: 'anime', 'series', or None for all"),
current_user: Optional[User] = Depends(get_optional_user), current_user: Optional[User] = Depends(get_optional_user),
): ):
"""Get personalized anime recommendations based on download history""" """Get personalized anime recommendations based on download history"""
@@ -45,6 +46,10 @@ async def get_recommendations(
try: try:
recommendations = await engine.get_personalized_recommendations(limit=limit) recommendations = await engine.get_personalized_recommendations(limit=limit)
# Filter by content_type if specified
if content_type and content_type != "all":
recommendations = [r for r in recommendations if r.get("content_type", r.get("type", "")) == content_type]
if html or is_htmx: if html or is_htmx:
return templates.TemplateResponse( return templates.TemplateResponse(
@@ -66,12 +71,17 @@ async def get_latest_releases(
request: Request, request: Request,
limit: int = 20, limit: int = 20,
html: bool = Query(False), html: bool = Query(False),
content_type: Optional[str] = Query(None, description="Filter: 'anime', 'series', or None for all"),
): ):
"""Get latest anime releases""" """Get latest anime releases"""
from app.recommendations import get_latest_releases_with_info from app.recommendations import get_latest_releases_with_info
try: try:
releases = await get_latest_releases_with_info(limit=limit) releases = await get_latest_releases_with_info(limit=limit)
# Filter by content_type if specified
if content_type and content_type != "all":
releases = [r for r in releases if r.get("content_type", r.get("type", "")) == content_type]
if html or request.headers.get("HX-Request"): if html or request.headers.get("HX-Request"):
return templates.TemplateResponse( return templates.TemplateResponse(
+15 -21
View File
@@ -12,7 +12,7 @@
<div id="tab-anime" class="tab-content" x-show="activeTab === 'anime'"> <div id="tab-anime" class="tab-content" x-show="activeTab === 'anime'">
<!-- Anime Search Section --> <!-- Anime Search Section -->
<div class="section-header"> <div class="section-header">
<h2>🎬 Rechercher un Anime</h2> <h2>Rechercher un Anime</h2>
</div> </div>
<div class="url-form"> <div class="url-form">
<form hx-get="/api/anime/search" <form hx-get="/api/anime/search"
@@ -38,9 +38,6 @@
<div id="search-loading" class="htmx-indicator" style="margin-top: 15px; color: var(--primary);"> <div id="search-loading" class="htmx-indicator" style="margin-top: 15px; color: var(--primary);">
<div class="spinner"></div> Recherche en cours... <div class="spinner"></div> Recherche en cours...
</div> </div>
<div style="margin-top: 15px; padding: 12px; background: rgba(0, 217, 255, 0.05); border: 1px solid rgba(0, 217, 255, 0.1); border-radius: var(--input-radius); font-size: 13px; color: var(--text-dim);">
💡 <strong>Astuce :</strong> La recherche unifiée explore plusieurs sources pour trouver vos animes préférés.
</div>
</div> </div>
<!-- Anime search results --> <!-- Anime search results -->
@@ -51,11 +48,11 @@
<hr style="border: none; border-top: 1px solid rgba(255,255,255,0.05); margin: 40px 0;"> <hr style="border: none; border-top: 1px solid rgba(255,255,255,0.05); margin: 40px 0;">
<!-- Latest Releases Section --> <!-- Latest Releases Section - Anime only -->
<div class="section-header"> <div class="section-header">
<h2>🔥 Dernières sorties Anime</h2> <h2>Dernieres sorties Anime</h2>
<button class="btn btn-secondary btn-small" <button class="btn btn-secondary btn-small"
hx-get="/api/releases/latest" hx-get="/api/releases/latest?content_type=anime&html=1"
hx-target="#animeReleasesList"> hx-target="#animeReleasesList">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;"> <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
@@ -63,13 +60,13 @@
Actualiser Actualiser
</button> </button>
</div> </div>
<div id="animeReleasesList" class="recommendations-carousel" hx-get="/api/releases/latest" hx-trigger="load delay:500ms"></div> <div id="animeReleasesList" class="recommendations-carousel" hx-get="/api/releases/latest?content_type=anime&html=1" hx-trigger="load delay:500ms"></div>
</div> </div>
<div id="tab-series" class="tab-content" x-show="activeTab === 'series'"> <div id="tab-series" class="tab-content" x-show="activeTab === 'series'">
<!-- Series Search Section --> <!-- Series Search Section -->
<div class="section-header"> <div class="section-header">
<h2>📺 Rechercher une Série TV</h2> <h2>Rechercher une Serie TV</h2>
</div> </div>
<div class="url-form"> <div class="url-form">
<form hx-get="/api/series/search" <form hx-get="/api/series/search"
@@ -82,7 +79,7 @@
type="text" type="text"
name="q" name="q"
id="seriesSearchInput" id="seriesSearchInput"
placeholder="Rechercher une série (ex: Breaking Bad, Game of Thrones...)" placeholder="Rechercher une serie (ex: Breaking Bad, Game of Thrones...)"
required required
> >
<button type="submit" class="btn btn-primary btn-search"> <button type="submit" class="btn btn-primary btn-search">
@@ -95,9 +92,6 @@
<div id="series-search-loading" class="htmx-indicator" style="margin-top: 15px; color: var(--secondary);"> <div id="series-search-loading" class="htmx-indicator" style="margin-top: 15px; color: var(--secondary);">
<div class="spinner"></div> Recherche en cours... <div class="spinner"></div> Recherche en cours...
</div> </div>
<div style="margin-top: 15px; padding: 12px; background: rgba(255, 107, 107, 0.05); border: 1px solid rgba(255, 107, 107, 0.1); border-radius: var(--input-radius); font-size: 13px; color: var(--text-dim);">
💡 <strong>Info:</strong> La recherche utilise FS7 pour trouver des séries TV américaines et européennes.
</div>
</div> </div>
<!-- Series search results --> <!-- Series search results -->
@@ -105,11 +99,11 @@
<hr style="border: none; border-top: 1px solid rgba(255,255,255,0.05); margin: 40px 0;"> <hr style="border: none; border-top: 1px solid rgba(255,255,255,0.05); margin: 40px 0;">
<!-- Recommendations Section --> <!-- Recommendations Section - Series only -->
<div class="section-header"> <div class="section-header">
<h2>🎯 Recommandé pour vous</h2> <h2>Recommande pour vous</h2>
<button class="btn btn-secondary btn-small" <button class="btn btn-secondary btn-small"
hx-get="/api/recommendations" hx-get="/api/recommendations?content_type=series&html=1"
hx-target="#seriesRecommendationsList"> hx-target="#seriesRecommendationsList">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;"> <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
@@ -117,13 +111,13 @@
Actualiser Actualiser
</button> </button>
</div> </div>
<div id="seriesRecommendationsList" class="recommendations-carousel" style="margin-bottom: 40px;" hx-get="/api/recommendations" hx-trigger="load delay:600ms"></div> <div id="seriesRecommendationsList" class="recommendations-carousel" style="margin-bottom: 40px;" hx-get="/api/recommendations?content_type=series&html=1" hx-trigger="load delay:600ms"></div>
<!-- Latest Releases Section --> <!-- Latest Releases Section - Series only -->
<div class="section-header" style="margin-top: 40px;"> <div class="section-header" style="margin-top: 40px;">
<h2>🔥 Dernières sorties Séries TV</h2> <h2>Dernieres sorties Series TV</h2>
<button class="btn btn-secondary btn-small" <button class="btn btn-secondary btn-small"
hx-get="/api/releases/latest" hx-get="/api/releases/latest?content_type=series&html=1"
hx-target="#seriesReleasesList"> hx-target="#seriesReleasesList">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;"> <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
@@ -131,7 +125,7 @@
Actualiser Actualiser
</button> </button>
</div> </div>
<div id="seriesReleasesList" class="releases-carousel" hx-get="/api/releases/latest" hx-trigger="load delay:700ms"></div> <div id="seriesReleasesList" class="releases-carousel" hx-get="/api/releases/latest?content_type=series&html=1" hx-trigger="load delay:700ms"></div>
</div> </div>
<div id="tab-watchlist" class="tab-content" x-show="activeTab === 'watchlist'"> <div id="tab-watchlist" class="tab-content" x-show="activeTab === 'watchlist'">