duplicate cleaner

This commit is contained in:
Broque Thomas 2025-11-10 19:47:26 -08:00
parent aa09b0ba5b
commit 0b7b88e892
3 changed files with 385 additions and 5 deletions

View file

@ -240,7 +240,10 @@
<h3 class="section-title">Tools & Operations</h3>
<div class="tools-grid">
<div class="tool-card" id="db-updater-card">
<h4 class="tool-card-title">Database Updater</h4>
<div class="tool-card-header">
<h4 class="tool-card-title">Database Updater</h4>
<button class="tool-help-button" data-tool="db-updater" title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Last Full Refresh: <span id="db-last-refresh">Never</span></p>
<div class="tool-card-stats">
<div class="stat-item">
@ -277,8 +280,9 @@
</div>
<div class="tool-card" id="metadata-updater-card">
<div class="card-header">
<h3>Metadata Updater</h3>
<div class="tool-card-header">
<h4 class="tool-card-title">Metadata Updater</h4>
<button class="tool-help-button" data-tool="metadata-updater" title="Learn more about this tool">?</button>
</div>
<p class="metadata-updater-description tool-card-info">Updates artist photos, genres, and album art from Spotify.</p>
<div class="tool-card-controls">
@ -302,7 +306,10 @@
</div>
<div class="tool-card" id="quality-scanner-card">
<h4 class="tool-card-title">Quality Scanner</h4>
<div class="tool-card-header">
<h4 class="tool-card-title">Quality Scanner</h4>
<button class="tool-help-button" data-tool="quality-scanner" title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Scan library for tracks below quality preferences</p>
<div class="tool-card-stats">
<div class="stat-item">
@ -339,7 +346,10 @@
</div>
<div class="tool-card" id="duplicate-cleaner-card">
<h4 class="tool-card-title">Duplicate Cleaner</h4>
<div class="tool-card-header">
<h4 class="tool-card-title">Duplicate Cleaner</h4>
<button class="tool-help-button" data-tool="duplicate-cleaner" title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Detect and remove duplicate tracks in Transfer folder</p>
<div class="tool-card-stats">
<div class="stat-item">
@ -2212,6 +2222,19 @@
</div>
</div>
<!-- Tool Help Modal -->
<div class="tool-help-modal" id="tool-help-modal">
<div class="tool-help-modal-content">
<div class="tool-help-modal-header">
<h3 id="tool-help-modal-title">Tool Information</h3>
<button class="tool-help-modal-close">&times;</button>
</div>
<div class="tool-help-modal-body" id="tool-help-modal-body">
<!-- Content will be dynamically inserted -->
</div>
</div>
</div>
<script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>

View file

@ -9049,6 +9049,197 @@ function stopDuplicateCleanerPolling() {
}
}
// ============================================
// == TOOL HELP MODAL ==
// ============================================
const TOOL_HELP_CONTENT = {
'db-updater': {
title: 'Database Updater',
content: `
<h4>What does this tool do?</h4>
<p>The Database Updater syncs your media server library (Plex, Jellyfin, or Navidrome) with SoulSync's internal database.</p>
<h4>Update Modes</h4>
<ul>
<li><strong>Incremental Update:</strong> Only scans for new artists, albums, and tracks that have been added since the last update. Fast and efficient for regular updates.</li>
<li><strong>Full Refresh:</strong> Completely rebuilds the database from scratch. Use this if you've made significant changes to your library or if data seems out of sync.</li>
</ul>
<h4>When to use it?</h4>
<ul>
<li>After adding new music to your media server</li>
<li>When library statistics seem incorrect</li>
<li>After changing media server settings</li>
</ul>
<h4>Progress Persistence</h4>
<p>The update runs in the background. You can close this page and return later - progress will be preserved and continue where it left off.</p>
`
},
'metadata-updater': {
title: 'Metadata Updater',
content: `
<h4>What does this tool do?</h4>
<p>The Metadata Updater enhances your library by fetching artist photos, genres, and album artwork from Spotify.</p>
<h4>Refresh Interval Options</h4>
<ul>
<li><strong>6 months:</strong> Only updates metadata for artists not updated in the last 180 days</li>
<li><strong>3 months:</strong> Updates metadata for artists not updated in the last 90 days</li>
<li><strong>1 month:</strong> Updates metadata for artists not updated in the last 30 days</li>
<li><strong>Force All:</strong> Updates all artists regardless of when they were last updated</li>
</ul>
<h4>What gets updated?</h4>
<ul>
<li>Artist profile photos</li>
<li>Music genres</li>
<li>Album cover artwork</li>
<li>Spotify popularity scores</li>
</ul>
<h4>Note</h4>
<p>This tool is only available for <strong>Plex</strong> media servers. It requires Spotify authentication to fetch metadata.</p>
`
},
'quality-scanner': {
title: 'Quality Scanner',
content: `
<h4>What does this tool do?</h4>
<p>The Quality Scanner identifies tracks in your library that don't meet your preferred quality settings and automatically matches them to Spotify to add to your wishlist for re-downloading.</p>
<h4>Scan Scope</h4>
<ul>
<li><strong>Watchlist Artists Only:</strong> Only scans tracks from artists you're watching. Faster and more focused.</li>
<li><strong>All Library Tracks:</strong> Scans your entire music library. Comprehensive but takes longer.</li>
</ul>
<h4>How it works</h4>
<ol>
<li>Scans tracks and checks file format against your quality preferences</li>
<li>Identifies tracks below your quality threshold (e.g., MP3 when you prefer FLAC)</li>
<li>Uses fuzzy matching to find the track on Spotify (70% confidence minimum)</li>
<li>Automatically adds matched tracks to your wishlist for re-download</li>
</ol>
<h4>Quality Tiers</h4>
<ul>
<li><strong>Tier 1 (Best):</strong> FLAC, WAV, ALAC, AIFF - Lossless formats</li>
<li><strong>Tier 2:</strong> OPUS, OGG - High quality lossy</li>
<li><strong>Tier 3:</strong> M4A, AAC - Standard lossy</li>
<li><strong>Tier 4:</strong> MP3, WMA - Lower quality lossy</li>
</ul>
<h4>Stats Explained</h4>
<ul>
<li><strong>Processed:</strong> Total tracks scanned so far</li>
<li><strong>Quality Met:</strong> Tracks that meet your quality standards</li>
<li><strong>Low Quality:</strong> Tracks below your quality threshold</li>
<li><strong>Matched:</strong> Low quality tracks successfully matched to Spotify and added to wishlist</li>
</ul>
`
},
'duplicate-cleaner': {
title: 'Duplicate Cleaner',
content: `
<h4>What does this tool do?</h4>
<p>The Duplicate Cleaner scans your Transfer folder for duplicate audio files and automatically removes lower-quality versions, keeping only the best copy.</p>
<h4>How it detects duplicates</h4>
<p>Files are considered duplicates when:</p>
<ul>
<li>They are in the <strong>same folder</strong></li>
<li>They have the <strong>exact same filename</strong> (ignoring file extension)</li>
</ul>
<p>Example: <code>Song.flac</code> and <code>Song.mp3</code> in the same folder = duplicates </p>
<p>Example: <code>Song.flac</code> and <code>Song (Remaster).flac</code> = NOT duplicates </p>
<h4>Which file is kept?</h4>
<p>Priority order (best to worst):</p>
<ol>
<li><strong>Format priority:</strong> FLAC/Lossless > OPUS/OGG > M4A/AAC > MP3/WMA</li>
<li><strong>If same format:</strong> Larger file size is kept (usually indicates better bitrate)</li>
</ol>
<h4>Where do deleted files go?</h4>
<p>Removed files are moved to <code>Transfer/deleted/</code> folder (not permanently deleted). You can review and recover them if needed.</p>
<h4>Safety Features</h4>
<ul>
<li>Only processes audio files (FLAC, MP3, M4A, etc.)</li>
<li>Only removes files with identical names in the same folder</li>
<li>Files are moved, not deleted - fully recoverable</li>
<li>Preserves original folder structure in the deleted folder</li>
</ul>
<h4>Stats Explained</h4>
<ul>
<li><strong>Files Scanned:</strong> Total audio files checked</li>
<li><strong>Duplicates Found:</strong> Number of duplicate files detected</li>
<li><strong>Deleted:</strong> Files moved to deleted folder</li>
<li><strong>Space Freed:</strong> Total disk space reclaimed</li>
</ul>
`
}
};
function initializeToolHelpButtons() {
const helpButtons = document.querySelectorAll('.tool-help-button');
const modal = document.getElementById('tool-help-modal');
const closeButton = modal.querySelector('.tool-help-modal-close');
// Attach click handlers to all help buttons
helpButtons.forEach(button => {
button.addEventListener('click', (e) => {
e.stopPropagation();
const toolId = button.getAttribute('data-tool');
openToolHelpModal(toolId);
});
});
// Close modal when clicking close button
closeButton.addEventListener('click', closeToolHelpModal);
// Close modal when clicking outside content
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeToolHelpModal();
}
});
// Close modal on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('active')) {
closeToolHelpModal();
}
});
}
function openToolHelpModal(toolId) {
const modal = document.getElementById('tool-help-modal');
const titleElement = document.getElementById('tool-help-modal-title');
const bodyElement = document.getElementById('tool-help-modal-body');
const helpData = TOOL_HELP_CONTENT[toolId];
if (!helpData) {
console.warn(`No help content found for tool: ${toolId}`);
return;
}
titleElement.textContent = helpData.title;
bodyElement.innerHTML = helpData.content;
modal.classList.add('active');
document.body.style.overflow = 'hidden'; // Prevent background scrolling
}
function closeToolHelpModal() {
const modal = document.getElementById('tool-help-modal');
modal.classList.remove('active');
document.body.style.overflow = ''; // Restore scrolling
}
function stopWishlistCountPolling() {
if (wishlistCountInterval) {
clearInterval(wishlistCountInterval);
@ -9160,6 +9351,9 @@ async function loadDashboardData() {
duplicateCleanButton.addEventListener('click', handleDuplicateCleanButtonClick);
}
// Attach event listeners for tool help buttons
initializeToolHelpButtons();
// Attach event listener for the wishlist button
const wishlistButton = document.getElementById('wishlist-button');
if (wishlistButton) {

View file

@ -15937,3 +15937,166 @@ body {
font-size: 13px;
}
}
/* ====================================
Tool Help Button & Modal
==================================== */
.tool-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.tool-card-header .tool-card-title {
margin: 0;
}
.tool-help-button {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid #666;
background-color: transparent;
color: #666;
font-size: 14px;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: all 0.2s ease;
flex-shrink: 0;
}
.tool-help-button:hover {
background-color: #1db954;
border-color: #1db954;
color: #fff;
transform: scale(1.1);
}
.tool-help-modal {
display: none;
position: fixed;
z-index: 10000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
animation: fadeIn 0.2s ease;
}
.tool-help-modal.active {
display: flex;
align-items: center;
justify-content: center;
}
.tool-help-modal-content {
background-color: #1a1a1a;
border-radius: 12px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
animation: slideUp 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
transform: translateY(30px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.tool-help-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
border-bottom: 1px solid #333;
}
.tool-help-modal-header h3 {
margin: 0;
font-size: 20px;
color: #fff;
}
.tool-help-modal-close {
background: none;
border: none;
color: #999;
font-size: 32px;
cursor: pointer;
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
transition: color 0.2s ease;
line-height: 1;
}
.tool-help-modal-close:hover {
color: #fff;
}
.tool-help-modal-body {
padding: 24px;
color: #ccc;
line-height: 1.6;
}
.tool-help-modal-body h4 {
color: #1db954;
margin-top: 20px;
margin-bottom: 10px;
font-size: 16px;
}
.tool-help-modal-body h4:first-child {
margin-top: 0;
}
.tool-help-modal-body ul {
margin: 10px 0;
padding-left: 20px;
}
.tool-help-modal-body li {
margin: 8px 0;
}
.tool-help-modal-body p {
margin: 10px 0;
}
.tool-help-modal-body code {
background-color: #2a2a2a;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 13px;
color: #1db954;
}
.tool-help-modal-body strong {
color: #fff;
}