feat: flat design Sunset Glitch palette + Font Awesome icons
This commit is contained in:
+48
-40
@@ -1,14 +1,22 @@
|
||||
/* Ohm Streaming - Flat Design Theme */
|
||||
:root {
|
||||
/* ========== FLAT DESIGN VARIABLES ========== */
|
||||
--primary: #f15025;
|
||||
--primary-hover: #d94420;
|
||||
--bg-dark: #ffffff;
|
||||
--bg-card: #e6e8e6;
|
||||
--text-main: #191919;
|
||||
--text-dim: #ced0ce;
|
||||
--secondary: #ced0ce;
|
||||
--accent: #f15025;
|
||||
/* ========== FLAT DESIGN VARIABLES - SUNSET GLITCH PALETTE ========== */
|
||||
--primary: #FF9F1C;
|
||||
--primary-hover: #e08a15;
|
||||
--bg-dark: #15171A;
|
||||
--bg-card: #202327;
|
||||
--bg-elevated: #2a2d32;
|
||||
--text-main: #F2F2F2;
|
||||
--text-dim: #8a8f98;
|
||||
--text-muted: #5a5f68;
|
||||
--secondary: #FF9F1C;
|
||||
--border: #2a2d32;
|
||||
--border-hover: #FFBF69;
|
||||
--accent: #FF9F1C;
|
||||
--hover: rgba(255, 191, 105, 0.15);
|
||||
--lilac: #8a8f98;
|
||||
--pastel-petal: #F2F2F2;
|
||||
--surface-hover: rgba(255, 191, 105, 0.08);
|
||||
--danger: #e63946;
|
||||
--success: #2d936c;
|
||||
--warning: #f4a261;
|
||||
@@ -39,7 +47,7 @@ body {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #e6e8e6;
|
||||
background: #202327;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -50,13 +58,13 @@ body {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary);
|
||||
background: var(--text-main);
|
||||
}
|
||||
|
||||
/* Firefox scrollbar */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--text-dim) #e6e8e6;
|
||||
scrollbar-color: var(--text-dim) #202327;
|
||||
}
|
||||
|
||||
/* ========== CONTAINER ========== */
|
||||
@@ -97,7 +105,7 @@ h1 {
|
||||
.section-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
border-left: 4px solid var(--primary);
|
||||
border-left: 4px solid #FFBF69;
|
||||
padding-left: 15px;
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -188,8 +196,8 @@ h1 {
|
||||
}
|
||||
|
||||
.btn-outlined:hover:not(:disabled) {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
border-color: var(--text-dim);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
/* Text Button */
|
||||
@@ -268,7 +276,7 @@ h1 {
|
||||
}
|
||||
|
||||
.card:hover, .hc:hover, .download-item:hover {
|
||||
border-color: var(--primary);
|
||||
border-color: #FFBF69;
|
||||
}
|
||||
|
||||
/* ========== HORIZONTAL SCROLL ROW ========== */
|
||||
@@ -300,7 +308,7 @@ h1 {
|
||||
.streaming-row::-webkit-scrollbar-thumb:hover,
|
||||
.recommendations-carousel::-webkit-scrollbar-thumb:hover,
|
||||
.releases-carousel::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary);
|
||||
background: var(--text-main);
|
||||
}
|
||||
|
||||
/* ========== HOME CARD ========== */
|
||||
@@ -317,7 +325,7 @@ h1 {
|
||||
}
|
||||
|
||||
.hc:hover {
|
||||
border-color: var(--primary);
|
||||
border-color: #FFBF69;
|
||||
}
|
||||
|
||||
.hc-poster {
|
||||
@@ -432,7 +440,7 @@ h1 {
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: var(--primary);
|
||||
color: #FFBF69;
|
||||
}
|
||||
|
||||
.tab.active::after {
|
||||
@@ -442,7 +450,7 @@ h1 {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background: var(--primary);
|
||||
background: #FF9F1C;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
@@ -457,7 +465,7 @@ h1 {
|
||||
}
|
||||
|
||||
.input-group:focus-within {
|
||||
border-color: var(--primary);
|
||||
border-color: #FFBF69;
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
@@ -511,14 +519,14 @@ h1 {
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-bottom-color: var(--primary);
|
||||
border-bottom-color: #FFBF69;
|
||||
}
|
||||
|
||||
.form-group input:focus + label,
|
||||
.form-group input:not(:placeholder-shown) + label {
|
||||
transform: translateY(-24px);
|
||||
font-size: 0.75rem;
|
||||
color: var(--primary);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.form-group input::placeholder {
|
||||
@@ -530,7 +538,7 @@ h1 {
|
||||
margin-bottom: 25px;
|
||||
padding: 16px 20px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--primary);
|
||||
border: 1px solid var(--text-dim);
|
||||
border-radius: var(--card-radius);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -543,13 +551,13 @@ h1 {
|
||||
padding: 40px;
|
||||
background: var(--bg-card);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--text-dim);
|
||||
border: 1px solid rgba(255, 191, 105, 0.3);
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
color: var(--primary);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.auth-tabs {
|
||||
@@ -570,7 +578,7 @@ h1 {
|
||||
}
|
||||
|
||||
.auth-tab.active {
|
||||
color: var(--primary);
|
||||
color: #FFBF69;
|
||||
}
|
||||
|
||||
.auth-tab.active::after {
|
||||
@@ -580,7 +588,7 @@ h1 {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background: var(--primary);
|
||||
background: #FF9F1C;
|
||||
}
|
||||
|
||||
.auth-form {
|
||||
@@ -606,7 +614,7 @@ h1 {
|
||||
}
|
||||
|
||||
.auth-success {
|
||||
background: rgba(45, 147, 108, 0.1);
|
||||
background: rgba(255, 191, 105, 0.1);
|
||||
border: 1px solid var(--success);
|
||||
color: var(--success);
|
||||
}
|
||||
@@ -618,7 +626,7 @@ h1 {
|
||||
/* ========== FLAT PROGRESS BARS ========== */
|
||||
.progress-container {
|
||||
height: 6px;
|
||||
background: var(--text-dim);
|
||||
background: rgba(255, 191, 105, 0.15);
|
||||
border-radius: 3px;
|
||||
margin: 12px 0;
|
||||
overflow: hidden;
|
||||
@@ -626,7 +634,7 @@ h1 {
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: var(--primary);
|
||||
background: #FF9F1C;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@@ -690,8 +698,8 @@ h1 {
|
||||
}
|
||||
|
||||
.download-item:hover {
|
||||
border-color: var(--primary);
|
||||
border-left-color: var(--primary);
|
||||
border-color: #FFBF69;
|
||||
border-left-color: #FFBF69;
|
||||
}
|
||||
|
||||
.download-info {
|
||||
@@ -769,13 +777,13 @@ h1 {
|
||||
|
||||
/* Progress bar for downloading */
|
||||
.download-item.status-downloading .progress-bar {
|
||||
background: var(--primary);
|
||||
background: var(--text-dim);
|
||||
}
|
||||
|
||||
/* ========== BADGE SYSTEM ========== */
|
||||
.badge-completed {
|
||||
color: var(--success);
|
||||
background: rgba(45, 147, 108, 0.1);
|
||||
background: rgba(255, 191, 105, 0.1);
|
||||
padding: 4px 8px;
|
||||
border-radius: var(--input-radius);
|
||||
}
|
||||
@@ -789,7 +797,7 @@ h1 {
|
||||
|
||||
.badge-downloading {
|
||||
color: var(--primary);
|
||||
background: rgba(241, 80, 37, 0.1);
|
||||
background: rgba(255, 191, 105, 0.15);
|
||||
padding: 4px 8px;
|
||||
border-radius: var(--input-radius);
|
||||
}
|
||||
@@ -817,7 +825,7 @@ h1 {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid var(--text-dim);
|
||||
border-top-color: var(--primary);
|
||||
border-top-color: var(--text-main);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@@ -830,7 +838,7 @@ h1 {
|
||||
|
||||
/* Skeleton Loading */
|
||||
.skeleton {
|
||||
background: var(--bg-card);
|
||||
background: rgba(255, 191, 105, 0.1);
|
||||
animation: skeleton-loading 1.5s ease-in-out infinite;
|
||||
border-radius: var(--input-radius);
|
||||
}
|
||||
@@ -904,7 +912,7 @@ h1 {
|
||||
.toast {
|
||||
padding: 16px 24px;
|
||||
background: var(--bg-card);
|
||||
border-left: 4px solid var(--primary);
|
||||
border-left: 4px solid var(--text-dim);
|
||||
border-radius: var(--card-radius);
|
||||
border: 1px solid var(--text-dim);
|
||||
display: flex;
|
||||
@@ -929,7 +937,7 @@ h1 {
|
||||
}
|
||||
|
||||
.toast.info {
|
||||
border-left-color: var(--primary);
|
||||
border-left-color: #FFBF69;
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
|
||||
+24
-24
@@ -82,7 +82,7 @@ async function searchAnimeDetails(query, malId = null) {
|
||||
if (hasResults) {
|
||||
streamingParts.unshift(
|
||||
`<div class="streaming-results-header">
|
||||
<h3>🎬 Résultats de streaming</h3>
|
||||
<h3><i class="fa-solid fa-film"></i> Résultats de streaming</h3>
|
||||
</div>
|
||||
<div class="search-results" style="margin-top: 20px;">`
|
||||
);
|
||||
@@ -110,7 +110,7 @@ async function searchAnimeDetails(query, malId = null) {
|
||||
if (streamingHtml) {
|
||||
resultsContainer.innerHTML = `
|
||||
<div class="no-results" style="margin-bottom: 20px;">
|
||||
<p>ℹ️ Aucune fiche trouvée sur MyAnimeList pour "${escapeHtml(query)}"</p>
|
||||
<p><i class="fa-solid fa-circle-info"></i> Aucune fiche trouvée sur MyAnimeList pour "${escapeHtml(query)}"</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #888;">
|
||||
Essayez le nom en anglais ou japonais (ex: "Frieren: Beyond Journey's End")
|
||||
</p>
|
||||
@@ -125,7 +125,7 @@ async function searchAnimeDetails(query, malId = null) {
|
||||
} else {
|
||||
resultsContainer.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Aucun résultat trouvé pour "${escapeHtml(query)}"</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Aucun résultat trouvé pour "${escapeHtml(query)}"</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #888;">
|
||||
Essayez le nom en anglais ou japonais (ex: "Frieren: Beyond Journey's End", "One Piece")
|
||||
</p>
|
||||
@@ -138,7 +138,7 @@ async function searchAnimeDetails(query, malId = null) {
|
||||
console.error('Error searching anime details:', error);
|
||||
resultsContainer.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors de la recherche.</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors de la recherche.</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #ff6b6b;">${error.message}</p>
|
||||
</div>
|
||||
`;
|
||||
@@ -177,7 +177,7 @@ async function getProviderSearchResults(query) {
|
||||
if (hasResults) {
|
||||
htmlParts.unshift(
|
||||
`<div class="streaming-results-header">
|
||||
<h3>🎬 Résultats de streaming</h3>
|
||||
<h3><i class="fa-solid fa-film"></i> Résultats de streaming</h3>
|
||||
</div>
|
||||
<div class="search-results" style="margin-top: 20px;">`
|
||||
);
|
||||
@@ -249,16 +249,16 @@ function renderAnimeDetails(anime) {
|
||||
` : ''}
|
||||
|
||||
<div class="anime-details-meta">
|
||||
${score > 0 ? `<div class="anime-details-rating">★ ${score.toFixed(2)}</div>` : ''}
|
||||
${score > 0 ? `<div class="anime-details-rating"><i class="fa-solid fa-star"></i> ${score.toFixed(2)}</div>` : ''}
|
||||
${rank > 0 ? `<div class="anime-details-rank">#${rank}</div>` : ''}
|
||||
${popularity > 0 ? `<div class="anime-details-popularity">Popularity #${popularity}</div>` : ''}
|
||||
</div>
|
||||
|
||||
<div class="anime-details-stats">
|
||||
${anime.episodes ? `<span>📺 ${anime.episodes} épisodes</span>` : ''}
|
||||
${anime.status ? `<span>📡 ${translateStatus(anime.status)}</span>` : ''}
|
||||
${anime.duration ? `<span>⏱️ ${escapeHtml(anime.duration)}</span>` : ''}
|
||||
${anime.year ? `<span>📅 ${anime.year}</span>` : ''}
|
||||
${anime.episodes ? `<span><i class="fa-solid fa-tv"></i> ${anime.episodes} épisodes</span>` : ''}
|
||||
${anime.status ? `<span><i class="fa-solid fa-tower-broadcast"></i> ${translateStatus(anime.status)}</span>` : ''}
|
||||
${anime.duration ? `<span><i class="fa-solid fa-clock"></i> ${escapeHtml(anime.duration)}</span>` : ''}
|
||||
${anime.year ? `<span><i class="fa-solid fa-calendar"></i> ${anime.year}</span>` : ''}
|
||||
</div>
|
||||
|
||||
${studios.length > 0 ? `
|
||||
@@ -269,10 +269,10 @@ function renderAnimeDetails(anime) {
|
||||
|
||||
<div class="anime-details-actions">
|
||||
<a href="${escapeHtml(anime.url)}" target="_blank" class="btn btn-secondary btn-small">
|
||||
🔗 Voir sur MAL
|
||||
<i class="fa-solid fa-link"></i> Voir sur MAL
|
||||
</a>
|
||||
<button onclick="searchAnimeOnProviders('${escapeHtml(anime.title)}')" class="btn btn-primary btn-small">
|
||||
📥 Télécharger
|
||||
<i class="fa-solid fa-download"></i> Télécharger
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -290,9 +290,9 @@ function renderAnimeDetails(anime) {
|
||||
${synopsis ? `
|
||||
<div class="anime-details-section">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
|
||||
<h3 style="margin: 0;">📖 Synopsis</h3>
|
||||
<h3 style="margin: 0;"><i class="fa-solid fa-book"></i> Synopsis</h3>
|
||||
<button onclick="translateSynopsis('${synopsisId}', this)" class="btn btn-secondary btn-small" style="font-size: 12px;">
|
||||
🌐 Traduire en français
|
||||
<i class="fa-solid fa-globe"></i> Traduire en français
|
||||
</button>
|
||||
</div>
|
||||
<p id="${synopsisId}" class="anime-details-synopsis">${escapeHtml(synopsis)}</p>
|
||||
@@ -302,7 +302,7 @@ function renderAnimeDetails(anime) {
|
||||
<!-- Seasons (Sequel/Prequel) -->
|
||||
${seasons.length > 0 ? `
|
||||
<div class="anime-details-section">
|
||||
<h3>📺 Saisons</h3>
|
||||
<h3><i class="fa-solid fa-tv"></i> Saisons</h3>
|
||||
<div class="anime-related-list">
|
||||
${seasons.map(season => `
|
||||
<div class="anime-related-group">
|
||||
@@ -310,7 +310,7 @@ function renderAnimeDetails(anime) {
|
||||
<div class="anime-related-items">
|
||||
${season.entries.map(entry => `
|
||||
<div class="anime-related-item" onclick="searchAnimeDetails('${escapeHtml(entry.title)}', ${entry.mal_id})" style="cursor: pointer;">
|
||||
${entry.type ? `<span style="color: #f15025; font-size: 11px; margin-right: 8px;">${escapeHtml(entry.type)}</span>` : ''}
|
||||
${entry.type ? `<span style="color: #FFBF69; font-size: 11px; margin-right: 8px;">${escapeHtml(entry.type)}</span>` : ''}
|
||||
${escapeHtml(entry.title)}
|
||||
${entry.url ? `<a href="${escapeHtml(entry.url)}" target="_blank" style="margin-left: auto; color: #888; font-size: 18px; text-decoration: none;" title="Voir sur MyAnimeList">↗</a>` : ''}
|
||||
</div>
|
||||
@@ -358,7 +358,7 @@ async function loadStreamingResults(query) {
|
||||
if (successfulResults.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>⚠️ Aucun résultat de streaming trouvé pour "${escapeHtml(query)}"</p>
|
||||
<p><i class="fa-solid fa-triangle-exclamation"></i> Aucun résultat de streaming trouvé pour "${escapeHtml(query)}"</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
@@ -367,7 +367,7 @@ async function loadStreamingResults(query) {
|
||||
// Display results
|
||||
container.innerHTML = `
|
||||
<div class="streaming-results-header">
|
||||
<h3>🎬 Disponible sur</h3>
|
||||
<h3><i class="fa-solid fa-film"></i> Disponible sur</h3>
|
||||
</div>
|
||||
<div class="streaming-results-grid">
|
||||
${successfulResults.map(result => renderStreamingResult(result, query)).join('')}
|
||||
@@ -378,7 +378,7 @@ async function loadStreamingResults(query) {
|
||||
console.error('Error loading streaming results:', error);
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors de la recherche des sources de streaming.</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors de la recherche des sources de streaming.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -406,7 +406,7 @@ function renderStreamingResult(result, query) {
|
||||
</select>
|
||||
|
||||
<button class="btn btn-primary btn-small streaming-download-btn" onclick="downloadSelectedEpisode(this)">
|
||||
📥 Télécharger
|
||||
<i class="fa-solid fa-download"></i> Télécharger
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -475,7 +475,7 @@ async function translateSynopsis(synopsisId, button) {
|
||||
// Revert to original
|
||||
synopsisElement.textContent = originalText;
|
||||
synopsisElement.dataset.translated = 'false';
|
||||
button.innerHTML = '🌐 Traduire en français';
|
||||
button.innerHTML = '<i class="fa-solid fa-globe"></i> Traduire en français';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ async function translateSynopsis(synopsisId, button) {
|
||||
|
||||
// Show loading state
|
||||
button.disabled = true;
|
||||
button.innerHTML = '⏳ Traduction...';
|
||||
button.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Traduction...';
|
||||
synopsisElement.style.opacity = '0.5';
|
||||
|
||||
try {
|
||||
@@ -509,7 +509,7 @@ async function translateSynopsis(synopsisId, button) {
|
||||
|
||||
synopsisElement.textContent = data.translatedText;
|
||||
synopsisElement.dataset.translated = 'true';
|
||||
button.innerHTML = '🔄 Voir l\'original';
|
||||
button.innerHTML = '<i class="fa-solid fa-rotate"></i> Voir l\'original';
|
||||
} else {
|
||||
const errorData = await response.json().catch(() => ({ detail: 'Unknown error' }));
|
||||
console.error('Translation API error:', errorData);
|
||||
@@ -523,7 +523,7 @@ async function translateSynopsis(synopsisId, button) {
|
||||
const errorMessage = document.createElement('div');
|
||||
errorMessage.style.cssText = 'margin-top: 10px; padding: 10px; background: rgba(255, 107, 107, 0.2); border-radius: 8px; font-size: 12px; color: #ff6b6b;';
|
||||
errorMessage.innerHTML = `
|
||||
⚠️ Service de traduction temporairement indisponible.<br>
|
||||
<i class="fa-solid fa-triangle-exclamation"></i> Service de traduction temporairement indisponible.<br>
|
||||
<small>Essayez à nouveau dans quelques instants.</small>
|
||||
`;
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ async function loadRecommendations() {
|
||||
} else {
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>⚠️ Aucune recommandation disponible pour le moment.</p>
|
||||
<p><i class="fa-solid fa-triangle-exclamation"></i> Aucune recommandation disponible pour le moment.</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #888;">
|
||||
Soit l'API MyAnimeList est inaccessible, soit vous n'avez pas encore de téléchargements.
|
||||
</p>
|
||||
<button class="btn btn-secondary btn-small" onclick="loadRecommendations()" style="margin-top: 10px;">
|
||||
🔄 Réessayer
|
||||
<i class="fa-solid fa-rotate"></i> Réessayer
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -38,10 +38,10 @@ async function loadRecommendations() {
|
||||
console.error('Error loading recommendations:', error);
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors du chargement des recommandations.</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors du chargement des recommandations.</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #ff6b6b;">${error.message}</p>
|
||||
<button class="btn btn-secondary btn-small" onclick="loadRecommendations()" style="margin-top: 10px;">
|
||||
🔄 Réessayer
|
||||
<i class="fa-solid fa-rotate"></i> Réessayer
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -71,12 +71,12 @@ async function loadLatestReleases() {
|
||||
} else {
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>⚠️ Aucune sortie disponible pour le moment.</p>
|
||||
<p><i class="fa-solid fa-triangle-exclamation"></i> Aucune sortie disponible pour le moment.</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #888;">
|
||||
L'API MyAnimeList pourrait être temporairement inaccessible.
|
||||
</p>
|
||||
<button class="btn btn-secondary btn-small" onclick="loadLatestReleases()" style="margin-top: 10px;">
|
||||
🔄 Réessayer
|
||||
<i class="fa-solid fa-rotate"></i> Réessayer
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -87,10 +87,10 @@ async function loadLatestReleases() {
|
||||
console.error('Error loading releases:', error);
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors du chargement des sorties.</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors du chargement des sorties.</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #ff6b6b;">${error.message}</p>
|
||||
<button class="btn btn-secondary btn-small" onclick="loadLatestReleases()" style="margin-top: 10px;">
|
||||
🔄 Réessayer
|
||||
<i class="fa-solid fa-rotate"></i> Réessayer
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -100,7 +100,7 @@ async function loadLatestReleases() {
|
||||
|
||||
// Load all home content
|
||||
async function loadHomeContent() {
|
||||
console.log('🏠 loadHomeContent() called');
|
||||
console.log('loadHomeContent() called');
|
||||
|
||||
const loading = document.getElementById('homeLoading');
|
||||
const recommendationsSection = document.getElementById('recommendationsSection');
|
||||
@@ -123,13 +123,13 @@ async function loadHomeContent() {
|
||||
loadRecommendations(),
|
||||
loadLatestReleases()
|
||||
]);
|
||||
console.log('✅ Home content loaded successfully');
|
||||
console.log('Home content loaded successfully');
|
||||
|
||||
// Show sections if they have content
|
||||
if (recommendationsSection) recommendationsSection.style.display = 'block';
|
||||
if (releasesSection) releasesSection.style.display = 'block';
|
||||
} catch (error) {
|
||||
console.error('❌ Error loading home content:', error);
|
||||
console.error('Error loading home content:', error);
|
||||
if (loading) {
|
||||
loading.innerHTML = 'Erreur lors du chargement. Consultez la console pour plus de détails.';
|
||||
}
|
||||
@@ -149,11 +149,11 @@ function renderRecommendationCard(anime) {
|
||||
|
||||
return `
|
||||
<div class="anime-card-horizontal recommendation-card">
|
||||
${reason ? `<div class="recommendation-badge">💡 ${escapeHtml(reason)}</div>` : ''}
|
||||
${reason ? `<div class="recommendation-badge"><i class="fa-solid fa-lightbulb"></i> ${escapeHtml(reason)}</div>` : ''}
|
||||
|
||||
<div class="anime-card-header">
|
||||
<div class="anime-card-title">${escapeHtml(anime.title)}</div>
|
||||
${score > 0 ? `<div class="anime-card-rating">★ ${score.toFixed(1)}</div>` : ''}
|
||||
${score > 0 ? `<div class="anime-card-rating"><i class="fa-solid fa-star"></i> ${score.toFixed(1)}</div>` : ''}
|
||||
</div>
|
||||
|
||||
<div class="anime-card-content">
|
||||
@@ -165,7 +165,7 @@ function renderRecommendationCard(anime) {
|
||||
</div>
|
||||
|
||||
<div class="anime-card-meta">
|
||||
${anime.episodes ? `📺 ${anime.episodes} ep` : ''}
|
||||
${anime.episodes ? `<i class="fa-solid fa-tv"></i> ${anime.episodes} ep` : ''}
|
||||
${anime.episodes && anime.status ? ' • ' : ''}
|
||||
${anime.status ? translateStatus(anime.status) : ''}
|
||||
</div>
|
||||
@@ -174,17 +174,17 @@ function renderRecommendationCard(anime) {
|
||||
|
||||
${anime.synopsis ? `
|
||||
<details class="anime-synopsis">
|
||||
<summary>📖 Synopsis</summary>
|
||||
<summary><i class="fa-solid fa-book"></i> Synopsis</summary>
|
||||
<p>${escapeHtml(anime.synopsis.substring(0, 150))}${anime.synopsis.length > 150 ? '...' : ''}</p>
|
||||
</details>
|
||||
` : ''}
|
||||
|
||||
<div class="anime-card-actions">
|
||||
<button class="btn btn-secondary btn-small" onclick="window.open('${escapeHtml(anime.url)}', '_blank')">
|
||||
🔗 MAL
|
||||
<i class="fa-solid fa-link"></i> MAL
|
||||
</button>
|
||||
<button class="btn btn-primary btn-small" onclick="searchAnimeOnProviders('${escapeHtml(anime.title)}')">
|
||||
📥 Télécharger
|
||||
<i class="fa-solid fa-download"></i> Télécharger
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,11 +202,11 @@ function renderReleaseCard(anime) {
|
||||
|
||||
return `
|
||||
<div class="anime-card-horizontal release-card">
|
||||
<div class="release-badge">🔥 ${escapeHtml(releaseType)}</div>
|
||||
<div class="release-badge"><i class="fa-solid fa-fire"></i> ${escapeHtml(releaseType)}</div>
|
||||
|
||||
<div class="anime-card-header">
|
||||
<div class="anime-card-title">${escapeHtml(anime.title)}</div>
|
||||
${score > 0 ? `<div class="anime-card-rating">★ ${score.toFixed(1)}</div>` : ''}
|
||||
${score > 0 ? `<div class="anime-card-rating"><i class="fa-solid fa-star"></i> ${score.toFixed(1)}</div>` : ''}
|
||||
</div>
|
||||
|
||||
<div class="anime-card-content">
|
||||
@@ -218,7 +218,7 @@ function renderReleaseCard(anime) {
|
||||
</div>
|
||||
|
||||
<div class="anime-card-meta">
|
||||
${anime.episodes ? `📺 ${anime.episodes} ep` : ''}
|
||||
${anime.episodes ? `<i class="fa-solid fa-tv"></i> ${anime.episodes} ep` : ''}
|
||||
${anime.episodes && anime.status ? ' • ' : ''}
|
||||
${anime.status ? translateStatus(anime.status) : ''}
|
||||
</div>
|
||||
@@ -227,17 +227,17 @@ function renderReleaseCard(anime) {
|
||||
|
||||
${anime.synopsis ? `
|
||||
<details class="anime-synopsis">
|
||||
<summary>📖 Synopsis</summary>
|
||||
<summary><i class="fa-solid fa-book"></i> Synopsis</summary>
|
||||
<p>${escapeHtml(anime.synopsis.substring(0, 150))}${anime.synopsis.length > 150 ? '...' : ''}</p>
|
||||
</details>
|
||||
` : ''}
|
||||
|
||||
<div class="anime-card-actions">
|
||||
<button class="btn btn-secondary btn-small" onclick="window.open('${escapeHtml(anime.url)}', '_blank')">
|
||||
🔗 MAL
|
||||
<i class="fa-solid fa-link"></i> MAL
|
||||
</button>
|
||||
<button class="btn btn-primary btn-small" onclick="searchAnimeOnProviders('${escapeHtml(anime.title)}')">
|
||||
📥 Télécharger
|
||||
<i class="fa-solid fa-download"></i> Télécharger
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -248,9 +248,9 @@ function renderReleaseCard(anime) {
|
||||
function getRatingColor(score) {
|
||||
if (score >= 9) return '#ffd700';
|
||||
if (score >= 8) return '#2d936c';
|
||||
if (score >= 7) return '#f15025';
|
||||
if (score >= 7) return '#FF9F1C';
|
||||
if (score >= 6) return '#f4a261';
|
||||
return '#ced0ce';
|
||||
return '#888888';
|
||||
}
|
||||
|
||||
// Search anime on providers (redirects to anime tab)
|
||||
|
||||
+12
-12
@@ -26,7 +26,7 @@ async function handleSeriesSearch() {
|
||||
const series = data.results['fs7'];
|
||||
let html = `
|
||||
<div class="streaming-results-header">
|
||||
<h3>📺 Résultats pour "${escapeHtml(query)}"</h3>
|
||||
<h3><i class="fa-solid fa-tv"></i> Résultats pour "${escapeHtml(query)}"</h3>
|
||||
</div>
|
||||
<div class="search-results" style="margin-top: 20px;">
|
||||
`;
|
||||
@@ -46,7 +46,7 @@ async function handleSeriesSearch() {
|
||||
<div class="anime-card" id="series-fs7-${encodeURIComponent(s.url)}">
|
||||
<div class="anime-card-header">
|
||||
<div class="anime-card-title">${escapeHtml(s.title)}</div>
|
||||
<div class="anime-card-provider">📺 French Stream</div>
|
||||
<div class="anime-card-provider"><i class="fa-solid fa-tv"></i> French Stream</div>
|
||||
</div>
|
||||
${coverImage ? `
|
||||
<div style="text-align: center; margin: 10px 0;">
|
||||
@@ -55,10 +55,10 @@ async function handleSeriesSearch() {
|
||||
` : ''}
|
||||
<div class="anime-card-actions">
|
||||
<button class="btn btn-secondary btn-small" onclick="window.open('${escapeHtml(s.url)}', '_blank')">
|
||||
🔗 Voir sur FS7
|
||||
<i class="fa-solid fa-link"></i> Voir sur FS7
|
||||
</button>
|
||||
<button class="btn btn-primary btn-small" onclick="loadSeriesEpisodesDirect('${escapeHtml(s.url)}', '${escapeHtml(s.title)}')">
|
||||
📥 Voir les épisodes
|
||||
<i class="fa-solid fa-download"></i> Voir les épisodes
|
||||
</button>
|
||||
</div>
|
||||
<div id="episodes-fs7-${encodeURIComponent(s.url)}" style="margin-top: 10px;"></div>
|
||||
@@ -71,7 +71,7 @@ async function handleSeriesSearch() {
|
||||
} else {
|
||||
resultsContainer.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Aucune série trouvée pour "${escapeHtml(query)}"</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Aucune série trouvée pour "${escapeHtml(query)}"</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; opacity: 0.7;">
|
||||
Essayez avec un autre titre ou vérifiez l'orthographe
|
||||
</p>
|
||||
@@ -81,7 +81,7 @@ async function handleSeriesSearch() {
|
||||
console.error('Error searching series:', error);
|
||||
resultsContainer.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors de la recherche</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors de la recherche</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #ff6b6b;">${error.message}</p>
|
||||
</div>`;
|
||||
}
|
||||
@@ -102,10 +102,10 @@ async function loadSeriesEpisodesDirect(url, title) {
|
||||
if (data.episodes && data.episodes.length > 0) {
|
||||
let html = `
|
||||
<div style="margin-top: 15px;">
|
||||
<label style="font-size: 12px; color: #f15025; margin-bottom: 5px; display: block;">
|
||||
📺 Sélectionner un épisode:
|
||||
<label style="font-size: 12px; color: #FF9F1C; margin-bottom: 5px; display: block;">
|
||||
<i class="fa-solid fa-tv"></i> Sélectionner un épisode:
|
||||
</label>
|
||||
<select id="select-episodes-${encodeURIComponent(url)}" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ced0ce; background: #ffffff; color: #191919;">
|
||||
<select id="select-episodes-${encodeURIComponent(url)}" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #2a2d32; background: #202327; color: #F2F2F2;">
|
||||
<option value="">Sélectionner un épisode</option>
|
||||
${data.episodes.map(ep => `
|
||||
<option value="${escapeHtml(ep.url)}">Épisode ${escapeHtml(ep.episode)}</option>
|
||||
@@ -145,7 +145,7 @@ async function downloadSeriesEpisode(url, title) {
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert(`✅ Téléchargement démarré pour "${title}"`);
|
||||
alert(`Téléchargement démarré pour "${title}"`);
|
||||
// Refresh downloads
|
||||
if (typeof loadDownloads === 'function') {
|
||||
loadDownloads();
|
||||
@@ -155,11 +155,11 @@ async function downloadSeriesEpisode(url, title) {
|
||||
const errorMessage = error.detail
|
||||
? (typeof error.detail === 'string' ? error.detail : JSON.stringify(error.detail))
|
||||
: 'Impossible de démarrer le téléchargement';
|
||||
alert(`❌ Erreur: ${errorMessage}`);
|
||||
alert(`Erreur : ${errorMessage}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Download error:', error);
|
||||
alert(`❌ Erreur lors du téléchargement: ${error.message}`);
|
||||
alert(`Erreur lors du téléchargement : ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -19,7 +19,7 @@ function renderSeriesRecommendationCard(series) {
|
||||
|
||||
return `
|
||||
<div class="anime-card-horizontal recommendation-card">
|
||||
<div class="recommendation-badge">🎺 Série TV populaire</div>
|
||||
<div class="recommendation-badge"><i class="fa-solid fa-music"></i> Série TV populaire</div>
|
||||
|
||||
<div class="anime-card-header">
|
||||
<div class="anime-card-title">${escapeHtml(series.title)}</div>
|
||||
@@ -30,17 +30,17 @@ function renderSeriesRecommendationCard(series) {
|
||||
|
||||
<div class="anime-card-info">
|
||||
<div class="anime-card-meta">
|
||||
📺 Série TV
|
||||
<i class="fa-solid fa-tv"></i> Série TV
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="anime-card-actions">
|
||||
<button class="btn btn-secondary btn-small" onclick="window.open('${escapeHtml(series.url)}', '_blank')">
|
||||
🔗 Voir sur FS7
|
||||
<i class="fa-solid fa-link"></i> Voir sur FS7
|
||||
</button>
|
||||
<button class="btn btn-primary btn-small" onclick="loadSeriesEpisodes('${escapeHtml(series.url)}', '${escapeHtml(series.title)}')">
|
||||
📥 Voir les épisodes
|
||||
<i class="fa-solid fa-download"></i> Voir les épisodes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,17 +92,17 @@ function renderSeriesReleaseCard(series) {
|
||||
|
||||
<div class="anime-card-info">
|
||||
<div class="anime-card-meta">
|
||||
📺 Série TV • Nouveau
|
||||
<i class="fa-solid fa-tv"></i> Série TV • Nouveau
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="anime-card-actions">
|
||||
<button class="btn btn-secondary btn-small" onclick="window.open('${escapeHtml(series.url)}', '_blank')">
|
||||
🔗 Voir sur FS7
|
||||
<i class="fa-solid fa-link"></i> Voir sur FS7
|
||||
</button>
|
||||
<button class="btn btn-primary btn-small" onclick="loadSeriesEpisodes('${escapeHtml(series.url)}', '${escapeHtml(series.title)}')">
|
||||
📥 Voir les épisodes
|
||||
<i class="fa-solid fa-download"></i> Voir les épisodes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -236,10 +236,10 @@ async function loadSeriesReleases() {
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors du chargement des séries</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors du chargement des séries</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #ff6b6b;">${error.message}</p>
|
||||
<button class="btn btn-secondary btn-small" onclick="loadSeriesReleases()" style="margin-top: 10px;">
|
||||
🔄 Réessayer
|
||||
<i class="fa-solid fa-rotate"></i> Réessayer
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
@@ -260,7 +260,7 @@ async function loadProvidersGrid() {
|
||||
let html = '';
|
||||
|
||||
// Section Anime providers
|
||||
html += '<div class="section-header"><h3 style="margin-top: 20px;">🎬 Sites Anime</h3></div>';
|
||||
html += '<div class="section-header"><h3 style="margin-top: 20px;"><i class="fa-solid fa-film"></i> Sites Anime</h3></div>';
|
||||
html += '<div class="search-results">';
|
||||
|
||||
const animeProviders = Object.entries(data.anime_providers || {});
|
||||
@@ -281,11 +281,11 @@ async function loadProvidersGrid() {
|
||||
<div class="anime-card-actions">
|
||||
${domains.length > 0 ? `
|
||||
<button class="btn btn-primary btn-small" onclick="window.open('https://${domains[0]}', '_blank')">
|
||||
🔗 Visiter le site
|
||||
<i class="fa-solid fa-link"></i> Visiter le site
|
||||
</button>
|
||||
` : ''}
|
||||
<button class="btn btn-secondary btn-small" onclick="showProviderSearch('${id}')">
|
||||
🔍 Rechercher
|
||||
<i class="fa-solid fa-magnifying-glass"></i> Rechercher
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -298,7 +298,7 @@ async function loadProvidersGrid() {
|
||||
html += '</div>';
|
||||
|
||||
// Section File hosts
|
||||
html += '<div class="section-header" style="margin-top: 40px;"><h3>💾 Hébergeurs de fichiers</h3></div>';
|
||||
html += '<div class="section-header" style="margin-top: 40px;"><h3><i class="fa-solid fa-floppy-disk"></i> Hébergeurs de fichiers</h3></div>';
|
||||
html += '<div class="search-results">';
|
||||
|
||||
const fileHosts = Object.entries(data.file_hosts || {});
|
||||
@@ -311,7 +311,7 @@ async function loadProvidersGrid() {
|
||||
</div>
|
||||
<div class="anime-card-actions">
|
||||
<button class="btn btn-secondary btn-small" onclick="showDownloadInfo()">
|
||||
📥 Télécharger un fichier
|
||||
<i class="fa-solid fa-download"></i> Télécharger un fichier
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -330,10 +330,10 @@ async function loadProvidersGrid() {
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<div class="no-results">
|
||||
<p>❌ Erreur lors du chargement des fournisseurs</p>
|
||||
<p><i class="fa-solid fa-xmark"></i> Erreur lors du chargement des fournisseurs</p>
|
||||
<p style="font-size: 12px; margin-top: 10px; color: #ff6b6b;">${error.message}</p>
|
||||
<button class="btn btn-secondary btn-small" onclick="loadProvidersGrid()" style="margin-top: 10px;">
|
||||
🔄 Réessayer
|
||||
<i class="fa-solid fa-rotate"></i> Réessayer
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -349,7 +349,7 @@ function showProviderSearch(providerId) {
|
||||
|
||||
// Show download info (explains how to download)
|
||||
function showDownloadInfo() {
|
||||
alert('💡 Pour télécharger un fichier:\n\n1. Utilisez l\'onglet "Recherche"\n2. Entrez le nom de l\'anime/série\n3. Cliquez sur "Télécharger" sur un épisode\n\nOu bien:\n- Copiez directement un lien de téléchargement dans la barre d\'adresse de votre navigateur');
|
||||
alert('Pour télécharger un fichier:\n\n1. Utilisez l\'onglet "Recherche"\n2. Entrez le nom de l\'anime/série\n3. Cliquez sur "Télécharger" sur un épisode\n\nOu bien:\n- Copiez directement un lien de téléchargement dans la barre d\'adresse de votre navigateur');
|
||||
}
|
||||
|
||||
// Make additional functions available globally
|
||||
|
||||
@@ -346,10 +346,10 @@ async function handleStartScheduler() {
|
||||
try {
|
||||
await startScheduler();
|
||||
await loadSchedulerStatus();
|
||||
alert('✅ Planificateur démarré!');
|
||||
alert('Planificateur démarré !');
|
||||
} catch (error) {
|
||||
console.error('Error starting scheduler:', error);
|
||||
alert(`❌ Erreur: ${error.message}`);
|
||||
alert(`Erreur : ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,10 +360,10 @@ async function handleStopScheduler() {
|
||||
try {
|
||||
await stopScheduler();
|
||||
await loadSchedulerStatus();
|
||||
alert('✅ Planificateur arrêté!');
|
||||
alert('Planificateur arrêté !');
|
||||
} catch (error) {
|
||||
console.error('Error stopping scheduler:', error);
|
||||
alert(`❌ Erreur: ${error.message}`);
|
||||
alert(`Erreur : ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ async function handleCheckAll() {
|
||||
await loadSchedulerStatus();
|
||||
} catch (error) {
|
||||
console.error('Error checking all:', error);
|
||||
alert(`❌ Erreur: ${error.message}`);
|
||||
alert(`Erreur : ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ async function handleOpenSettings() {
|
||||
document.body.appendChild(modalContainer);
|
||||
} catch (error) {
|
||||
console.error('Error loading settings:', error);
|
||||
alert(`❌ Erreur: ${error.message}`);
|
||||
alert(`Erreur : ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,17 +438,17 @@ function updateSchedulerUI(status) {
|
||||
|
||||
if (status.next_run) {
|
||||
const nextRun = new Date(status.next_run);
|
||||
nextRunInfo.innerHTML = `✓ En cours<br>Prochaine vérification: ${nextRun.toLocaleString('fr-FR')}`;
|
||||
nextRunInfo.innerHTML = `<i class="fa-solid fa-check"></i> En cours<br>Prochaine vérification: ${nextRun.toLocaleString('fr-FR')}`;
|
||||
} else {
|
||||
// Scheduler running but no next_run yet (just started)
|
||||
const interval = status.settings?.check_interval_hours || 6;
|
||||
nextRunInfo.innerHTML = `✓ En cours<br>Vérification toutes les ${interval}h`;
|
||||
nextRunInfo.innerHTML = `<i class="fa-solid fa-check"></i> En cours<br>Vérification toutes les ${interval}h`;
|
||||
}
|
||||
} else {
|
||||
// Update buttons if they exist
|
||||
if (startBtn) startBtn.style.display = 'inline-block';
|
||||
if (stopBtn) stopBtn.style.display = 'none';
|
||||
nextRunInfo.innerHTML = '⏸️ Arrêté';
|
||||
nextRunInfo.innerHTML = '<i class="fa-solid fa-pause"></i> Arrêté';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user