Files
Roman 41566ab5fb v0.1.0 — Réécriture complète de OhmStreaming
Nouvelle version réécrite de zéro : recherche multi-sources (Vostfree,
French-Manga), extraction 2 niveaux, proxy vidéo intégré, streaming/téléchargement
HLS, métadonnées Kitsu, bibliothèque locale, comptes JWT + administration,
découverte fusionnée, indexeur Torznab (Sonarr/Prowlarr).
2026-09-22 10:05:47 +00:00

63 lines
2.3 KiB
HTML

{% extends "base.html" %}
{% set active = 'favorites' %}
{% block title %}Favoris — Ohm Stream{% endblock %}
{% block content %}
<h1 class="page-title">Favoris</h1>
<p class="page-sub" x-data x-text="$store.fav?.total != null ? $store.fav.total + ' titre(s) suivi(s)' : ''"></p>
<div x-data="favoritesPage()" x-init="load()">
<div class="spinner" x-show="loading"></div>
<template x-if="!loading && items.length === 0">
<div class="empty-state"><div class="big">⭐</div>Aucun favori — ajoute-en depuis une fiche de titre.</div>
</template>
<div class="grid">
<template x-for="f in items" :key="f.id">
<div class="card">
<a :href="`/title/${f.source}/${encodeURIComponent(f.source_id)}`">
<img class="card-poster" :src="f.image_url || '/static/img/placeholder.svg'" loading="lazy"
onerror="this.src='/static/img/placeholder.svg'">
</a>
<span class="card-chip" x-text="f.source"></span>
<button class="btn btn-sm btn-danger" style="position:absolute;top:0.5rem;right:0.5rem"
@click.prevent="remove(f)">✖</button>
<div class="card-overlay">
<div class="card-title" x-text="f.title"></div>
</div>
</div>
</template>
</div>
<div style="display:flex;gap:0.6rem;justify-content:center;margin-top:1.6rem" x-show="total > limit">
<button class="btn btn-ghost btn-sm" :disabled="offset === 0" @click="page(-1)">← Précédent</button>
<button class="btn btn-ghost btn-sm" :disabled="offset + limit >= total" @click="page(1)">Suivant →</button>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function favoritesPage() {
return {
items: [], total: 0, offset: 0, limit: 24, loading: true,
async load() {
this.loading = true;
const res = await fetch(`/api/favorites?offset=${this.offset}&limit=${this.limit}`);
const data = await res.json();
this.items = data.items; this.total = data.total;
Alpine.store('fav', { total: data.total });
this.loading = false;
},
async page(dir) { this.offset = Math.max(0, this.offset + dir * this.limit); await this.load(); },
async remove(f) {
await fetch('/api/favorites/' + f.id, { method: 'DELETE' });
await this.load();
toast('Favori retiré');
},
};
}
</script>
{% endblock %}