Add matrix background and prepare filters
This commit is contained in:
@@ -18,6 +18,8 @@ MAX_ATTEMPTS = 5
|
|||||||
BLOCK_TIME = timedelta(minutes=15)
|
BLOCK_TIME = timedelta(minutes=15)
|
||||||
|
|
||||||
MAX_SIZE_BYTES = 5*1024**3
|
MAX_SIZE_BYTES = 5*1024**3
|
||||||
|
NB_PAGES = 2
|
||||||
|
|
||||||
YGG_PASSKEY = "xj1MgNuyzFKCjOtnawGBC2egDOciUg04"
|
YGG_PASSKEY = "xj1MgNuyzFKCjOtnawGBC2egDOciUg04"
|
||||||
ALLDEBRID_KEY = "mtrQI4h583rHe2ZpvpbC"
|
ALLDEBRID_KEY = "mtrQI4h583rHe2ZpvpbC"
|
||||||
|
|
||||||
@@ -87,18 +89,31 @@ def init_app(app):
|
|||||||
|
|
||||||
# Préparer l'URL
|
# Préparer l'URL
|
||||||
url = "https://yggapi.eu/torrents"
|
url = "https://yggapi.eu/torrents"
|
||||||
|
|
||||||
|
# Préparer les paramètres
|
||||||
params = {
|
params = {
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"q": query,
|
"q": query,
|
||||||
"category_id" : category_id,
|
"order_by": "uploaded_at",
|
||||||
"order_by": "uploaded_at",
|
"per_page": 100
|
||||||
"per_page": 100
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Ajouter la catégorie seulement si elle est renseignée et non vide
|
||||||
|
if category_id:
|
||||||
|
params["category_id"] = category_id
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
# Appeler l'API
|
# Appeler l'API
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, params=params, timeout=5)
|
for page in range(1, NB_PAGES):
|
||||||
response.raise_for_status() # déclenche une exception si erreur HTTP
|
params['page'] = page
|
||||||
results = response.json()
|
print("Appel API page", page, "avec params:", params)
|
||||||
|
response = requests.get(url, params=params, timeout=5)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json() # ici c'est une liste directement
|
||||||
|
print(f"Nombre de torrents reçus page {page}:", len(data))
|
||||||
|
results.extend(data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Erreur API Yggtorrent:", e)
|
print("Erreur API Yggtorrent:", e)
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@@ -1,18 +1,49 @@
|
|||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
background-color: black;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
margin-bottom: 20px;
|
color: #00ff41;
|
||||||
}
|
text-shadow: 0 0 10px #00ff41;
|
||||||
table {
|
margin-bottom: 50px;
|
||||||
width: 100%;
|
text-align: center;
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
padding: 5px 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
background-color: #f0f0f0;
|
/* background-color: rgba(0, 0, 0, 0.8); */
|
||||||
|
background-color: #00ff41;
|
||||||
|
color: black;
|
||||||
|
font-weight:bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display:inline;
|
||||||
|
margin-right:10px;
|
||||||
|
color:white;
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#results-table td:nth-child(2),
|
||||||
|
#results-table td:nth-child(3),
|
||||||
|
#results-table td:nth-child(4),
|
||||||
|
#results-table th:nth-child(2),
|
||||||
|
#results-table th:nth-child(3),
|
||||||
|
#results-table th:nth-child(4) {
|
||||||
|
white-space: nowrap; /* empêche le retour à la ligne */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
76
frontend/static/js/matrix.js
Normal file
76
frontend/static/js/matrix.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
const canvas = document.getElementById('matrix');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
const chars = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンあいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん0123456789!#$%&'()*+,-./:;<=>?@";
|
||||||
|
const fontSize = 14;
|
||||||
|
let columns = Math.floor(canvas.width / fontSize);
|
||||||
|
let drops = Array.from({ length: columns }, () => Math.random() * canvas.height / fontSize);
|
||||||
|
|
||||||
|
// Récupère les zones à masquer : formulaire + tableau
|
||||||
|
function getMaskRects() {
|
||||||
|
const rects = [];
|
||||||
|
const form = document.getElementById('search-form');
|
||||||
|
if (form) rects.push(form.getBoundingClientRect());
|
||||||
|
const table = document.getElementById('results-table');
|
||||||
|
if (table) rects.push(table.getBoundingClientRect());
|
||||||
|
return rects;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcul de l'opacité selon distance au centre de la zone
|
||||||
|
function opacityForPosition(x, y, rects) {
|
||||||
|
if (!rects || rects.length === 0) return 1;
|
||||||
|
let minOpacity = 1;
|
||||||
|
rects.forEach(rect => {
|
||||||
|
const cx = rect.left + rect.width / 2;
|
||||||
|
const cy = rect.top + rect.height / 2;
|
||||||
|
const dx = x - cx;
|
||||||
|
const dy = y - cy;
|
||||||
|
const distance = Math.sqrt(dx*dx + dy*dy);
|
||||||
|
const radius = Math.max(rect.width, rect.height) * 0.8;
|
||||||
|
|
||||||
|
let alpha;
|
||||||
|
const inner = radius * 0.3; // centre complètement transparent réduit
|
||||||
|
const outer = radius; // bord complet visible
|
||||||
|
|
||||||
|
if (distance < inner) alpha = 0;
|
||||||
|
else if (distance > outer) alpha = 1;
|
||||||
|
else alpha = (distance - inner) / (outer - inner); // transition plus large
|
||||||
|
|
||||||
|
if (alpha < minOpacity) minOpacity = alpha;
|
||||||
|
});
|
||||||
|
return minOpacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.fillStyle = "rgba(0,0,0,0.05)";
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
ctx.font = fontSize + "px monospace";
|
||||||
|
|
||||||
|
const maskRects = getMaskRects();
|
||||||
|
|
||||||
|
for (let i = 0; i < drops.length; i++) {
|
||||||
|
const text = chars.charAt(Math.floor(Math.random() * chars.length));
|
||||||
|
const x = i * fontSize;
|
||||||
|
const y = drops[i] * fontSize;
|
||||||
|
|
||||||
|
const alpha = opacityForPosition(x, y, maskRects);
|
||||||
|
ctx.fillStyle = `rgba(0,255,65,${alpha})`;
|
||||||
|
ctx.fillText(text, x, y);
|
||||||
|
|
||||||
|
if (y > canvas.height && Math.random() > 0.98) drops[i] = 0;
|
||||||
|
drops[i]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(draw, 20);
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
columns = Math.floor(canvas.width / fontSize);
|
||||||
|
drops = Array.from({ length: columns }, () => Math.random() * canvas.height / fontSize);
|
||||||
|
});
|
||||||
@@ -4,14 +4,30 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}Yggtorrent App{% endblock %}</title>
|
<title>{% block title %}Yggtorrent App{% endblock %}</title>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
|
<style>
|
||||||
|
/* Position du fond Matrix */
|
||||||
|
#matrix {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none; /* pour éviter tout blocage des clics */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Fond Matrix -->
|
||||||
|
{% block matrix %}{% endblock %}
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1>Les films de Lulu</h1>
|
<h1>Les films de Lulu</h1>
|
||||||
{% if user %}
|
{% if user %}
|
||||||
<p>Connecté en tant que {{ user }}</p>
|
<p class="user-info">
|
||||||
<form action="{{ url_for('logout') }}" method="GET" style="display:inline">
|
Connecté en tant que {{ user }}
|
||||||
|
</p>
|
||||||
|
<form action="{{ url_for('logout') }}" method="GET" style="display:inline;">
|
||||||
<button type="submit">Déconnexion</button>
|
<button type="submit">Déconnexion</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -2,25 +2,149 @@
|
|||||||
|
|
||||||
{% block title %}Tableau de bord{% endblock %}
|
{% block title %}Tableau de bord{% endblock %}
|
||||||
|
|
||||||
|
{% block matrix %}
|
||||||
|
<canvas id="matrix"></canvas>
|
||||||
|
<script src="{{ url_for('static', filename='js/matrix.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form id="search-form" action="/search" method="GET">
|
<form id="search-form" action="/search" method="GET" style="display: flex; gap: 10px; align-items: flex-stretch; flex-wrap: wrap;">
|
||||||
<label>Catégorie</label>
|
|
||||||
<select name="category_id">
|
|
||||||
<option value="2178">Films d’animation</option>
|
|
||||||
<option value="2179">Séries d’animation / Mangas</option>
|
|
||||||
<option value="2181">Documentaires</option>
|
|
||||||
<option value="2182">Émissions TV</option>
|
|
||||||
<option value="2183">Films</option>
|
|
||||||
<option value="2184">Séries</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label>Recherche</label>
|
<!-- Colonne principale des filtres -->
|
||||||
<input type="text" name="query" required>
|
<div style="display: flex; flex-direction: column; gap: 10px;">
|
||||||
|
|
||||||
<button type="submit">Rechercher</button>
|
<!-- Ligne 1 : Catégorie et Recherche -->
|
||||||
|
<div style="display: flex; gap: 10px; align-items: flex-start">
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
<label class="bold-label">Catégorie</label>
|
||||||
|
<select name="category_id">
|
||||||
|
<option value="">Tout</option>
|
||||||
|
<optgroup label="🎬 Film / Vidéo">
|
||||||
|
<option value="2178">Film/Vidéo : Animation</option>
|
||||||
|
<option value="2179">Film/Vidéo : Animation Série</option>
|
||||||
|
<option value="2180">Film/Vidéo : Concert</option>
|
||||||
|
<option value="2181">Film/Vidéo : Documentaire</option>
|
||||||
|
<option value="2182">Film/Vidéo : Émission TV</option>
|
||||||
|
<option value="2183">Film/Vidéo : Film</option>
|
||||||
|
<option value="2184">Film/Vidéo : Série TV</option>
|
||||||
|
<option value="2185">Film/Vidéo : Spectacle</option>
|
||||||
|
<option value="2186">Film/Vidéo : Sport</option>
|
||||||
|
<option value="2187">Film/Vidéo : Vidéo-clips</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="🎧 Audio">
|
||||||
|
<option value="2147">Audio : Karaoké</option>
|
||||||
|
<option value="2148">Audio : Musique</option>
|
||||||
|
<option value="2150">Audio : Podcast / Radio</option>
|
||||||
|
<option value="2149">Audio : Samples</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="💻 Application">
|
||||||
|
<option value="2177">Application : Autre</option>
|
||||||
|
<option value="2176">Application : Formation</option>
|
||||||
|
<option value="2171">Application : Linux</option>
|
||||||
|
<option value="2172">Application : MacOS</option>
|
||||||
|
<option value="2174">Application : Smartphone</option>
|
||||||
|
<option value="2175">Application : Tablette</option>
|
||||||
|
<option value="2173">Application : Windows</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="🕹️ Jeu vidéo">
|
||||||
|
<option value="2167">Jeu vidéo : Autre</option>
|
||||||
|
<option value="2159">Jeu vidéo : Linux</option>
|
||||||
|
<option value="2160">Jeu vidéo : MacOS</option>
|
||||||
|
<option value="2161">Jeu vidéo : Windows</option>
|
||||||
|
<option value="2162">Jeu vidéo : Microsoft / Xbox</option>
|
||||||
|
<option value="2163">Jeu vidéo : Nintendo</option>
|
||||||
|
<option value="2164">Jeu vidéo : Sony / PlayStation</option>
|
||||||
|
<option value="2165">Jeu vidéo : Smartphone</option>
|
||||||
|
<option value="2166">Jeu vidéo : Tablette</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="📚 eBook">
|
||||||
|
<option value="2151">eBook : Audio</option>
|
||||||
|
<option value="2152">eBook : BDs</option>
|
||||||
|
<option value="2153">eBook : Comics</option>
|
||||||
|
<option value="2154">eBook : Livres</option>
|
||||||
|
<option value="2155">eBook : Mangas</option>
|
||||||
|
<option value="2156">eBook : Presse</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; width: 100%; box-sizing: border-box;">
|
||||||
|
<label class="bold-label">Recherche</label>
|
||||||
|
<input type="text" name="query" placeholder="Gladiator" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ligne 2 : Langue et Taille max -->
|
||||||
|
<div style="display: flex; gap: 10px; align-items: flex-start;">
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
<label class="bold-label">Langue</label>
|
||||||
|
<select name="language">
|
||||||
|
<option value="">Toutes</option>
|
||||||
|
<option value="VO">VO</option>
|
||||||
|
<option value="VF">VF</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
<label class="bold-label">Taille max (Go)</label>
|
||||||
|
<input type="number" name="max_size" min="1" step="1" value="15">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ligne 3 : Série -->
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 5px;">
|
||||||
|
<label class="bold-label">Série</label>
|
||||||
|
<div style="display: flex; gap: 10px;">
|
||||||
|
<label><input type="radio" name="serie_type" value="all" checked> Tout</label>
|
||||||
|
<label><input type="radio" name="serie_type" value="season"> Saison entière</label>
|
||||||
|
<label><input type="radio" name="serie_type" value="episode"> Épisode précis</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Champs conditionnels -->
|
||||||
|
<div id="season-field" style="display: none; margin-top: 5px;">
|
||||||
|
<label>Saison</label>
|
||||||
|
<input type="number" name="season" min="1" step="1" placeholder="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="episode-field" style="display: none; margin-top: 5px;">
|
||||||
|
<label>Épisode</label>
|
||||||
|
<input type="number" name="episode_number" min="1" step="1" placeholder="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Bouton Recherche sur le côté -->
|
||||||
|
<div style="display: flex; justify-content: center;">
|
||||||
|
<button type="submit" style="padding: 5px 30px;">Rechercher</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="results">
|
<div id="results">
|
||||||
<!-- Les résultats de recherche seront injectés ici -->
|
<!-- Les résultats de recherche seront injectés ici -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const radioButtons = document.querySelectorAll('input[name="serie_type"]');
|
||||||
|
const seasonField = document.getElementById('season-field');
|
||||||
|
const episodeField = document.getElementById('episode-field');
|
||||||
|
|
||||||
|
radioButtons.forEach(radio => {
|
||||||
|
radio.addEventListener('change', () => {
|
||||||
|
if (radio.value === 'all') {
|
||||||
|
seasonField.style.display = 'none';
|
||||||
|
episodeField.style.display = 'none';
|
||||||
|
} else if (radio.value === 'season') {
|
||||||
|
seasonField.style.display = 'block';
|
||||||
|
episodeField.style.display = 'none';
|
||||||
|
} else if (radio.value === 'episode') {
|
||||||
|
seasonField.style.display = 'block';
|
||||||
|
episodeField.style.display = 'block';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="/login" method="POST">
|
<form action="/login" method="POST">
|
||||||
<label>Nom d'utilisateur</label>
|
<label class="bold-label">Nom d'utilisateur</label>
|
||||||
<input type="text" name="username" required>
|
<input type="text" name="username" required>
|
||||||
<label>Mot de passe</label>
|
<label class="bold-label">Mot de passe</label>
|
||||||
<input type="password" name="password" required>
|
<input type="password" name="password" required>
|
||||||
<button type="submit">Se connecter</button>
|
<button type="submit">Se connecter</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block matrix %}
|
||||||
|
<canvas id="matrix"></canvas>
|
||||||
|
<script src="{{ url_for('static', filename='js/matrix.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Résultats pour "{{ query }}"</h2>
|
<h2>Résultats pour "{{ query }}"</h2>
|
||||||
<table id="results-table">
|
<table id="results-table">
|
||||||
|
|||||||
Reference in New Issue
Block a user