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).
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active = 'search' %}
|
||||
{% block title %}Recherche — Ohm Stream{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="page-title">Recherche unifiée</h1>
|
||||
<p class="page-sub">Une seule requête interroge toutes tes sources activées.</p>
|
||||
|
||||
<div x-data="searchPage()">
|
||||
<form class="search-bar" @submit.prevent="run">
|
||||
<input type="search" x-model="query" placeholder="Naruto, One Piece, Frieren…" autofocus>
|
||||
<button class="btn" type="submit" :disabled="loading">Rechercher</button>
|
||||
</form>
|
||||
|
||||
<div class="spinner" x-show="loading"></div>
|
||||
|
||||
<template x-if="error">
|
||||
<div class="login-error" x-text="error"></div>
|
||||
</template>
|
||||
|
||||
<template x-if="!loading && searched && results.length === 0">
|
||||
<div class="empty-state"><div class="big">🏜️</div>Aucun résultat pour cette recherche.</div>
|
||||
</template>
|
||||
|
||||
<div class="grid" x-show="results.length > 0">
|
||||
<template x-for="r in results" :key="r.source + r.source_id">
|
||||
<a class="card" :href="`/title/${r.source}/${encodeURIComponent(r.source_id)}`">
|
||||
<img class="card-poster" :src="r.image_url || '/static/img/placeholder.svg'" :alt="r.title" loading="lazy"
|
||||
onerror="this.src='/static/img/placeholder.svg'">
|
||||
<span class="card-chip" x-text="r.source"></span>
|
||||
<div class="card-overlay">
|
||||
<div class="card-title" x-text="r.title"></div>
|
||||
<div class="card-meta">
|
||||
<span class="badge badge-type" x-text="r.media_type"></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function searchPage() {
|
||||
return {
|
||||
query: new URLSearchParams(location.search).get('q') || '',
|
||||
results: [], loading: false, searched: false, error: null,
|
||||
init() { if (this.query) this.run(); },
|
||||
async run() {
|
||||
if (this.query.trim().length < 2) return;
|
||||
this.loading = true; this.error = null; this.searched = false;
|
||||
history.replaceState(null, '', '?q=' + encodeURIComponent(this.query));
|
||||
try {
|
||||
const res = await fetch('/api/search?q=' + encodeURIComponent(this.query.trim()));
|
||||
if (!res.ok) throw new Error('Erreur ' + res.status);
|
||||
const data = await res.json();
|
||||
this.results = data.results;
|
||||
if (data.failed_sources.length) this.error = 'Sources en échec : ' + data.failed_sources.join(', ');
|
||||
} catch (e) { this.error = e.message; }
|
||||
finally { this.loading = false; this.searched = true; }
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user