425 lines
21 KiB
HTML
425 lines
21 KiB
HTML
{{ define "config" }}
|
|
<div class="container mt-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="mb-0"><i class="bi bi-gear me-2"></i>Configuration</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<form id="configForm">
|
|
<div class="section mb-5">
|
|
<h5 class="border-bottom pb-2">General Configuration</h5>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="qbitDebug">Log Level</label>
|
|
<select class="form-select" name="qbit.log_level" id="log-level" disabled>
|
|
<option value="info">Info</option>
|
|
<option value="debug">Debug</option>
|
|
<option value="warn">Warning</option>
|
|
<option value="error">Error</option>
|
|
<option value="trace">Trace</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<!-- Register Magnet Link Button -->
|
|
<div class="col-md-6">
|
|
<label>
|
|
<!-- Empty label to keep the button aligned -->
|
|
</label>
|
|
<div class="btn btn-primary w-100" onclick="registerMagnetLinkHandler()" id="registerMagnetLink">
|
|
Open Magnet Links in Decypharr
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mt-3">
|
|
<div class="form-group">
|
|
<label for="discordWebhookUrl">Discord Webhook URL</label>
|
|
<div class="input-group">
|
|
<textarea type="text"
|
|
class="form-control"
|
|
id="discordWebhookUrl"
|
|
name="discord_webhook_url"
|
|
disabled
|
|
placeholder="https://discord..."></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mt-3">
|
|
<div class="form-group">
|
|
<label for="allowedExtensions">Allowed File Extensions</label>
|
|
<div class="input-group">
|
|
<textarea
|
|
class="form-control"
|
|
id="allowedExtensions"
|
|
name="allowed_file_types"
|
|
disabled
|
|
placeholder="mkv, mp4, avi, etc.">
|
|
</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mt-3">
|
|
<div class="form-group">
|
|
<label for="minFileSize">Minimum File Size</label>
|
|
<input type="text"
|
|
class="form-control"
|
|
id="minFileSize"
|
|
name="min_file_size"
|
|
disabled
|
|
placeholder="e.g., 10MB, 1GB">
|
|
<small class="form-text text-muted">Minimum file size to download (0 for no limit)</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6 mt-3">
|
|
<div class="form-group">
|
|
<label for="maxFileSize">Maximum File Size</label>
|
|
<input type="text"
|
|
class="form-control"
|
|
id="maxFileSize"
|
|
name="max_file_size"
|
|
disabled
|
|
placeholder="e.g., 50GB, 100MB">
|
|
<small class="form-text text-muted">Maximum file size to download (0 for no limit)</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Debrid Configuration -->
|
|
<div class="section mb-5">
|
|
<h5 class="border-bottom pb-2">Debrid Configuration</h5>
|
|
<div id="debridConfigs"></div>
|
|
</div>
|
|
|
|
<!-- QBitTorrent Configuration -->
|
|
<div class="section mb-5">
|
|
<h5 class="border-bottom pb-2">QBitTorrent Configuration</h5>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Username</label>
|
|
<input type="text" disabled class="form-control" name="qbit.username">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Password</label>
|
|
<input type="password" disabled class="form-control" name="qbit.password">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Port</label>
|
|
<input type="text" disabled class="form-control" name="qbit.port">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Symlink/Download Folder</label>
|
|
<input type="text" disabled class="form-control" name="qbit.download_folder">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Refresh Interval (seconds)</label>
|
|
<input type="number" class="form-control" name="qbit.refresh_interval">
|
|
</div>
|
|
<div class="col-12 mb-3">
|
|
<div class="form-group">
|
|
<label for="qbitDebug">Log Level</label>
|
|
<select class="form-select" name="qbit.log_level" id="qbitDebug" disabled>
|
|
<option value="info">Info</option>
|
|
<option value="debug">Debug</option>
|
|
<option value="warn">Warning</option>
|
|
<option value="error">Error</option>
|
|
<option value="trace">Trace</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Arr Configurations -->
|
|
<div class="section mb-5">
|
|
<h5 class="border-bottom pb-2">Arr Configurations</h5>
|
|
<div id="arrConfigs"></div>
|
|
</div>
|
|
|
|
<!-- Repair Configuration -->
|
|
<div class="section mb-5">
|
|
<h5 class="border-bottom pb-2">Repair Configuration</h5>
|
|
<div class="row">
|
|
<div class="col-md-3 mb-3">
|
|
<label class="form-label">Interval</label>
|
|
<input type="text" disabled class="form-control" name="repair.interval" placeholder="e.g., 24h">
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Zurg URL</label>
|
|
<input type="text" disabled class="form-control" name="repair.zurg_url" placeholder="http://zurg:9999">
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="form-check me-3 d-inline-block">
|
|
<input type="checkbox" disabled class="form-check-input" name="repair.enabled" id="repairEnabled">
|
|
<label class="form-check-label" for="repairEnabled">Enable Repair</label>
|
|
</div>
|
|
<div class="form-check me-3 d-inline-block">
|
|
<input type="checkbox" disabled class="form-check-input" name="repair.run_on_start" id="repairOnStart">
|
|
<label class="form-check-label" for="repairOnStart">Run on Start</label>
|
|
</div>
|
|
<div class="form-check d-inline-block">
|
|
<input type="checkbox" disabled class="form-check-input" name="repair.auto_process" id="autoProcess">
|
|
<label class="form-check-label" for="autoProcess">Auto Process(Scheduled jobs will be processed automatically)</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// Templates for dynamic elements
|
|
const debridTemplate = (index) => `
|
|
<div class="config-item position-relative mb-3 p-3 border rounded">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" disabled class="form-control" name="debrid[${index}].name" required>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Host</label>
|
|
<input type="text" disabled class="form-control" name="debrid[${index}].host" required>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">API Key</label>
|
|
<input type="password" disabled class="form-control" name="debrid[${index}].api_key" required>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Mount Folder</label>
|
|
<input type="text" disabled class="form-control" name="debrid[${index}].folder">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Rate Limit</label>
|
|
<input type="text" disabled class="form-control" name="debrid[${index}].rate_limit" placeholder="e.g., 200/minute">
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="form-check me-3 d-inline-block">
|
|
<input type="checkbox" disabled class="form-check-input" name="debrid[${index}].download_uncached">
|
|
<label class="form-check-label">Download Uncached</label>
|
|
</div>
|
|
<div class="form-check d-inline-block">
|
|
<input type="checkbox" disabled class="form-check-input" name="debrid[${index}].check_cached">
|
|
<label class="form-check-label">Check Cached</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const arrTemplate = (index) => `
|
|
<div class="config-item position-relative mb-3 p-3 border rounded">
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" disabled class="form-control" name="arr[${index}].name" required>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Host</label>
|
|
<input type="text" disabled class="form-control" name="arr[${index}].host" required>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">API Token</label>
|
|
<input type="password" disabled class="form-control" name="arr[${index}].token" required>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-2 mb-3">
|
|
<div class="form-check">
|
|
<label class="form-check-label" for="repairOnStart">Cleanup Queue</label>
|
|
<input type="checkbox" disabled class="form-check-input" name="arr[${index}].cleanup">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Main functionality
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
let debridCount = 0;
|
|
let arrCount = 0;
|
|
|
|
// Load existing configuration
|
|
fetch('/internal/config')
|
|
.then(response => response.json())
|
|
.then(config => {
|
|
// Load Debrid configs
|
|
config.debrids?.forEach(debrid => {
|
|
addDebridConfig(debrid);
|
|
});
|
|
|
|
// Load QBitTorrent config
|
|
if (config.qbittorrent) {
|
|
Object.entries(config.qbittorrent).forEach(([key, value]) => {
|
|
const input = document.querySelector(`[name="qbit.${key}"]`);
|
|
if (input) {
|
|
if (input.type === 'checkbox') {
|
|
input.checked = value;
|
|
} else {
|
|
input.value = value;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Load Arr configs
|
|
config.arrs?.forEach(arr => {
|
|
addArrConfig(arr);
|
|
});
|
|
|
|
// Load Repair config
|
|
if (config.repair) {
|
|
Object.entries(config.repair).forEach(([key, value]) => {
|
|
const input = document.querySelector(`[name="repair.${key}"]`);
|
|
if (input) {
|
|
if (input.type === 'checkbox') {
|
|
input.checked = value;
|
|
} else {
|
|
input.value = value;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Load general config
|
|
|
|
const logLevel = document.getElementById('log-level');
|
|
logLevel.value = config.log_level;
|
|
if (config.allowed_file_types && Array.isArray(config.allowed_file_types)) {
|
|
document.querySelector('[name="allowed_file_types"]').value = config.allowed_file_types.join(', ');
|
|
}
|
|
if (config.min_file_size) {
|
|
document.querySelector('[name="min_file_size"]').value = config.min_file_size;
|
|
}
|
|
if (config.max_file_size) {
|
|
document.querySelector('[name="max_file_size"]').value = config.max_file_size;
|
|
}
|
|
if (config.discord_webhook_url) {
|
|
document.querySelector('[name="discord_webhook_url"]').value = config.discord_webhook_url;
|
|
}
|
|
|
|
});
|
|
|
|
// Handle form submission
|
|
document.getElementById('configForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const formData = new FormData(e.target);
|
|
const config = {
|
|
debrids: [],
|
|
qbittorrent: {},
|
|
arrs: [],
|
|
repair: {}
|
|
};
|
|
|
|
// Process form data
|
|
for (let [key, value] of formData.entries()) {
|
|
if (key.startsWith('debrid[')) {
|
|
const match = key.match(/debrid\[(\d+)\]\.(.+)/);
|
|
if (match) {
|
|
const [_, index, field] = match;
|
|
if (!config.debrids[index]) config.debrids[index] = {};
|
|
config.debrids[index][field] = value;
|
|
}
|
|
} else if (key.startsWith('qbit.')) {
|
|
config.qbittorrent[key.replace('qbit.', '')] = value;
|
|
} else if (key.startsWith('arr[')) {
|
|
const match = key.match(/arr\[(\d+)\]\.(.+)/);
|
|
if (match) {
|
|
const [_, index, field] = match;
|
|
if (!config.arrs[index]) config.arrs[index] = {};
|
|
config.arrs[index][field] = value;
|
|
}
|
|
} else if (key.startsWith('repair.')) {
|
|
config.repair[key.replace('repair.', '')] = value;
|
|
}
|
|
}
|
|
|
|
// Clean up arrays (remove empty entries)
|
|
config.debrids = config.debrids.filter(Boolean);
|
|
config.arrs = config.arrs.filter(Boolean);
|
|
|
|
try {
|
|
const response = await fetch('/internal/config', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(config)
|
|
});
|
|
|
|
if (!response.ok) throw new Error(await response.text());
|
|
|
|
createToast('Configuration saved successfully!');
|
|
} catch (error) {
|
|
createToast(`Error saving configuration: ${error.message}`, 'error');
|
|
}
|
|
});
|
|
|
|
// Helper functions
|
|
function addDebridConfig(data = {}) {
|
|
const container = document.getElementById('debridConfigs');
|
|
container.insertAdjacentHTML('beforeend', debridTemplate(debridCount));
|
|
|
|
if (data) {
|
|
Object.entries(data).forEach(([key, value]) => {
|
|
const input = container.querySelector(`[name="debrid[${debridCount}].${key}"]`);
|
|
if (input) {
|
|
if (input.type === 'checkbox') {
|
|
input.checked = value;
|
|
} else {
|
|
input.value = value;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
debridCount++;
|
|
}
|
|
|
|
function addArrConfig(data = {}) {
|
|
const container = document.getElementById('arrConfigs');
|
|
container.insertAdjacentHTML('beforeend', arrTemplate(arrCount));
|
|
|
|
if (data) {
|
|
Object.entries(data).forEach(([key, value]) => {
|
|
const input = container.querySelector(`[name="arr[${arrCount}].${key}"]`);
|
|
if (input) {
|
|
if (input.type === 'checkbox') {
|
|
input.checked = value;
|
|
} else {
|
|
input.value = value;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
arrCount++;
|
|
}
|
|
});
|
|
|
|
|
|
// Register magnet link handler
|
|
function registerMagnetLinkHandler() {
|
|
if ('registerProtocolHandler' in navigator) {
|
|
try {
|
|
navigator.registerProtocolHandler(
|
|
'magnet',
|
|
`${window.location.origin}/download?magnet=%s`,
|
|
'DecyphArr'
|
|
);
|
|
localStorage.setItem('magnetHandler', 'true');
|
|
document.getElementById('registerMagnetLink').innerText = '✅ DecyphArr Can Open Magnet Links';
|
|
document.getElementById('registerMagnetLink').classList.add('bg-white', 'text-black');
|
|
console.log('Registered magnet link handler successfully.');
|
|
} catch (error) {
|
|
console.error('Failed to register magnet link handler:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
var magnetHandler = localStorage.getItem('magnetHandler');
|
|
if (magnetHandler === 'true') {
|
|
document.getElementById('registerMagnetLink').innerText = '✅ DecyphArr Can Open Magnet Links';
|
|
document.getElementById('registerMagnetLink').classList.add('bg-white', 'text-black');
|
|
}
|
|
</script>
|
|
{{ end }} |