Refactor Cleanuperr integration by removing the Cleanuperr module and associated UI components. Update documentation to reflect changes in credential storage paths for both Docker and local environments. Enhance API fetch methods across various modules to utilize a timeout utility for improved reliability.

This commit is contained in:
Admin9705
2025-06-12 23:15:44 -04:00
parent c76584f109
commit 0a3069700f
13 changed files with 46 additions and 468 deletions

View File

@@ -495,7 +495,7 @@ services:
docker stop huntarr
# Remove credentials file
# Credentials are now stored in the database at /path/to/huntarr/data/huntarr.db
# Credentials are now stored in the database at /config/huntarr.db (Docker) or /path/to/huntarr/data/huntarr.db (local)
# To reset credentials, delete the database file or use the web interface
# Start Huntarr
@@ -514,7 +514,7 @@ docker start huntarr</code></pre>
<div class="faq-answer">
Same solution as password reset - remove the credentials file:
<pre class="terminal"><code># Credentials are now stored in the database at /path/to/huntarr/data/huntarr.db
<pre class="terminal"><code># Credentials are now stored in the database at /config/huntarr.db (Docker) or /path/to/huntarr/data/huntarr.db (local)
# To reset credentials, delete the database file or use the web interface</code></pre>
This will reset both password and 2FA settings.

View File

@@ -502,25 +502,7 @@ const appsModule = {
}
if (!selectedApp || selectedApp === this.currentApp) return;
// Special case for Cleanuperr - it's an information page, not a settings page
if (selectedApp === 'cleanuperr') {
// Hide apps section and show Cleanuperr section
document.getElementById('appsSection').classList.remove('active');
document.getElementById('cleanuperrSection').classList.add('active');
// Update the page title
if (huntarrUI && typeof huntarrUI.switchSection === 'function') {
huntarrUI.currentSection = 'cleanuparr';
// We're not calling the full switchSection as that would alter navigation
// Just update the title
const pageTitleElement = document.getElementById('currentPageTitle');
if (pageTitleElement) {
pageTitleElement.textContent = 'Cleanuperr';
}
}
return;
}
// Check for unsaved changes
if (this.settingsChanged) {

View File

@@ -1,7 +0,0 @@
/**
* Cleanuperr - Information display component for Huntarr
* Provides information about the Cleanuperr project by Flaminel
*/
// No active functionality needed for this information page
console.log('Cleanuperr information module loaded');

View File

@@ -72,7 +72,7 @@
loadStatistics: function() {
// Load statistics from the API
fetch('./api/swaparr/status')
HuntarrUtils.fetchWithTimeout('./api/swaparr/status')
.then(response => response.json())
.then(data => {
if (data.session_statistics) {

View File

@@ -56,8 +56,12 @@ window.CycleCountdown = (function() {
// First try to fetch from API
console.log('[CycleCountdown] Fetching initial data from API...');
fetchAllCycleData()
.then(() => {
console.log('[CycleCountdown] Initial data fetch successful');
.then((data) => {
if (data && Object.keys(data).length > 0) {
console.log('[CycleCountdown] Initial data fetch successful');
} else {
console.log('[CycleCountdown] No apps configured yet - countdown will activate when apps are set up');
}
// Success - data is processed in fetchAllCycleData
})
.catch((error) => {
@@ -81,8 +85,11 @@ window.CycleCountdown = (function() {
if (!isFetchingData) {
console.log('[CycleCountdown] API sync (every 10s) to maintain accuracy...');
fetchAllCycleData()
.then(() => {
console.log('[CycleCountdown] API sync completed, timers will self-correct');
.then((data) => {
if (data && Object.keys(data).length > 0) {
console.log('[CycleCountdown] API sync completed, timers will self-correct');
}
// Don't log anything when no apps are configured - this is normal
})
.catch(() => {
console.log('[CycleCountdown] API sync failed, timers continue with last known data');
@@ -328,8 +335,9 @@ window.CycleCountdown = (function() {
// Check if we got valid data
if (Object.keys(data).length === 0) {
console.warn('[CycleCountdown] API returned no data');
reject(new Error('No data from API'));
// No data likely means no apps are configured yet - this is normal
console.log('[CycleCountdown] No cycle data available (no apps configured yet)');
resolve({}); // Resolve with empty data instead of rejecting
return;
}
@@ -415,8 +423,9 @@ window.CycleCountdown = (function() {
if (dataProcessed) {
resolve(data);
} else {
console.warn('[CycleCountdown] No valid app data found in API response');
reject(new Error('No valid app data'));
// No valid app data likely means no apps are configured yet - this is normal
console.log('[CycleCountdown] No configured apps found in API response');
resolve({}); // Resolve with empty data instead of rejecting
}
})
.catch(error => {
@@ -640,6 +649,13 @@ window.CycleCountdown = (function() {
document.addEventListener('DOMContentLoaded', function() {
console.log('[CycleCountdown] DOM loaded, checking page...');
// Skip initialization on login page or if not authenticated
const isLoginPage = document.querySelector('.login-container, #loginForm, .login-form');
if (isLoginPage) {
console.log('[CycleCountdown] Login page detected, skipping initialization');
return;
}
// Only initialize if we're on a page that has app status cards
// Check for the home section or any app status elements
const homeSection = document.getElementById('homeSection');

View File

@@ -30,7 +30,7 @@ window.LogsModule = {
// Set immediate fallback to prevent warnings during loading
this.userTimezone = this.userTimezone || 'UTC';
fetch('./api/settings')
HuntarrUtils.fetchWithTimeout('./api/settings')
.then(response => response.json())
.then(settings => {
this.userTimezone = settings.general?.timezone || 'UTC';

View File

@@ -41,6 +41,13 @@ let huntarrUI = {
init: function() {
console.log('[huntarrUI] Initializing UI...');
// Skip initialization on login page
const isLoginPage = document.querySelector('.login-container, #loginForm, .login-form');
if (isLoginPage) {
console.log('[huntarrUI] Login page detected, skipping full initialization');
return;
}
// Cache frequently used DOM elements
this.cacheElements();

View File

@@ -747,7 +747,7 @@ function updateTimezoneDisplay(serverTimezone) {
function loadServerTimezone() {
console.debug('Loading server timezone from settings API');
fetch('./api/settings')
HuntarrUtils.fetchWithTimeout('./api/settings')
.then(response => response.json())
.then(data => {
const serverTimezone = data.general?.timezone || 'UTC';

View File

@@ -26,11 +26,17 @@ const HuntarrUtils = {
const timeoutId = setTimeout(() => controller.abort(), apiTimeout);
// Merge options with signal from AbortController
// Only include credentials for internal API calls (not external URLs)
const fetchOptions = {
...options,
signal: controller.signal
};
// Add credentials only for internal API calls
if (url && typeof url === 'string' && !url.startsWith('http') && !url.startsWith('//')) {
fetchOptions.credentials = 'include';
}
// Process URL to handle base URL for reverse proxy subpaths
let processedUrl = url;

View File

@@ -148,7 +148,7 @@
<div id="whisparrApps" class="app-apps-panel app-content-panel"></div>
<div id="erosApps" class="app-apps-panel app-content-panel"></div>
<div id="swaparrApps" class="app-apps-panel app-content-panel"></div>
<div id="cleanuperrApps" class="app-apps-panel app-content-panel"></div>
</div>
</div>
</section>

View File

@@ -1,426 +0,0 @@
<section id="cleanuperrSection" class="content-section">
<div class="section-header">
<div class="header-buttons">
<button id="backToAppsButton" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Apps
</button>
</div>
<h2>Cleanuperr</h2>
</div>
<div class="cleanuperr-container">
<div class="cleanuperr-info-box">
<div class="cleanuperr-header">
<div class="cleanuperr-logo">
<img src="./static/logo/apps/cleanuperr.png" alt="Cleanuperr Logo" class="app-logo">
</div>
<div class="cleanuperr-title">
<h3>Cleanuperr by <a href="https://github.com/Flaminel" target="_blank">Flaminel</a></h3>
</div>
</div>
<div class="github-info-container">
<a href="https://github.com/flmorg/cleanuperr" class="github-link" target="_blank">
<i class="fab fa-github"></i> View on GitHub
</a>
<div class="github-stars">
<i class="fas fa-star star-icon"></i>
<span id="cleanuperr-stars-count">Loading...</span>
</div>
</div>
<div class="cleanuperr-description">
<p>Cleanuperr is a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients like qBittorrent. It removes incomplete or blocked downloads, updates queues, and enforces blacklists or whitelists to manage file selection. After removing blocked content, Cleanuperr can also trigger a search to replace the deleted shows/movies.</p>
<h4>Key Features:</h4>
<ul>
<li>Strike system to mark stalled or downloads stuck in metadata downloading</li>
<li>Remove and block downloads that reached a maximum number of strikes</li>
<li>Remove and block downloads that have a low download speed or high estimated completion time</li>
<li>Remove downloads blocked by qBittorrent or by Cleanuperr's content blocker</li>
<li>Trigger a search for downloads removed from the *arrs</li>
<li>Clean up downloads that have been seeding for a certain amount of time</li>
<li>Notify on strike or download removal</li>
<li>Ignore certain torrent hashes, categories, tags or trackers from being processed</li>
</ul>
<div class="origin-section">
<p>Cleanuperr was created primarily to address malicious files, such as *.lnk or *.zipx, that were getting stuck in Sonarr/Radarr and required manual intervention. It supports both qBittorrent's built-in exclusion features and its own blocklist-based system.</p>
</div>
<div class="author-section">
<div class="author-info">
<img src="https://avatars.githubusercontent.com/u/6135377?v=4" alt="Flaminel" class="author-avatar">
<div class="author-details">
<h4>About the Author</h4>
<p>Cleanuperr is developed by <a href="https://github.com/Flaminel" target="_blank">Flaminel</a>, a passionate developer focused on creating tools that enhance the media server experience.</p>
</div>
</div>
</div>
<div class="collaboration-note">
<p>Huntarr is proud to feature Cleanuperr as part of our commitment to helping other projects grow. We believe in collaboration across the media server community to create better tools for everyone.</p>
</div>
<div class="cleanuperr-cta">
<a href="https://github.com/flmorg/cleanuperr" class="button primary-button" target="_blank">
<i class="fab fa-github"></i> GitHub
</a>
<a href="https://discord.com/invite/SCtMCgtsc4" class="button discord-button" target="_blank">
<i class="fab fa-discord"></i> Join Discord
</a>
<a href="https://github.com/flmorg/cleanuperr#installation" class="button secondary-button" target="_blank">
<i class="fas fa-book"></i> Installation Guide
</a>
</div>
</div>
</div>
</div>
</section>
<style>
#cleanuperrSection {
padding: 20px;
max-width: 100%;
display: none;
}
#cleanuperrSection.active {
display: block;
}
.cleanuperr-container {
width: 100%;
margin-bottom: 20px;
}
.cleanuperr-info-box {
background: linear-gradient(135deg, rgba(28, 36, 54, 0.9), rgba(24, 32, 48, 0.8));
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
margin-bottom: 20px;
border: 1px solid rgba(90, 109, 137, 0.2);
}
.cleanuperr-header {
display: flex;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 20px;
}
.cleanuperr-logo {
display: flex;
align-items: center;
justify-content: center;
}
.app-logo {
width: 64px;
height: 64px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.github-info-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin: 15px 0;
padding: 15px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
border: 1px solid rgba(90, 109, 137, 0.2);
}
.github-stars {
display: flex;
align-items: center;
gap: 5px;
font-size: 16px;
}
.star-icon {
color: #f1c40f;
}
.cleanuperr-title {
flex: 2;
}
.cleanuperr-title h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 24px;
color: #e0e6ed;
}
.github-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: linear-gradient(135deg, #2c3e50, #1a2632);
border-radius: 6px;
color: #e0e6ed;
text-decoration: none;
font-size: 14px;
transition: all 0.3s ease;
border: 1px solid rgba(90, 109, 137, 0.3);
}
.github-link:hover {
background: linear-gradient(135deg, #34495e, #2c3e50);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.cleanuperr-description {
color: #e0e6ed;
line-height: 1.6;
}
.cleanuperr-description h4 {
margin-top: 20px;
margin-bottom: 10px;
color: #3498db;
}
.cleanuperr-description ul {
margin-left: 20px;
margin-bottom: 20px;
}
.cleanuperr-description li {
margin-bottom: 8px;
}
.author-section {
margin: 30px 0;
padding: 20px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
border: 1px solid rgba(90, 109, 137, 0.2);
}
.author-info {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
.author-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
border: 3px solid #3498db;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.author-details {
flex: 1;
}
.author-details h4 {
margin-top: 0;
color: #3498db;
}
.origin-section {
margin: 25px 0;
padding: 15px;
background: rgba(41, 128, 185, 0.1);
border-radius: 8px;
border: 1px solid rgba(41, 128, 185, 0.2);
}
.collaboration-note {
margin: 30px 0;
padding: 15px;
background: rgba(52, 152, 219, 0.1);
border-left: 4px solid #3498db;
border-radius: 0 8px 8px 0;
}
.cleanuperr-cta {
display: flex;
gap: 15px;
margin-top: 30px;
flex-wrap: wrap;
}
.button, .back-button {
padding: 10px 20px;
border-radius: 8px;
font-weight: bold;
text-decoration: none;
text-align: center;
transition: all 0.3s ease;
}
.back-button {
background: linear-gradient(135deg, #34495e, #2c3e50);
color: white;
border: none;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.back-button:hover {
background: linear-gradient(135deg, #3a536b, #34495e);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25);
}
.header-buttons {
margin-right: 20px;
}
.primary-button {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
border: none;
}
.primary-button:hover {
background: linear-gradient(135deg, #2980b9, #2471a3);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(52, 152, 219, 0.3);
}
.discord-button {
background: linear-gradient(135deg, #5865F2, #4752C4);
color: white;
border: none;
}
.discord-button:hover {
background: linear-gradient(135deg, #4752C4, #3C45A5);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(88, 101, 242, 0.4);
}
.secondary-button {
background: transparent;
color: #3498db;
border: 1px solid #3498db;
}
.secondary-button:hover {
background: rgba(52, 152, 219, 0.1);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(52, 152, 219, 0.2);
}
@media (max-width: 768px) {
.cleanuperr-header {
flex-direction: column;
}
.cleanuperr-logo {
width: 100%;
}
.author-info {
flex-direction: column;
text-align: center;
}
.cleanuperr-cta {
flex-direction: column;
}
.button {
width: 100%;
}
}
</style>
<script>
// Add event listener to the back button to return to Apps section
document.addEventListener('DOMContentLoaded', function() {
const backToAppsButton = document.getElementById('backToAppsButton');
if (backToAppsButton) {
backToAppsButton.addEventListener('click', function() {
// Hide Cleanuperr section
document.getElementById('cleanuperrSection').classList.remove('active');
// Show Apps section
document.getElementById('appsSection').classList.add('active');
// Update the current section in the main UI
if (huntarrUI) {
huntarrUI.currentSection = 'apps';
// Update the page title
const pageTitleElement = document.getElementById('currentPageTitle');
if (pageTitleElement) {
pageTitleElement.textContent = 'Apps';
}
// Update the selected menu item
const menuItems = document.querySelectorAll('.nav-item');
menuItems.forEach(item => {
item.classList.remove('active');
if (item.getAttribute('data-section') === 'apps') {
item.classList.add('active');
}
});
// Reset the app selector to a default app (not Cleanuperr)
const appsAppSelect = document.getElementById('appsAppSelect');
if (appsAppSelect) {
// Set to the first option that isn't Cleanuperr
for (let i = 0; i < appsAppSelect.options.length; i++) {
if (appsAppSelect.options[i].value !== 'cleanuperr') {
appsAppSelect.selectedIndex = i;
break;
}
}
}
}
});
}
});
// Load GitHub star count for Cleanuperr
function loadCleanuperrStarCount() {
HuntarrUtils.fetchWithTimeout('https://api.github.com/repos/flmorg/cleanuperr')
.then(response => {
if (!response.ok) {
// Handle rate limiting or other errors
if (response.status === 403) {
console.warn('GitHub API rate limit likely exceeded.');
throw new Error('Rate limited');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
const starsElement = document.getElementById('cleanuperr-stars-count');
if (starsElement && data && data.stargazers_count !== undefined) {
starsElement.textContent = data.stargazers_count;
} else if (starsElement) {
starsElement.textContent = 'N/A';
}
})
.catch(error => {
console.error('Error loading Cleanuperr star count from GitHub:', error);
const starsElement = document.getElementById('cleanuperr-stars-count');
if (starsElement) {
starsElement.textContent = error.message === 'Rate limited' ? 'Rate Limited' : 'Error';
}
});
}
// Load star count when the page is loaded
loadCleanuperrStarCount();
</script>

View File

@@ -73,7 +73,7 @@
<div class="nav-icon-wrapper">
<i class="fas fa-film"></i>
</div>
<span>My AV1 Guide</span>
<span>My AV1 Guide</span>
</a>
<a href="https://github.com/flmorg/cleanuperr" class="nav-item" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper">

View File

@@ -29,7 +29,7 @@
{% include 'components/apps_section.html' %}
<!-- Cleanuperr Section -->
{% include 'components/cleanuperr_section.html' %}
<!-- Settings Section -->
{% include 'components/settings_section.html' %}