192 lines
13 KiB
HTML
192 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fi">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Torrent Vahti Ohjauspaneeli</title>
|
||
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<style>
|
||
body { background-color: #f8f9fa; font-family: system-ui, -apple-system, sans-serif; }
|
||
.card { border: none; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
|
||
.nav-tabs .nav-link { color: #495057; font-weight: 500; }
|
||
.nav-tabs .nav-link.active { font-weight: 600; }
|
||
#loki-sisalto, #skripti-loki {
|
||
background-color: #212529;
|
||
color: #f8f9fa;
|
||
padding: 15px;
|
||
border-radius: 5px;
|
||
font-size: 0.85rem;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
white-space: pre-wrap;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<nav class="navbar navbar-dark bg-dark mb-4">
|
||
<div class="container">
|
||
<span class="navbar-brand mb-0 h1">🤖 Torrent Vahti Ohjauspaneeli</span>
|
||
<span id="tila-badge" class="badge bg-secondary">Ladataan...</span>
|
||
</div>
|
||
</nav>
|
||
<div class="container mb-5">
|
||
<div class="row">
|
||
<div class="col-100">
|
||
<ul class="nav nav-tabs mb-4" id="paneeliTabs" role="tablist">
|
||
<li class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#haut-pane" type="button">📋 Hakujen Hallinta</button></li>
|
||
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#historia-pane" type="button">⏳ Lataushistoria</button></li>
|
||
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#asetukset-pane" type="button">⚙️ Järjestelmäasetukset</button></li>
|
||
</ul>
|
||
<div class="tab-content">
|
||
<div class="tab-pane fade show active" id="haut-pane">
|
||
<div class="card p-4 mb-4">
|
||
<h5>➕ Lisää uusi haku</h5>
|
||
<form id="lisaa-form" class="row g-3">
|
||
<div class="col-md-6"><input type="text" id="haku-sana" class="form-control form-control-sm" placeholder="Haku" required></div>
|
||
<input type="hidden" id="haku-regex" value=".*">
|
||
<div class="col-md-5"><input type="text" id="haku-kansio" class="form-control form-control-sm" placeholder="Kansio" required></div>
|
||
<div class="col-md-12">
|
||
<label class="small text-muted">Aktiiviset päivät:</label><br>
|
||
<div class="btn-group btn-group-sm" role="group">
|
||
<input type="checkbox" class="btn-check" id="pv-0" checked><label class="btn btn-outline-secondary" for="pv-0">Ma</label>
|
||
<input type="checkbox" class="btn-check" id="pv-1" checked><label class="btn btn-outline-secondary" for="pv-1">Ti</label>
|
||
<input type="checkbox" class="btn-check" id="pv-2" checked><label class="btn btn-outline-secondary" for="pv-2">Ke</label>
|
||
<input type="checkbox" class="btn-check" id="pv-3" checked><label class="btn btn-outline-secondary" for="pv-3">To</label>
|
||
<input type="checkbox" class="btn-check" id="pv-4" checked><label class="btn btn-outline-secondary" for="pv-4">Pe</label>
|
||
<input type="checkbox" class="btn-check" id="pv-5" checked><label class="btn btn-outline-secondary" for="pv-5">La</label>
|
||
<input type="checkbox" class="btn-check" id="pv-6" checked><label class="btn btn-outline-secondary" for="pv-6">Su</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-1"><button type="submit" class="btn btn-success btn-sm w-100">Lisää</button></div>
|
||
</form>
|
||
</div>
|
||
<div class="card p-4">
|
||
<h5>Aktiiviset haut</h5>
|
||
<table class="table table-hover align-middle"><tbody id="hakukohteet-lista"></tbody></table>
|
||
</div>
|
||
</div>
|
||
<div class="tab-pane fade" id="historia-pane">
|
||
<div class="card p-4">
|
||
<h5>Ladatut Torrentit</h5>
|
||
<div class="d-flex gap-2 mb-3">
|
||
<input type="text" id="historia-haku" class="form-control form-control-sm" placeholder="Suodata nimen mukaan..." oninput="paivitaNakyma()">
|
||
<button id="tyhjenna-loki-btn" class="btn btn-outline-danger btn-sm text-nowrap">Tyhjennä historia</button>
|
||
</div>
|
||
<div id="loki-sisalto">Ladataan...</div>
|
||
</div>
|
||
</div>
|
||
<div class="tab-pane fade" id="asetukset-pane">
|
||
<div class="card p-4">
|
||
<form id="asetukset-form">
|
||
<h6 class="fw-bold mb-3 text-primary">📡 Jackett Asetukset</h6>
|
||
<div class="mb-3"><input type="url" id="cfg-url" class="form-control form-control-sm" placeholder="Jackett URL"></div>
|
||
<div class="mb-4"><input type="text" id="cfg-apikey" class="form-control form-control-sm" placeholder="Jackett API-avain"></div>
|
||
<hr class="my-4">
|
||
<h6 class="fw-bold mb-3 text-success">🐾 qBittorrent Asetukset</h6>
|
||
<div class="mb-2"><input type="url" id="cfg-qbiturl" class="form-control form-control-sm" placeholder="Web UI URL"></div>
|
||
<div class="row mb-2">
|
||
<div class="col-sm-6"><input type="text" id="cfg-qbituser" class="form-control form-control-sm" placeholder="Käyttäjätunnus"></div>
|
||
<div class="col-sm-6"><input type="text" id="cfg-qbitpass" class="form-control form-control-sm" placeholder="Salasana/API"></div>
|
||
</div>
|
||
<button type="submit" class="btn btn-primary btn-sm mt-3">Tallenna kaikki asetukset</button>
|
||
</form>
|
||
<hr class="mt-5">
|
||
<h6 class="fw-bold mb-3 text-warning">🔍 Jackett Indexerit</h6>
|
||
<div class="input-group mb-3" style="max-width: 400px;">
|
||
<input type="text" id="uusi-indexer" class="form-control form-control-sm" placeholder="Indexer ID">
|
||
<button type="button" onclick="lisaaIndexer()" class="btn btn-outline-secondary btn-sm">Lisää</button>
|
||
</div>
|
||
<ul id="indexer-lista" class="list-group mb-3" style="max-width: 400px;"></ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card p-4 mt-4 bg-dark text-light">
|
||
<div class="d-flex justify-content-between"><h5>⚡ Järjestelmäloki</h5><button id="suorita-haku-btn" class="btn btn-success btn-sm">Suorita haku</button></div>
|
||
<pre id="skripti-loki">Ladataan...</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
function rullaaLokiAlas() {
|
||
const lokit = ['loki-sisalto', 'skripti-loki'];
|
||
lokit.forEach(id => {
|
||
const el = document.getElementById(id);
|
||
el.scrollTop = el.scrollHeight;
|
||
});
|
||
}
|
||
|
||
function paivitaNakyma() {
|
||
fetch('/status').then(res => res.json()).then(data => {
|
||
document.getElementById('tila-badge').innerText = "Valmis";
|
||
document.getElementById('tila-badge').className = "badge bg-success";
|
||
|
||
const lista = document.getElementById('hakukohteet-lista');
|
||
lista.innerHTML = data.hakukohteet.map((k, i) => `<tr><td>${k.jackett_haku}</td><td>${k.kohdekansio}</td><td class="text-end"><button onclick="poistaKohde(${i})" class="btn btn-link text-danger btn-sm p-0">Poista</button></td></tr>`).join('');
|
||
|
||
const hakuTermi = document.getElementById('historia-haku').value.toLowerCase();
|
||
const lokiDiv = document.getElementById('loki-sisalto');
|
||
const kaikkiRivit = (data.loki_torrentit || '').split('\n').filter(r => r.trim() !== '');
|
||
|
||
lokiDiv.innerHTML = kaikkiRivit
|
||
.filter(r => r.toLowerCase().includes(hakuTermi))
|
||
.map((r) => {
|
||
return `<div class="d-flex justify-content-between border-bottom py-1">
|
||
<span class="font-monospace small text-truncate" style="max-width: 80%;" title="${r}">${r}</span>
|
||
<button onclick="poistaHist('${r.replace(/'/g, "\\'")}')" class="btn btn-outline-danger btn-sm py-0">Poista</button>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
const iList = document.getElementById('indexer-lista');
|
||
iList.innerHTML = (data.indeksorit || []).map((idx, i) => `<li class="list-group-item d-flex justify-content-between py-1">${idx}<button onclick="poistaIndexer(${i})" class="btn btn-danger btn-sm py-0">Poista</button></li>`).join('');
|
||
|
||
document.getElementById('skripti-loki').innerText = data.loki_skripti;
|
||
document.getElementById('cfg-url').value = data.asetukset.jackett_url || '';
|
||
document.getElementById('cfg-apikey').value = data.asetukset.jackett_api_key || '';
|
||
document.getElementById('cfg-qbiturl').value = data.asetukset.qbit_url || '';
|
||
document.getElementById('cfg-qbituser').value = data.asetukset.qbit_user || '';
|
||
document.getElementById('cfg-qbitpass').value = data.asetukset.qbit_pass || '';
|
||
|
||
rullaaLokiAlas();
|
||
});
|
||
}
|
||
|
||
function poistaHist(riviTeksti) {
|
||
if(confirm("Poistetaanko tämä rivi historiasta?")) {
|
||
fetch('/poista_historiarivi', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify({ rivi: riviTeksti })
|
||
}).then(() => paivitaNakyma());
|
||
}
|
||
}
|
||
|
||
function lisaaIndexer() { const val = document.getElementById('uusi-indexer').value; if(!val) return; fetch('/status').then(r => r.json()).then(d => { let l = d.indeksorit || []; l.push(val); fetch('/paivita_indeksorit', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({indeksorit:l})}).then(() => paivitaNakyma()); }); }
|
||
function poistaIndexer(i) { fetch('/status').then(r => r.json()).then(d => { let l = d.indeksorit; l.splice(i, 1); fetch('/paivita_indeksorit', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({indeksorit:l})}).then(() => paivitaNakyma()); }); }
|
||
function poistaKohde(i) { fetch(`/poista_kohde/${i}`, {method:'DELETE'}).then(() => paivitaNakyma()); }
|
||
|
||
document.getElementById('lisaa-form').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
const paivat = [0,1,2,3,4,5,6].filter(i => document.getElementById('pv-'+i).checked);
|
||
fetch('/lisaa_kohde', {
|
||
method:'POST',
|
||
headers:{'Content-Type':'application/json'},
|
||
body:JSON.stringify({
|
||
jackett_haku: document.getElementById('haku-sana').value,
|
||
regex_suodatin: document.getElementById('haku-regex').value,
|
||
kohdekansio: document.getElementById('haku-kansio').value,
|
||
paivat: paivat
|
||
})
|
||
}).then(() => { e.target.reset(); paivitaNakyma(); });
|
||
});
|
||
document.getElementById('asetukset-form').addEventListener('submit', (e) => { e.preventDefault(); fetch('/tallenna_asetukset', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({jackett_url:document.getElementById('cfg-url').value, jackett_api_key:document.getElementById('cfg-apikey').value, qbit_url:document.getElementById('cfg-qbiturl').value, qbit_user:document.getElementById('cfg-qbituser').value, qbit_pass:document.getElementById('cfg-qbitpass').value})}).then(() => paivitaNakyma()); });
|
||
document.getElementById('suorita-haku-btn').addEventListener('click', () => fetch('/suorita_haku', {method:'POST'}));
|
||
document.getElementById('tyhjenna-loki-btn').addEventListener('click', () => fetch('/tyhjenna_loki', {method:'POST'}).then(() => paivitaNakyma()));
|
||
|
||
paivitaNakyma(); setInterval(paivitaNakyma, 5000);
|
||
</script>
|
||
</body>
|
||
</html>
|