From 7f245e9519ab2f1b057d9268cc33faebc8176f2e Mon Sep 17 00:00:00 2001 From: Admin9705 <9705@duck.com> Date: Wed, 30 Apr 2025 08:47:21 -0400 Subject: [PATCH] Updates --- frontend/static/css/new-style.css | 98 +++++ frontend/static/js/apps/swaparr.js | 381 ++++++++++++++++++ frontend/static/js/new-main.js | 117 ++++-- frontend/templates/base.html | 1 + .../templates/components/logs_section.html | 1 + src/primary/utils/logger.py | 3 +- src/primary/web_server.py | 1 + 7 files changed, 578 insertions(+), 24 deletions(-) create mode 100644 frontend/static/js/apps/swaparr.js diff --git a/frontend/static/css/new-style.css b/frontend/static/css/new-style.css index 759c3893..bac5e53d 100644 --- a/frontend/static/css/new-style.css +++ b/frontend/static/css/new-style.css @@ -1065,3 +1065,101 @@ input:checked + .slider:before { .connection-status.testing { color: var(--accent-color); } + +/* Swaparr specific styles */ +.swaparr-panel { + margin-bottom: 15px; + border-radius: 8px; + background-color: var(--bg-secondary); + border-left: 4px solid var(--accent-color); +} + +.swaparr-config { + padding: 12px; +} + +.swaparr-config h3 { + margin-top: 0; + margin-bottom: 10px; + color: var(--accent-color); +} + +.swaparr-config-content { + display: flex; + flex-wrap: wrap; + gap: 15px; +} + +.swaparr-config-content span { + background-color: var(--bg-tertiary); + padding: 5px 10px; + border-radius: 4px; + font-family: monospace; +} + +.swaparr-table { + width: 100%; + overflow-x: auto; + margin-bottom: 15px; +} + +.swaparr-table table { + width: 100%; + border-collapse: collapse; + background-color: var(--bg-secondary); + border-radius: 8px; + overflow: hidden; +} + +.swaparr-table th { + background-color: var(--bg-tertiary); + padding: 10px; + text-align: left; + color: var(--text-secondary); +} + +.swaparr-table td { + padding: 10px; + border-bottom: 1px solid var(--bg-tertiary); +} + +.swaparr-status-striked { + background-color: rgba(255, 193, 7, 0.1); +} + +.swaparr-status-pending { + background-color: rgba(13, 110, 253, 0.1); +} + +.swaparr-status-ignored { + background-color: rgba(108, 117, 125, 0.1); +} + +.swaparr-status-normal { + background-color: rgba(25, 135, 84, 0.1); +} + +.swaparr-status-removed { + background-color: rgba(220, 53, 69, 0.1); +} + +/* When in dark mode */ +.dark-theme .swaparr-status-striked { + background-color: rgba(255, 193, 7, 0.2); +} + +.dark-theme .swaparr-status-pending { + background-color: rgba(13, 110, 253, 0.2); +} + +.dark-theme .swaparr-status-ignored { + background-color: rgba(108, 117, 125, 0.2); +} + +.dark-theme .swaparr-status-normal { + background-color: rgba(25, 135, 84, 0.2); +} + +.dark-theme .swaparr-status-removed { + background-color: rgba(220, 53, 69, 0.2); +} diff --git a/frontend/static/js/apps/swaparr.js b/frontend/static/js/apps/swaparr.js new file mode 100644 index 00000000..9a0f10ad --- /dev/null +++ b/frontend/static/js/apps/swaparr.js @@ -0,0 +1,381 @@ +// Swaparr-specific functionality + +(function(app) { + if (!app) { + console.error("Huntarr App core is not loaded!"); + return; + } + + const swaparrModule = { + elements: {}, + isTableView: true, // Default to table view for Swaparr logs + hasRenderedAnyContent: false, // Track if we've rendered any content + + // Store data for display + logData: { + config: { + platform: '', + maxStrikes: 3, + scanInterval: '10m', + maxDownloadTime: '2h', + ignoreAboveSize: '25 GB' + }, + downloads: [], // Will store download status records + rawLogs: [] // Store raw logs for backup display + }, + + init: function() { + console.log('[Swaparr Module] Initializing...'); + this.setupLogProcessor(); + + // Add a listener for when the log tab changes to Swaparr + const swaparrTab = document.querySelector('.log-tab[data-app="swaparr"]'); + if (swaparrTab) { + swaparrTab.addEventListener('click', () => { + console.log('[Swaparr Module] Swaparr tab clicked'); + // Small delay to ensure everything is ready + setTimeout(() => { + this.ensureContentRendered(); + }, 200); + }); + } + }, + + setupLogProcessor: function() { + // Setup a listener for custom event from huntarrUI's log processing + document.addEventListener('swaparrLogReceived', (event) => { + console.log('[Swaparr Module] Received log event:', event.detail.logData.substring(0, 100) + '...'); + this.processLogLine(event.detail.logData); + }); + }, + + processLogLine: function(logLine) { + // Always store raw logs for backup display + this.logData.rawLogs.push(logLine); + + // Limit raw logs storage to prevent memory issues + if (this.logData.rawLogs.length > 500) { + this.logData.rawLogs.shift(); + } + + // Process log lines specific to Swaparr + if (!logLine) return; + + // Check if this looks like a Swaparr config line and extract information + if (logLine.includes('Platform:') && logLine.includes('Max strikes:')) { + this.extractConfigInfo(logLine); + this.renderConfigPanel(); + return; + } + + // Look for strike-related logs from system + if (logLine.includes('Added strike') || + logLine.includes('Max strikes reached') || + logLine.includes('removing download') || + logLine.includes('Would have removed')) { + + this.processStrikeLog(logLine); + return; + } + + // Check if this is a table header/separator line + if (logLine.includes('strikes') && logLine.includes('status') && logLine.includes('name') && logLine.includes('size') && logLine.includes('eta')) { + // This is the header line, we can ignore it or use it to confirm table format + return; + } + + // Try to match download info line + // Format: [strikes/max] status name size eta + // Example: 2/3 Striked MyDownload.mkv 1.5 GB 2h 15m + const downloadLinePattern = /(\d+\/\d+)\s+(\w+)\s+(.+?)\s+(\d+(?:\.\d+)?)\s*(\w+)\s+([\ddhms\s]+|Infinite)/; + const match = logLine.match(downloadLinePattern); + + if (match) { + // Extract download information + const downloadInfo = { + strikes: match[1], + status: match[2], + name: match[3], + size: match[4] + ' ' + match[5], + eta: match[6] + }; + + // Update or add to our list of downloads + this.updateDownloadsList(downloadInfo); + this.renderTableView(); + } + + // If we're viewing the Swaparr tab, always ensure content is rendered + if (app.currentLogApp === 'swaparr') { + this.ensureContentRendered(); + } + }, + + // Process strike-related logs from system logs + processStrikeLog: function(logLine) { + // Try to extract download name and strike info + let downloadName = ''; + let strikes = '1/3'; // Default value + let status = 'Striked'; + + // Extract download name + if (logLine.includes('Added strike')) { + const match = logLine.match(/Added strike \((\d+)\/(\d+)\) to (.+?) - Reason:/); + if (match) { + strikes = `${match[1]}/${match[2]}`; + downloadName = match[3]; + status = 'Striked'; + } + } else if (logLine.includes('Max strikes reached')) { + const match = logLine.match(/Max strikes reached for (.+?), removing download/); + if (match) { + downloadName = match[1]; + status = 'Removed'; + } + } else if (logLine.includes('Would have removed')) { + const match = logLine.match(/Would have removed (.+?) after (\d+) strikes/); + if (match) { + downloadName = match[1]; + status = 'Pending Removal'; + strikes = `${match[2]}/3`; + } + } + + if (downloadName) { + // Create a download info object with partial information + const downloadInfo = { + strikes: strikes, + status: status, + name: downloadName, + size: 'Unknown', + eta: 'Unknown' + }; + + // Update downloads list + this.updateDownloadsList(downloadInfo); + this.renderTableView(); + } + }, + + extractConfigInfo: function(logLine) { + // Extract the config data from the log line + const platformMatch = logLine.match(/Platform:\s+(\w+)/); + const maxStrikesMatch = logLine.match(/Max strikes:\s+(\d+)/); + const scanIntervalMatch = logLine.match(/Scan interval:\s+(\d+\w+)/); + const maxDownloadTimeMatch = logLine.match(/Max download time:\s+(\d+\w+)/); + const ignoreSizeMatch = logLine.match(/Ignore above size:\s+(\d+\s*\w+)/); + + if (platformMatch) this.logData.config.platform = platformMatch[1]; + if (maxStrikesMatch) this.logData.config.maxStrikes = maxStrikesMatch[1]; + if (scanIntervalMatch) this.logData.config.scanInterval = scanIntervalMatch[1]; + if (maxDownloadTimeMatch) this.logData.config.maxDownloadTime = maxDownloadTimeMatch[1]; + if (ignoreSizeMatch) this.logData.config.ignoreAboveSize = ignoreSizeMatch[1]; + }, + + updateDownloadsList: function(downloadInfo) { + // Find if this download already exists in our list + const existingIndex = this.logData.downloads.findIndex(item => + item.name.trim() === downloadInfo.name.trim() + ); + + if (existingIndex >= 0) { + // Update existing entry + this.logData.downloads[existingIndex] = downloadInfo; + } else { + // Add new entry + this.logData.downloads.push(downloadInfo); + } + }, + + renderConfigPanel: function() { + // Find the logs container + const logsContainer = document.getElementById('logsContainer'); + if (!logsContainer) return; + + // If the user has selected swaparr logs, show the config panel at the top + if (app.currentLogApp === 'swaparr') { + // Check if config panel already exists + let configPanel = document.getElementById('swaparr-config-panel'); + if (!configPanel) { + // Create the panel + configPanel = document.createElement('div'); + configPanel.id = 'swaparr-config-panel'; + configPanel.classList.add('swaparr-panel'); + logsContainer.appendChild(configPanel); + } + + // Update the panel content + configPanel.innerHTML = ` +
| Strikes | +Status | +Name | +Size | +ETA | +
|---|---|---|---|---|
| ${download.strikes} | +${download.status} | +${download.name} | +${download.size} | +${download.eta} | +
Waiting for structured Swaparr data. Showing raw logs below:
+