add service
This commit is contained in:
parent
7b97e74b39
commit
feddf3f7a5
@ -1,45 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="fr" class="h-full">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>{% block title %}Kodi - Films{% endblock %}</title>
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
if (stored === 'dark' || (!stored && prefersDark)) {
|
|
||||||
document.documentElement.classList.add('dark');
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove('dark');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
|
||||||
<script>
|
|
||||||
tailwind.config = { darkMode: 'class' };
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body class="h-full bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
|
||||||
<div class="max-w-7xl mx-auto p-4 md:p-6">
|
|
||||||
<header class="flex items-center gap-3 mb-4">
|
|
||||||
<h1 class="text-xl md:text-2xl font-semibold">Kodi - Films</h1>
|
|
||||||
<div class="ml-auto flex items-center gap-2">
|
|
||||||
<button id="themeToggle" class="px-3 py-1 rounded-lg border dark:border-gray-700 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700">
|
|
||||||
<span class="inline dark:hidden">🌙 Mode sombre</span>
|
|
||||||
<span class="hidden dark:inline">☀️ Mode clair</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
{% block content %}{% endblock %}
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
document.getElementById('themeToggle')?.addEventListener('click', () => {
|
|
||||||
const el = document.documentElement;
|
|
||||||
const newTheme = el.classList.contains('dark') ? 'light' : 'dark';
|
|
||||||
el.classList.toggle('dark');
|
|
||||||
localStorage.setItem('theme', newTheme);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% block title %}Erreur{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200 p-4 rounded-2xl shadow">
|
|
||||||
<h1 class="text-xl font-semibold mb-2">Erreur</h1>
|
|
||||||
<pre class="whitespace-pre-wrap text-sm">{{ error }}</pre>
|
|
||||||
<p class="mt-4 text-sm text-gray-600 dark:text-gray-300">Vérifie le chemin de la base (<code>KODI_DB_PATH</code>) et tes permissions.</p>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% block title %}{{ m.nom }} - Détail{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<a href="javascript:history.back()" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline">← Retour</a>
|
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow p-6 mt-3 border border-gray-200 dark:border-gray-700">
|
|
||||||
<div class="flex gap-6">
|
|
||||||
<div>
|
|
||||||
{% if m.poster %}
|
|
||||||
<img src="{{ m.poster }}" alt="poster" class="w-40 object-cover rounded-xl shadow">
|
|
||||||
{% else %}
|
|
||||||
<div class="w-40 h-60 bg-gray-200 dark:bg-gray-700 rounded-xl"></div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
<h1 class="text-2xl font-bold mb-2">{{ m.nom }}</h1>
|
|
||||||
<div class="text-gray-600 dark:text-gray-300 mb-4">
|
|
||||||
{% if m.year %}<span class="mr-2">{{ m.year }}</span>{% endif %}
|
|
||||||
{% if m.pays %}<span class="mr-2">• {{ m.pays }}</span>{% endif %}
|
|
||||||
{% if m.date %}<span class="mr-2">• {{ m.date }}</span>{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if m.titre %}
|
|
||||||
<div class="mb-2"><span class="font-medium">Titre original :</span> {{ m.titre }}</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if m.acteurs %}
|
|
||||||
<div class="mb-2"><span class="font-medium">Acteurs :</span> {{ m.acteurs }}</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if m.desc %}
|
|
||||||
<div class="mt-4">
|
|
||||||
<h2 class="font-semibold mb-1">Résumé</h2>
|
|
||||||
<p class="whitespace-pre-wrap">{{ m.desc }}</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if m.path %}
|
|
||||||
<div class="mt-4">
|
|
||||||
<h2 class="font-semibold mb-1">Chemin</h2>
|
|
||||||
<code class="text-sm">{{ m.path }}</code>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,201 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% block title %}Kodi - Films{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow p-4 md:p-6 mb-6 border border-gray-200 dark:border-gray-700">
|
|
||||||
<form method="get" class="grid grid-cols-1 md:grid-cols-6 gap-3 items-end">
|
|
||||||
<!-- Hidden to preserve state -->
|
|
||||||
<input type="hidden" name="view" value="{{ view }}">
|
|
||||||
<input type="hidden" name="dir" value="{{ direction }}">
|
|
||||||
<input type="hidden" name="page" value="{{ page }}">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Recherche</label>
|
|
||||||
<input type="text" name="q" value="{{ q }}" placeholder="Titre / synopsis"
|
|
||||||
class="w-full rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Acteur</label>
|
|
||||||
<input type="text" name="actor" value="{{ actor }}" placeholder="ex: Tom Hanks"
|
|
||||||
class="w-full rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Pays</label>
|
|
||||||
<input type="text" name="country" value="{{ country }}" placeholder="ex: France"
|
|
||||||
class="w-full rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Année min</label>
|
|
||||||
<input type="number" name="year_from" value="{{ year_from }}" min="1900" max="2100"
|
|
||||||
class="w-full rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Année max</label>
|
|
||||||
<input type="number" name="year_to" value="{{ year_to }}" min="1900" max="2100"
|
|
||||||
class="w-full rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<button type="submit"
|
|
||||||
class="rounded-xl px-4 py-2 bg-indigo-600 text-white font-medium hover:bg-indigo-700 shadow">
|
|
||||||
Filtrer
|
|
||||||
</button>
|
|
||||||
<a href="/?view={{ view }}"
|
|
||||||
class="rounded-xl px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 font-medium hover:bg-gray-200 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-600">
|
|
||||||
Réinitialiser
|
|
||||||
</a>
|
|
||||||
<a href="/export.csv?{{ build_querystring() }}"
|
|
||||||
class="rounded-xl px-4 py-2 bg-emerald-600 text-white font-medium hover:bg-emerald-700 shadow">
|
|
||||||
Export CSV
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="md:col-span-6 flex items-center gap-2 mt-1">
|
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300">Vue :</span>
|
|
||||||
<a href="/?{{ build_querystring(view='gallery', page=1, per_page=24) }}"
|
|
||||||
class="text-sm px-3 py-1 rounded-lg border {{ 'bg-indigo-600 text-white' if view=='gallery' else 'bg-white dark:bg-gray-900 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 border-gray-300 dark:border-gray-600' }}">
|
|
||||||
Galerie
|
|
||||||
</a>
|
|
||||||
<a href="/?{{ build_querystring(view='list', page=1, per_page=25) }}"
|
|
||||||
class="text-sm px-3 py-1 rounded-lg border {{ 'bg-indigo-600 text-white' if view=='list' else 'bg-white dark:bg-gray-900 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 border-gray-300 dark:border-gray-600' }}">
|
|
||||||
Liste
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="ml-auto flex items-center gap-2">
|
|
||||||
<label class="text-sm text-gray-600 dark:text-gray-300">Trier par :</label>
|
|
||||||
<select name="sort"
|
|
||||||
onchange="this.form.page.value=1; this.form.submit()"
|
|
||||||
class="rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 py-1">
|
|
||||||
{% for key,label in {'name':'Nom','original':'Titre','actors':'Acteurs','country':'Pays','year':'Année','date':'Date','path':'Chemin'}.items() %}
|
|
||||||
<option value="{{key}}" {% if sort==key %}selected{% endif %}>{{label}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% set dir_toggle = ('desc' if direction=='asc' else 'asc') %}
|
|
||||||
<a href="/?{{ build_querystring(dir=dir_toggle, page=1) }}"
|
|
||||||
class="text-sm px-3 py-1 rounded-lg border bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
||||||
{{ '▲ Asc' if direction=='asc' else '▼ Desc' }}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<label class="text-sm text-gray-600 dark:text-gray-300 ml-3">Par page :</label>
|
|
||||||
<select name="per_page"
|
|
||||||
onchange="this.form.page.value=1; this.form.submit()"
|
|
||||||
class="rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 py-1">
|
|
||||||
{% if view=='gallery' %}
|
|
||||||
{% for n in (12,24,48,96) %}
|
|
||||||
<option value="{{n}}" {% if per_page==n %}selected{% endif %}>{{n}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% for n in (10,25,50,100) %}
|
|
||||||
<option value="{{n}}" {% if per_page==n %}selected{% endif %}>{{n}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3 text-sm text-gray-600 dark:text-gray-300">
|
|
||||||
Résultats: <span class="font-semibold">{{ total }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if view == 'gallery' %}
|
|
||||||
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
|
||||||
{% for m in movies %}
|
|
||||||
<a href="{% if m.id %}/movie/{{ m.id }}{% elif m.path %}/movie/by-path?path={{ m.path | urlencode }}{% else %}#{% endif %}"
|
|
||||||
class="group block bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl overflow-hidden shadow hover:shadow-md transition">
|
|
||||||
{% if m.poster %}
|
|
||||||
<img src="{{ m.poster }}" alt="poster" class="w-full aspect-[2/3] object-cover">
|
|
||||||
{% else %}
|
|
||||||
<div class="w-full aspect-[2/3] bg-gray-200 dark:bg-gray-700"></div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="p-2">
|
|
||||||
<div class="text-sm font-medium line-clamp-2 group-hover:underline">{{ m.nom|default('—') }}</div>
|
|
||||||
{% if m.year %}
|
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1">{{ m.year }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
{% if movies|length == 0 %}
|
|
||||||
<div class="col-span-full text-center text-gray-500 dark:text-gray-400 py-8">Aucun résultat.</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow overflow-x-auto border border-gray-200 dark:border-gray-700">
|
|
||||||
<table class="min-w-full text-sm">
|
|
||||||
<thead class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200">
|
|
||||||
<tr>
|
|
||||||
<th class="text-left px-4 py-2">Poster</th>
|
|
||||||
<th class="text-left px-4 py-2">Nom</th>
|
|
||||||
<th class="text-left px-4 py-2">Titre originel</th>
|
|
||||||
<th class="text-left px-4 py-2">Acteurs</th>
|
|
||||||
<th class="text-left px-4 py-2">Pays</th>
|
|
||||||
<th class="text-left px-4 py-2">Année</th>
|
|
||||||
<th class="text-left px-4 py-2">Date</th>
|
|
||||||
<th class="text-left px-4 py-2">Chemin</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for m in movies %}
|
|
||||||
<tr class="border-t border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/40">
|
|
||||||
<td class="px-4 py-2">
|
|
||||||
{% if m.poster %}
|
|
||||||
<img src="{{ m.poster }}" alt="poster" class="w-10 h-16 object-cover rounded-lg shadow">
|
|
||||||
{% else %}
|
|
||||||
<div class="w-10 h-16 bg-gray-200 dark:bg-gray-700 rounded-lg"></div>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-2">
|
|
||||||
{% if m.id %}
|
|
||||||
<a href="/movie/{{ m.id }}" class="text-indigo-600 dark:text-indigo-400 hover:underline">{{ m.nom|default('—') }}</a>
|
|
||||||
{% elif m.path %}
|
|
||||||
<a href="/movie/by-path?path={{ m.path | urlencode }}" class="text-indigo-600 dark:text-indigo-400 hover:underline">{{ m.nom|default('—') }}</a>
|
|
||||||
{% else %}
|
|
||||||
<span>{{ m.nom|default('—') }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-2">{{ m.titre|none_to_dash }}</td>
|
|
||||||
<td class="px-4 py-2"><div class="line-clamp-2">{{ m.acteurs|none_to_dash }}</div></td>
|
|
||||||
<td class="px-4 py-2">{{ m.pays|none_to_dash }}</td>
|
|
||||||
<td class="px-4 py-2">{{ m.year if m.year else '—' }}</td>
|
|
||||||
<td class="px-4 py-2">{{ m.date if m.date else '—' }}</td>
|
|
||||||
<td class="px-4 py-2"><code class="text-xs">{{ m.path|none_to_dash }}</code></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% if movies|length == 0 %}
|
|
||||||
<tr><td colspan="8" class="px-4 py-6 text-center text-gray-500 dark:text-gray-400">Aucun résultat.</td></tr>
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if last_page > 1 %}
|
|
||||||
<nav class="mt-6 flex items-center justify-center gap-2 flex-wrap">
|
|
||||||
<a href="/?{{ build_querystring(page=1) }}"
|
|
||||||
class="px-3 py-1 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 {{ 'opacity-60 pointer-events-none' if page==1 else 'hover:bg-gray-100 dark:hover:bg-gray-800' }}">«</a>
|
|
||||||
<a href="/?{{ build_querystring(page=page-1) }}"
|
|
||||||
class="px-3 py-1 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800">‹</a>
|
|
||||||
|
|
||||||
{% if start_page > 1 %}
|
|
||||||
<span class="px-2">…</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% for p in range(start_page, end_page + 1) %}
|
|
||||||
<a href="/?{{ build_querystring(page=p) }}"
|
|
||||||
class="px-3 py-1 rounded-lg border border-gray-300 dark:border-gray-700 {{ 'bg-indigo-600 text-white' if p==page else 'bg-white dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800' }}">{{ p }}</a>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% if end_page < last_page %}
|
|
||||||
<span class="px-2">…</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a href="/?{{ build_querystring(page=page+1) }}"
|
|
||||||
class="px-3 py-1 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800">›</a>
|
|
||||||
<a href="/?{{ build_querystring(page=last_page) }}"
|
|
||||||
class="px-3 py-1 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 {{ 'opacity-60 pointer-events-none' if page==last_page else 'hover:bg-gray-100 dark:hover:bg-gray-800' }}">»</a>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
9
webfilm.service
Normal file
9
webfilm.service
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description= Media on kodi service
|
||||||
|
After=multi-user.target
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
ExecStart=/usr/bin/python3 /data/web_film/app.py
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Loading…
x
Reference in New Issue
Block a user