1339 lines
48 KiB
HTML
1339 lines
48 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Media Library - Gooner Training</title>
|
|
|
|
<!-- Core Styles -->
|
|
<link rel="stylesheet" href="src/styles/color-variables.css">
|
|
<link rel="stylesheet" href="src/styles/styles.css">
|
|
<link rel="stylesheet" href="src/styles/styles-dark-edgy.css">
|
|
<link rel="stylesheet" href="src/styles/base-video-player.css">
|
|
<link rel="stylesheet" href="src/styles/media-tags.css">
|
|
<script src="src/utils/themeManager.js"></script>
|
|
|
|
<style>
|
|
/* Library Page Specific Styles */
|
|
/* Library Header - Matching Training Academy Style */
|
|
.library-header {
|
|
background: var(--gradient-primary);
|
|
border-bottom: 2px solid var(--header-border);
|
|
box-shadow: var(--shadow-md);
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
width: 100%;
|
|
z-index: 1000;
|
|
backdrop-filter: blur(10px);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.library-nav {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 1rem 2rem;
|
|
gap: 2rem;
|
|
width: 100%;
|
|
}
|
|
|
|
.library-nav .nav-left h1 {
|
|
color: var(--header-title-color);
|
|
font-size: 1.8rem;
|
|
margin: 0;
|
|
text-shadow: var(--shadow-glow-primary);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.library-nav .nav-center {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.library-subtitle {
|
|
color: var(--header-subtitle-color);
|
|
font-size: 1rem;
|
|
font-style: italic;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.library-nav .nav-right {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Button Styles */
|
|
.btn {
|
|
background: var(--btn-secondary-bg);
|
|
border: 1px solid var(--btn-secondary-border);
|
|
color: var(--btn-secondary-text);
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 0.9rem;
|
|
transition: all 0.3s ease;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: var(--btn-secondary-hover-bg);
|
|
border-color: var(--btn-secondary-hover-border);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: var(--bg-secondary-overlay-20);
|
|
border-color: var(--color-primary);
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: var(--bg-secondary-overlay-30);
|
|
border-color: var(--color-primary-light);
|
|
}
|
|
|
|
.btn-primary {
|
|
background: var(--btn-primary-bg);
|
|
border-color: var(--btn-primary-border);
|
|
color: var(--btn-primary-text);
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: var(--btn-primary-hover-bg);
|
|
border-color: var(--btn-primary-hover-border);
|
|
}
|
|
|
|
.btn-danger {
|
|
background: var(--btn-danger-bg);
|
|
border-color: var(--btn-danger-border);
|
|
color: var(--btn-danger-text);
|
|
}
|
|
|
|
.btn-danger:hover {
|
|
background: var(--btn-danger-hover-bg);
|
|
border-color: var(--btn-danger-hover-border);
|
|
}
|
|
|
|
.btn-warning {
|
|
background: var(--color-warning);
|
|
border-color: var(--color-warning);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.btn-warning:hover {
|
|
background: var(--color-accent-gold);
|
|
border-color: var(--color-accent-gold);
|
|
}
|
|
|
|
.btn-outline {
|
|
background: transparent;
|
|
border-color: var(--color-primary);
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
.btn-outline:hover {
|
|
background: var(--bg-secondary-overlay-20);
|
|
border-color: var(--color-primary-light);
|
|
}
|
|
|
|
.btn-small {
|
|
padding: 0.3rem 0.6rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.btn-success {
|
|
background: var(--color-success);
|
|
border-color: var(--color-success);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.btn-success:hover {
|
|
background: var(--color-success-light);
|
|
border-color: var(--color-success-light);
|
|
}
|
|
|
|
.btn-success:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.btn-danger:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Loading Overlay */
|
|
.loading-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: var(--bg-primary);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 10000;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.loading-content {
|
|
text-align: center;
|
|
}
|
|
|
|
.loading-content h2 {
|
|
font-family: 'Audiowide', cursive;
|
|
font-size: var(--font-xxxl);
|
|
background: linear-gradient(45deg, var(--color-primary), var(--color-secondary));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
margin-bottom: var(--space-lg);
|
|
animation: titleGlow 2s ease-in-out infinite alternate;
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 60px;
|
|
height: 60px;
|
|
border: 4px solid var(--border-color);
|
|
border-top: 4px solid var(--color-primary);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto var(--space-lg);
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.loading-status {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-lg);
|
|
margin-bottom: var(--space-lg);
|
|
}
|
|
|
|
.loading-progress {
|
|
width: 300px;
|
|
height: 8px;
|
|
background: var(--bg-tertiary);
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
margin: 0 auto var(--space-base);
|
|
}
|
|
|
|
.loading-progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
|
|
border-radius: 4px;
|
|
width: 0%;
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.loading-percentage {
|
|
color: var(--text-primary);
|
|
font-size: var(--font-lg);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* Main Content Area */
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
padding-top: 80px; /* Account for fixed header */
|
|
background: var(--bg-primary);
|
|
color: var(--text-primary);
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
}
|
|
|
|
.library-container {
|
|
max-width: 95vw;
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
/* Library Tabs */
|
|
.library-tabs {
|
|
display: flex;
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.library-tab {
|
|
background: var(--bg-secondary);
|
|
border: 2px solid var(--border-color);
|
|
color: var(--text-primary);
|
|
padding: 0.75rem 1.5rem;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.library-tab:hover {
|
|
background: var(--bg-secondary-overlay-30);
|
|
border-color: var(--color-primary);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.library-tab.active {
|
|
background: var(--gradient-primary);
|
|
border-color: var(--color-primary);
|
|
box-shadow: var(--shadow-glow-primary);
|
|
}
|
|
|
|
/* Library Content Sections */
|
|
.library-content {
|
|
display: none;
|
|
}
|
|
|
|
.library-content.active {
|
|
display: block;
|
|
}
|
|
|
|
.content-section {
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 12px;
|
|
padding: 2rem;
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.content-section h2,
|
|
.content-section h3 {
|
|
color: var(--color-primary);
|
|
margin-top: 0;
|
|
}
|
|
|
|
.content-section h4 {
|
|
color: var(--color-secondary);
|
|
}
|
|
|
|
.content-section p {
|
|
color: var(--text-secondary);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
/* Directory Management Section */
|
|
.directory-management-section,
|
|
.upload-section {
|
|
background: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.directory-controls,
|
|
.upload-controls {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
margin: 1rem 0;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
}
|
|
|
|
.linked-directories-container {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
#linked-image-directories-list,
|
|
#linked-video-directories-list {
|
|
background: var(--bg-primary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 6px;
|
|
padding: 1rem;
|
|
min-height: 100px;
|
|
}
|
|
|
|
.no-directories {
|
|
color: var(--text-secondary);
|
|
text-align: center;
|
|
padding: 2rem;
|
|
font-style: italic;
|
|
}
|
|
|
|
.upload-info {
|
|
background: var(--bg-secondary-overlay-20);
|
|
border-left: 3px solid var(--color-primary);
|
|
padding: 0.75rem;
|
|
margin: 1rem 0;
|
|
border-radius: 4px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* Gallery Sections */
|
|
.gallery-section,
|
|
.audio-gallery-section,
|
|
.video-gallery-section {
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.gallery-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.gallery-controls {
|
|
display: flex;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.gallery-controls select {
|
|
background: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-primary);
|
|
padding: 0.5rem;
|
|
border-radius: 6px;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.image-gallery,
|
|
.audio-gallery,
|
|
.video-gallery {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
background: var(--bg-primary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
padding: 1rem;
|
|
min-height: 200px;
|
|
max-height: 75vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.no-images-message,
|
|
.no-audio-message,
|
|
.no-video-message {
|
|
grid-column: 1 / -1;
|
|
text-align: center;
|
|
padding: 3rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.gallery-item {
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
padding: 0.75rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.gallery-item:hover {
|
|
border-color: var(--color-primary);
|
|
box-shadow: var(--shadow-glow-primary);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.gallery-item img {
|
|
width: 100%;
|
|
height: 150px;
|
|
object-fit: cover;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.gallery-item-info {
|
|
margin-top: 0.5rem;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.gallery-item-type {
|
|
background: var(--color-primary);
|
|
color: var(--text-primary);
|
|
padding: 0.2rem 0.5rem;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.gallery-item-name {
|
|
color: var(--text-secondary);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.audio-item,
|
|
.video-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
}
|
|
|
|
.audio-info,
|
|
.video-info {
|
|
text-align: center;
|
|
}
|
|
|
|
.audio-icon,
|
|
.video-icon {
|
|
font-size: 2rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.audio-name,
|
|
.video-name {
|
|
color: var(--text-primary);
|
|
font-weight: 500;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.audio-type,
|
|
.video-type {
|
|
color: var(--text-secondary);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* Photo Gallery Styles */
|
|
.gallery-categories {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-bottom: 1.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.gallery-category-btn {
|
|
background: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-primary);
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 0.9rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.gallery-category-btn:hover {
|
|
background: var(--bg-secondary-overlay-30);
|
|
border-color: var(--color-primary);
|
|
}
|
|
|
|
.gallery-category-btn.active {
|
|
background: var(--color-primary);
|
|
border-color: var(--color-primary);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.photo-galleries {
|
|
background: var(--bg-primary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.photo-gallery {
|
|
display: none;
|
|
}
|
|
|
|
.photo-gallery.active {
|
|
display: block;
|
|
}
|
|
|
|
.bulk-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
padding: 1rem;
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 6px;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.selection-controls,
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.selected-count {
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.photo-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.photo-item {
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.photo-item:hover {
|
|
border-color: var(--color-primary);
|
|
box-shadow: var(--shadow-glow-primary);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.photo-container {
|
|
position: relative;
|
|
}
|
|
|
|
.photo-container img {
|
|
width: 100%;
|
|
height: 180px;
|
|
object-fit: cover;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.photo-checkbox {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
left: 0.5rem;
|
|
z-index: 10;
|
|
}
|
|
|
|
.photo-checkbox input[type="checkbox"] {
|
|
width: 20px;
|
|
height: 20px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.photo-actions {
|
|
position: absolute;
|
|
bottom: 0.5rem;
|
|
right: 0.5rem;
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.photo-download-btn,
|
|
.photo-delete-btn {
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border-color);
|
|
padding: 0.4rem 0.6rem;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.photo-download-btn:hover {
|
|
background: var(--color-success);
|
|
}
|
|
|
|
.photo-delete-btn:hover {
|
|
background: var(--color-danger);
|
|
}
|
|
|
|
.photo-info {
|
|
padding: 0.5rem;
|
|
background: var(--bg-tertiary);
|
|
}
|
|
|
|
.photo-date,
|
|
.photo-type {
|
|
display: block;
|
|
font-size: 0.75rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.photo-type {
|
|
margin-top: 0.25rem;
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
.no-photos-message {
|
|
text-align: center;
|
|
padding: 3rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* Management Buttons */
|
|
.management-buttons {
|
|
display: flex;
|
|
gap: 1rem;
|
|
margin-top: 2rem;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* Desktop Feature Badge */
|
|
.desktop-feature {
|
|
background: var(--bg-secondary-overlay-20);
|
|
border-left: 3px solid var(--color-accent);
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 4px;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* Video Disabled Styles */
|
|
.video-disabled {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.video-disabled .video-thumbnail-container {
|
|
position: relative;
|
|
}
|
|
|
|
.disabled-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #ff4444;
|
|
font-weight: bold;
|
|
font-size: 1.2rem;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.btn-warning {
|
|
background: var(--color-warning, #ff9800);
|
|
}
|
|
|
|
.btn-warning:hover {
|
|
background: var(--color-warning-hover, #f57c00);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Loading Overlay -->
|
|
<div id="library-loading" class="loading-overlay" style="display: flex;">
|
|
<div class="loading-content">
|
|
<div class="loading-spinner"></div>
|
|
<h2>📚 Loading Media Library...</h2>
|
|
<p class="loading-status" id="loading-status">Initializing file systems...</p>
|
|
<div class="loading-progress">
|
|
<div class="loading-progress-fill" id="loading-progress-fill"></div>
|
|
</div>
|
|
<div class="loading-percentage" id="loading-percentage">0%</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Library Header -->
|
|
<header class="library-header">
|
|
<nav class="library-nav">
|
|
<div class="nav-left">
|
|
<h1>📚 Media Library</h1>
|
|
</div>
|
|
<div class="nav-center">
|
|
<p class="library-subtitle">Manage all your media content in one place</p>
|
|
</div>
|
|
<div class="nav-right">
|
|
<button id="manage-tags-btn" class="btn btn-primary">🏷️ Manage Tags</button>
|
|
<button id="refresh-library-btn" class="btn btn-primary">🔄 Refresh</button>
|
|
<button id="back-to-home-btn" class="btn btn-secondary">🏠 Home</button>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<!-- Main Library Content -->
|
|
<div class="library-container">
|
|
<!-- Library Navigation Tabs -->
|
|
<div class="library-tabs">
|
|
<button class="library-tab active" data-tab="images">🖼️ Images</button>
|
|
<button class="library-tab" data-tab="audio">🎵 Audio</button>
|
|
<button class="library-tab" data-tab="video">🎬 Video</button>
|
|
<button class="library-tab" data-tab="gallery">📸 Gallery</button>
|
|
</div>
|
|
|
|
<!-- Images Tab Content -->
|
|
<div id="library-images-content" class="library-content active">
|
|
<div class="content-section">
|
|
<h3>🖼️ Image Library Management</h3>
|
|
<p>Link directories from your computer to access image content</p>
|
|
|
|
<!-- Directory Management Section -->
|
|
<div class="directory-management-section">
|
|
<h4>📁 Linked Image Directories</h4>
|
|
<div class="directory-controls">
|
|
<button id="lib-add-image-directory-btn" class="btn btn-primary">📁 Add Directory(ies)</button>
|
|
<button id="lib-add-individual-images-btn" class="btn btn-primary">🖼️ Add Individual Images</button>
|
|
<button id="lib-refresh-image-directories-btn" class="btn btn-secondary">🔄 Refresh</button>
|
|
<button id="lib-clear-image-directories-btn" class="btn btn-danger">🗑️ Clear All</button>
|
|
<span id="lib-directories-count">0 directories linked</span>
|
|
</div>
|
|
<div class="linked-directories-container">
|
|
<div id="linked-image-directories-list">
|
|
<div class="no-directories">No image directories linked yet</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Image Gallery -->
|
|
<div class="gallery-section">
|
|
<div class="gallery-header">
|
|
<h4>🖼️ Current Image Library</h4>
|
|
<div class="gallery-controls">
|
|
<select id="lib-image-category-filter">
|
|
<option value="all">All Images</option>
|
|
<option value="untagged">📝 Untagged</option>
|
|
</select>
|
|
<span id="lib-image-count">0 images</span>
|
|
</div>
|
|
</div>
|
|
<div class="image-gallery active" id="lib-image-gallery">
|
|
<div class="no-images-message">
|
|
<p>🗃️ No images found in linked directories</p>
|
|
<p>Click "Add Directory" to link a folder containing images</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Audio Tab Content -->
|
|
<div id="library-audio-content" class="library-content">
|
|
<div class="content-section">
|
|
<h3>🎵 Audio Library Management</h3>
|
|
<p>WORK IN PROGRESS - FEATURE TO COME</p>
|
|
|
|
<p>Organize your background music and ambient sounds</p>
|
|
|
|
<!-- Audio Upload Section -->
|
|
<div class="upload-section">
|
|
<h4>🎵 Import Audio Files</h4>
|
|
<div class="upload-controls">
|
|
<button id="lib-import-background-music-btn" class="btn btn-primary">🎵 Background Music</button>
|
|
<button id="lib-import-ambient-audio-btn" class="btn btn-secondary">🌊 Ambient Sounds</button>
|
|
<input type="file" id="lib-audio-upload-input" accept="audio/*" multiple style="display: none;">
|
|
</div>
|
|
<div class="upload-info desktop-feature">
|
|
<span>💻 Desktop: Native file dialogs • Supports MP3, WAV, OGG, M4A formats</span>
|
|
</div>
|
|
<div class="directory-controls">
|
|
<button id="lib-audio-storage-info-btn" class="btn btn-outline">📊 Storage Info</button>
|
|
<button id="lib-cleanup-invalid-audio-btn" class="btn btn-warning">🧹 Cleanup</button>
|
|
<button id="lib-clear-all-audio-btn" class="btn btn-danger">🗑️ Clear All</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Audio Library -->
|
|
<div class="audio-gallery-section">
|
|
<div class="gallery-header">
|
|
<h4>🎵 Current Audio Library</h4>
|
|
<div class="gallery-controls">
|
|
<select id="lib-audio-category-filter">
|
|
<option value="all">All Categories</option>
|
|
<option value="background">Background Music</option>
|
|
<option value="ambient">Ambient Sounds</option>
|
|
</select>
|
|
<span id="lib-audio-count">0 files</span>
|
|
</div>
|
|
</div>
|
|
<div class="audio-gallery" id="lib-audio-gallery">
|
|
<div class="no-audio-message">
|
|
<p>🎵 No audio files found</p>
|
|
<p>Import audio to get started</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Video Tab Content -->
|
|
<div id="library-video-content" class="library-content">
|
|
<div class="content-section">
|
|
<h3>🎬 Video Library Management</h3>
|
|
<p>Manage your video content for enhanced training sessions</p>
|
|
|
|
<!-- Video Directory Management Section -->
|
|
<div class="directory-management-section">
|
|
<h4>📁 Linked Video Directories</h4>
|
|
<p>Link directories from your computer to access video content</p>
|
|
<div class="directory-controls">
|
|
<button id="lib-add-video-directory-btn" class="btn btn-primary">📁 Add Directory(ies)</button>
|
|
<button id="lib-add-individual-videos-btn" class="btn btn-primary">🎬 Add Individual Videos</button>
|
|
<button id="lib-refresh-video-directories-btn" class="btn btn-secondary">🔄 Refresh</button>
|
|
<button id="lib-clear-video-directories-btn" class="btn btn-danger">🗑️ Clear All</button>
|
|
<span id="lib-video-directories-count">0 directories linked</span>
|
|
</div>
|
|
<div class="linked-directories-container">
|
|
<div id="linked-video-directories-list">
|
|
<div class="no-directories">No video directories linked yet</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Video Library -->
|
|
<div class="video-gallery-section">
|
|
<div class="gallery-header">
|
|
<h4>🎬 Current Video Library</h4>
|
|
<div class="gallery-controls">
|
|
<select id="lib-video-category-filter">
|
|
<option value="all">All Videos</option>
|
|
<option value="untagged">📝 Untagged</option>
|
|
</select>
|
|
<span id="lib-video-count">0 files</span>
|
|
</div>
|
|
</div>
|
|
<div class="video-gallery active" id="lib-video-gallery">
|
|
<div class="no-video-message">
|
|
<p>🎬 No video files found</p>
|
|
<p>Import videos to get started</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Gallery Tab Content -->
|
|
<div id="library-gallery-content" class="library-content">
|
|
<div class="content-section">
|
|
<h3>📸 Photo Gallery</h3>
|
|
<p>Browse and organize your photo collections</p>
|
|
|
|
<!-- Gallery Categories -->
|
|
<div class="gallery-categories">
|
|
<button class="gallery-category-btn active" data-category="all">All Photos</button>
|
|
<button class="gallery-category-btn" data-category="dress-up">Dress Up</button>
|
|
<button class="gallery-category-btn" data-category="studio">Studio</button>
|
|
<button class="gallery-category-btn" data-category="custom">Custom</button>
|
|
</div>
|
|
|
|
<!-- Photo Gallery Display -->
|
|
<div class="photo-galleries">
|
|
<div id="lib-all-photos-gallery" class="photo-gallery active">
|
|
<div class="gallery-header">
|
|
<h4>📸 All Photos</h4>
|
|
<span class="photo-count" id="lib-all-photos-count">0 photos</span>
|
|
</div>
|
|
<div class="bulk-actions">
|
|
<div class="selection-controls">
|
|
<button id="select-all-photos" class="btn btn-small">☑️ Select All</button>
|
|
<button id="deselect-all-photos" class="btn btn-small">☐ Deselect All</button>
|
|
<span id="selected-count" class="selected-count">0 selected</span>
|
|
</div>
|
|
<div class="action-buttons">
|
|
<button id="download-selected-photos" class="btn btn-success" disabled>📥 Download Selected</button>
|
|
<button id="delete-selected-photos" class="btn btn-danger" disabled>🗑️ Delete Selected</button>
|
|
</div>
|
|
</div>
|
|
<div class="photo-grid" id="lib-all-photos-grid">
|
|
<div class="no-photos-message">
|
|
<p>📸 No photos found</p>
|
|
<p>Take some photos during gameplay to see them here</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="lib-dress-up-photos-gallery" class="photo-gallery">
|
|
<div class="gallery-header">
|
|
<h4>👗 Dress Up Photos</h4>
|
|
<span class="photo-count" id="lib-dress-up-photos-count">0 photos</span>
|
|
</div>
|
|
<div class="bulk-actions">
|
|
<div class="selection-controls">
|
|
<button id="select-all-dress-up" class="btn btn-small">☑️ Select All</button>
|
|
<button id="deselect-all-dress-up" class="btn btn-small">☐ Deselect All</button>
|
|
<span id="selected-dress-up-count" class="selected-count">0 selected</span>
|
|
</div>
|
|
<div class="action-buttons">
|
|
<button id="download-selected-dress-up" class="btn btn-success" disabled>📥 Download Selected</button>
|
|
<button id="delete-selected-dress-up" class="btn btn-danger" disabled>🗑️ Delete Selected</button>
|
|
</div>
|
|
</div>
|
|
<div class="photo-grid" id="lib-dress-up-photos-grid">
|
|
<div class="no-photos-message">
|
|
<p>👗 No dress up photos found</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="lib-studio-photos-gallery" class="photo-gallery">
|
|
<div class="gallery-header">
|
|
<h4>🎬 Studio Photos</h4>
|
|
<span class="photo-count" id="lib-studio-photos-count">0 photos</span>
|
|
</div>
|
|
<div class="photo-grid" id="lib-studio-photos-grid">
|
|
<div class="no-photos-message">
|
|
<p>🎬 No studio photos found</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="lib-custom-photos-gallery" class="photo-gallery">
|
|
<div class="gallery-header">
|
|
<h4>⭐ Custom Photos</h4>
|
|
<span class="photo-count" id="lib-custom-photos-count">0 photos</span>
|
|
</div>
|
|
<div class="photo-grid" id="lib-custom-photos-grid">
|
|
<div class="no-photos-message">
|
|
<p>⭐ No custom photos found</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Game Data System -->
|
|
<script src="src/data/modes/mainGameData.js"></script>
|
|
<script src="src/data/modes/humiliationGameData.js"></script>
|
|
<script src="src/data/modes/trainingGameData.js"></script>
|
|
<script src="src/data/modes/enduranceGameData.js"></script>
|
|
<script src="src/data/modes/dressUpGameData.js"></script>
|
|
<script src="src/data/gameDataManager.js"></script>
|
|
|
|
<!-- Desktop File Manager -->
|
|
<script src="src/utils/desktop-file-manager.js"></script>
|
|
|
|
<!-- Library Management JavaScript -->
|
|
<script>
|
|
// Navigation
|
|
document.getElementById('back-to-home-btn').addEventListener('click', () => {
|
|
window.location.href = 'index.html';
|
|
});
|
|
|
|
// Tab Switching
|
|
document.querySelectorAll('.library-tab').forEach(tab => {
|
|
tab.addEventListener('click', () => {
|
|
const targetTab = tab.dataset.tab;
|
|
|
|
// Remove active class from all tabs and content
|
|
document.querySelectorAll('.library-tab').forEach(t => t.classList.remove('active'));
|
|
document.querySelectorAll('.library-content').forEach(c => c.classList.remove('active'));
|
|
|
|
// Add active class to clicked tab and corresponding content
|
|
tab.classList.add('active');
|
|
document.getElementById(`library-${targetTab}-content`).classList.add('active');
|
|
});
|
|
});
|
|
|
|
// Gallery Category Switching
|
|
document.querySelectorAll('.gallery-category-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const category = btn.dataset.category;
|
|
|
|
// Remove active class from all category buttons
|
|
document.querySelectorAll('.gallery-category-btn').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
|
|
// Show corresponding gallery
|
|
document.querySelectorAll('.photo-gallery').forEach(g => g.classList.remove('active'));
|
|
|
|
if (category === 'all') {
|
|
document.getElementById('lib-all-photos-gallery').classList.add('active');
|
|
} else if (category === 'dress-up') {
|
|
document.getElementById('lib-dress-up-photos-gallery').classList.add('active');
|
|
} else if (category === 'studio') {
|
|
document.getElementById('lib-studio-photos-gallery').classList.add('active');
|
|
} else if (category === 'custom') {
|
|
document.getElementById('lib-custom-photos-gallery').classList.add('active');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Refresh Library Button
|
|
document.getElementById('refresh-library-btn').addEventListener('click', () => {
|
|
console.log('Refreshing library...');
|
|
refreshAllLibraryContent();
|
|
});
|
|
|
|
// Initialize library on page load
|
|
window.addEventListener('DOMContentLoaded', async () => {
|
|
console.log('Library page loaded');
|
|
|
|
const loadingStatus = document.getElementById('loading-status');
|
|
const loadingProgressFill = document.getElementById('loading-progress-fill');
|
|
const loadingPercentage = document.getElementById('loading-percentage');
|
|
|
|
function updateProgress(percent, status) {
|
|
if (loadingProgressFill) loadingProgressFill.style.width = `${percent}%`;
|
|
if (loadingPercentage) loadingPercentage.textContent = `${percent}%`;
|
|
if (loadingStatus) loadingStatus.textContent = status;
|
|
}
|
|
|
|
updateProgress(10, 'Initializing desktop file manager...');
|
|
|
|
// Create a data manager wrapper that matches the real DataManager behavior
|
|
// This ensures linked directories are stored in the same place across all pages
|
|
const simpleDataManager = {
|
|
data: null,
|
|
storageKey: 'webGame-data',
|
|
|
|
loadData() {
|
|
try {
|
|
const stored = localStorage.getItem(this.storageKey);
|
|
if (stored) {
|
|
this.data = JSON.parse(stored);
|
|
} else {
|
|
this.data = {};
|
|
}
|
|
} catch (e) {
|
|
console.error('Error loading data:', e);
|
|
this.data = {};
|
|
}
|
|
},
|
|
|
|
saveData() {
|
|
try {
|
|
if (!this.data) this.data = {};
|
|
this.data.timestamp = Date.now();
|
|
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
|
|
} catch (e) {
|
|
console.error('Error saving data:', e);
|
|
}
|
|
},
|
|
|
|
get(key) {
|
|
if (!this.data) this.loadData();
|
|
return this.data[key];
|
|
},
|
|
|
|
set(key, value) {
|
|
if (!this.data) this.loadData();
|
|
this.data[key] = value;
|
|
this.saveData();
|
|
}
|
|
};
|
|
|
|
// Load data initially
|
|
simpleDataManager.loadData();
|
|
|
|
// Check if running in Electron or browser mode
|
|
if (!window.electronAPI) {
|
|
console.warn('⚠️ Running in browser mode - linked directories will not work');
|
|
console.info('💡 To access local media files, run with: npm start');
|
|
|
|
// Show warning banner
|
|
const warningBanner = document.createElement('div');
|
|
warningBanner.style.cssText = `
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: linear-gradient(135deg, #f39c12 0%, #e74c3c 100%);
|
|
color: white;
|
|
padding: 15px 20px;
|
|
text-align: center;
|
|
z-index: 10001;
|
|
font-weight: bold;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
`;
|
|
warningBanner.innerHTML = `
|
|
⚠️ Browser Mode: Cannot access local directories.
|
|
To use linked media libraries, run with <code style="background: rgba(0,0,0,0.3); padding: 2px 6px; border-radius: 3px;">npm start</code> in Electron.
|
|
<button onclick="this.parentElement.remove()" style="margin-left: 20px; background: rgba(0,0,0,0.3); border: none; color: white; padding: 5px 15px; border-radius: 4px; cursor: pointer;">Dismiss</button>
|
|
`;
|
|
document.body.insertBefore(warningBanner, document.body.firstChild);
|
|
}
|
|
|
|
// Initialize desktop file manager first
|
|
if (window.electronAPI) {
|
|
console.log('📸 Initializing desktop file manager for library...');
|
|
if (!window.desktopFileManager) {
|
|
window.desktopFileManager = new DesktopFileManager(simpleDataManager);
|
|
// Wait for initialization to complete (includes loading linked directories)
|
|
await window.desktopFileManager.init();
|
|
} else {
|
|
// If already exists, ensure it has dataManager and reload linked directories
|
|
if (!window.desktopFileManager.dataManager) {
|
|
window.desktopFileManager.dataManager = simpleDataManager;
|
|
}
|
|
await window.desktopFileManager.loadLinkedDirectories();
|
|
}
|
|
console.log('📸 Desktop file manager ready:', !!window.desktopFileManager);
|
|
console.log('📸 isElectron:', window.desktopFileManager?.isElectron);
|
|
console.log('📸 photoDirectory:', window.desktopFileManager?.photoDirectory);
|
|
console.log('📸 Linked video directories:', window.desktopFileManager?.externalVideoDirectories?.length);
|
|
|
|
// Clean up legacy localStorage video directories
|
|
const legacyDirs = localStorage.getItem('linkedVideoDirectories');
|
|
const legacyIndividual = localStorage.getItem('linkedIndividualVideos');
|
|
if (legacyDirs || legacyIndividual) {
|
|
console.log('🧹 Cleaning up legacy localStorage video directories...');
|
|
localStorage.removeItem('linkedVideoDirectories');
|
|
localStorage.removeItem('linkedIndividualVideos');
|
|
console.log('✅ Legacy video directory storage cleared');
|
|
}
|
|
}
|
|
|
|
updateProgress(30, 'Loading image library...');
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Initialize all library tabs
|
|
setupLibraryImagesTab();
|
|
updateProgress(50, 'Loading audio library...');
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
setupLibraryAudioTab();
|
|
updateProgress(70, 'Loading video library...');
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
setupLibraryVideoTab();
|
|
updateProgress(90, 'Loading photo gallery...');
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
setupLibraryGalleryTab();
|
|
updateProgress(100, 'Library ready!');
|
|
|
|
// Hide loading overlay after brief delay
|
|
setTimeout(() => {
|
|
const loadingOverlay = document.getElementById('library-loading');
|
|
if (loadingOverlay) {
|
|
loadingOverlay.style.display = 'none';
|
|
}
|
|
}, 500);
|
|
});
|
|
|
|
function refreshAllLibraryContent() {
|
|
console.log('Refreshing all library content...');
|
|
|
|
// Refresh based on currently active tab
|
|
const activeTab = document.querySelector('.library-tab.active');
|
|
if (activeTab) {
|
|
const tabType = activeTab.getAttribute('data-tab');
|
|
switch(tabType) {
|
|
case 'images':
|
|
setupLibraryImagesTab();
|
|
break;
|
|
case 'audio':
|
|
setupLibraryAudioTab();
|
|
break;
|
|
case 'video':
|
|
setupLibraryVideoTab();
|
|
break;
|
|
case 'gallery':
|
|
setupLibraryGalleryTab();
|
|
break;
|
|
}
|
|
}
|
|
|
|
console.log('Library content refreshed');
|
|
}
|
|
</script>
|
|
|
|
<!-- Library Compatibility Layer -->
|
|
<script>
|
|
// Create minimal game object for compatibility
|
|
window.game = window.game || {};
|
|
|
|
// Simple DataManager for library page
|
|
if (!window.game.dataManager) {
|
|
window.game.dataManager = {
|
|
get: function(key) {
|
|
try {
|
|
const data = localStorage.getItem('library-' + key);
|
|
return data ? JSON.parse(data) : null;
|
|
} catch (error) {
|
|
console.error('Error loading data:', error);
|
|
return null;
|
|
}
|
|
},
|
|
set: function(key, value) {
|
|
try {
|
|
localStorage.setItem('library-' + key, JSON.stringify(value));
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error saving data:', error);
|
|
return false;
|
|
}
|
|
},
|
|
remove: function(key) {
|
|
try {
|
|
localStorage.removeItem('library-' + key);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error removing data:', error);
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
console.log('✅ DataManager initialized for library');
|
|
}
|
|
|
|
// Flash message manager placeholder
|
|
if (!window.game.flashMessageManager) {
|
|
window.game.flashMessageManager = {
|
|
show: function(message, type) {
|
|
console.log(`[${type}] ${message}`);
|
|
// Simple alert fallback
|
|
if (type === 'error') {
|
|
alert(message);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Notification placeholder
|
|
if (!window.game.showNotification) {
|
|
window.game.showNotification = function(message, type) {
|
|
console.log(`[${type}] ${message}`);
|
|
// Create a simple notification
|
|
const notification = document.createElement('div');
|
|
notification.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 1rem 1.5rem;
|
|
background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : type === 'warning' ? '#ff9800' : '#2196F3'};
|
|
color: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
|
|
z-index: 10000;
|
|
animation: slideIn 0.3s ease-out;
|
|
`;
|
|
notification.textContent = message;
|
|
document.body.appendChild(notification);
|
|
|
|
setTimeout(() => {
|
|
notification.style.animation = 'slideOut 0.3s ease-in';
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 3000);
|
|
};
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
@keyframes slideIn {
|
|
from {
|
|
transform: translateX(400px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
@keyframes slideOut {
|
|
from {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
to {
|
|
transform: translateX(400px);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<!-- Library Manager Functions -->
|
|
<script src="src/utils/libraryManager.js"></script>
|
|
|
|
<!-- Media Tagging System -->
|
|
<script src="src/features/media/mediaTagManager.js"></script>
|
|
<script src="src/features/media/mediaTagUI.js"></script>
|
|
<!-- Only use libraryTaggingIntegration.js for library.html, not globalTaggingInit.js -->
|
|
<script src="src/features/media/libraryTaggingIntegration.js"></script>
|
|
</body>
|
|
</html>
|