Add delete tag, episode count matching indicator, fix season episode counts
This commit is contained in:
parent
ef2fbd1a95
commit
92320cc908
88
index.html
88
index.html
@ -126,7 +126,47 @@
|
||||
#seasons-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.season-item {
|
||||
padding: 12px;
|
||||
background-color: #1a1a2e;
|
||||
border: 1px solid #16213e;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.season-item:hover {
|
||||
background-color: #e94560;
|
||||
border-color: #e94560;
|
||||
}
|
||||
|
||||
.season-item.selected {
|
||||
background-color: #0f3460;
|
||||
border-color: #e94560;
|
||||
}
|
||||
|
||||
#episodes-container {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.episode-item {
|
||||
padding: 10px;
|
||||
background-color: #0f3460;
|
||||
border-radius: 4px;
|
||||
color: #ccc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.episode-item:hover {
|
||||
background-color: #16213e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Main Content - Files */
|
||||
@ -218,13 +258,53 @@
|
||||
padding: 12px 15px;
|
||||
background-color: #16213e;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s;
|
||||
transition: all 0.2s;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.file-item:hover {
|
||||
background-color: #0f3460;
|
||||
}
|
||||
|
||||
.file-item.dragging {
|
||||
opacity: 0.5;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.file-item.drag-over {
|
||||
border: 2px dashed #e94560;
|
||||
background-color: #0f3460;
|
||||
}
|
||||
|
||||
.file-item.folder-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.episode-number {
|
||||
min-width: 40px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #e94560;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
color: #666;
|
||||
margin-right: 10px;
|
||||
cursor: grab;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.drag-handle:hover {
|
||||
color: #e94560;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
@ -273,6 +353,10 @@
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.delete-tag:hover {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.play-button {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
36
main.js
36
main.js
@ -213,14 +213,20 @@ ipcMain.handle('move-file-to-folder', async (event, { filePath, folderName }) =>
|
||||
|
||||
// Validate folder name and map to actual folder names
|
||||
// "extra" maps to "extras" for Jellyfin compatibility
|
||||
if (folderName !== 'extra' && folderName !== 'commentary') {
|
||||
const error = 'Invalid folder name. Must be "extra" or "commentary"';
|
||||
const validFolders = ['extra', 'commentary', 'delete'];
|
||||
if (!validFolders.includes(folderName)) {
|
||||
const error = 'Invalid folder name. Must be "extra", "commentary", or "delete"';
|
||||
writeLog(`ERROR: ${error}`);
|
||||
return { success: false, error: error };
|
||||
}
|
||||
|
||||
// Map folder names to actual directory names
|
||||
const actualFolderName = folderName === 'extra' ? 'extras' : 'commentary';
|
||||
let actualFolderName;
|
||||
if (folderName === 'extra') {
|
||||
actualFolderName = 'extras';
|
||||
} else {
|
||||
actualFolderName = folderName; // 'commentary' and 'delete' stay as-is
|
||||
}
|
||||
|
||||
// Get directory containing the file
|
||||
const fileDir = path.dirname(filePath);
|
||||
@ -421,6 +427,28 @@ ipcMain.handle('get-show-details', async (event, showId) => {
|
||||
if (response.data && response.data.data) {
|
||||
const showData = response.data.data;
|
||||
|
||||
// Fetch episodes to count per season
|
||||
let episodeCounts = {};
|
||||
try {
|
||||
const episodesResponse = await axios.get(`https://api4.thetvdb.com/v4/series/${numericId}/episodes/default`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
params: { page: 0 }
|
||||
});
|
||||
|
||||
if (episodesResponse.data?.data?.episodes) {
|
||||
episodesResponse.data.data.episodes.forEach(ep => {
|
||||
const sn = ep.seasonNumber;
|
||||
episodeCounts[sn] = (episodeCounts[sn] || 0) + 1;
|
||||
});
|
||||
}
|
||||
} catch (epError) {
|
||||
console.log('Could not fetch episodes for count:', epError.message);
|
||||
}
|
||||
|
||||
// Extract season information properly
|
||||
let seasons = [];
|
||||
if (showData.seasons && Array.isArray(showData.seasons)) {
|
||||
@ -428,7 +456,7 @@ ipcMain.handle('get-show-details', async (event, showId) => {
|
||||
id: season.id,
|
||||
number: season.number,
|
||||
type: season.type?.name || 'Unknown',
|
||||
episodeCount: season.episodeCount || (season.episodes ? season.episodes.length : 0)
|
||||
episodeCount: episodeCounts[season.number] || 0
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
372
renderer.js
372
renderer.js
@ -18,6 +18,7 @@ let currentFiles = [];
|
||||
let currentShow = null;
|
||||
let currentSeasons = [];
|
||||
let currentEpisodes = [];
|
||||
let selectedSeasonEpisodeCount = 0;
|
||||
|
||||
// Event Listeners
|
||||
selectDirBtn.addEventListener('click', selectDirectory);
|
||||
@ -177,31 +178,41 @@ function displayFiles(files) {
|
||||
return;
|
||||
}
|
||||
|
||||
files.forEach(file => {
|
||||
// Separate folders from media files
|
||||
const folders = files.filter(f => f.isFolder);
|
||||
const mediaFiles = files.filter(f => !f.isFolder);
|
||||
|
||||
// Track episode number for media files only
|
||||
let episodeNumber = 1;
|
||||
|
||||
// Display folders first (not draggable, no episode number)
|
||||
folders.forEach(file => {
|
||||
const fileItem = document.createElement('div');
|
||||
fileItem.className = 'file-item folder-item';
|
||||
fileItem.innerHTML = `
|
||||
<div style="font-size: 18px; margin-right: 10px;">📁</div>
|
||||
<div class="file-name folder-name" data-file-path="${file.path}">${file.name}</div>
|
||||
<div class="file-duration"></div>
|
||||
<div class="file-quality"></div>
|
||||
<div class="file-fps"></div>
|
||||
<div class="file-tags"></div>
|
||||
`;
|
||||
|
||||
// Add click handler to navigate into folder
|
||||
fileItem.addEventListener('click', function() {
|
||||
openDirectory(file.path);
|
||||
});
|
||||
|
||||
fileListEl.appendChild(fileItem);
|
||||
});
|
||||
|
||||
// Display media files with episode numbers and drag/drop
|
||||
mediaFiles.forEach((file, index) => {
|
||||
const fileItem = document.createElement('div');
|
||||
fileItem.className = 'file-item';
|
||||
|
||||
// Handle folders differently from media files
|
||||
if (file.isFolder) {
|
||||
fileItem.classList.add('folder-item');
|
||||
fileItem.style.cursor = 'pointer';
|
||||
fileItem.innerHTML = `
|
||||
<div style="font-size: 18px; margin-right: 10px;">📁</div>
|
||||
<div class="file-name folder-name" data-file-path="${file.path}">${file.name}</div>
|
||||
<div class="file-duration"></div>
|
||||
<div class="file-quality"></div>
|
||||
<div class="file-fps"></div>
|
||||
<div class="file-tags"></div>
|
||||
`;
|
||||
|
||||
// Add click handler to navigate into folder
|
||||
fileItem.addEventListener('click', function() {
|
||||
openDirectory(file.path);
|
||||
});
|
||||
|
||||
fileListEl.appendChild(fileItem);
|
||||
return;
|
||||
}
|
||||
fileItem.draggable = true;
|
||||
fileItem.dataset.index = index;
|
||||
fileItem.dataset.filePath = file.path;
|
||||
|
||||
// Use actual duration from file metadata
|
||||
const duration = file.duration || '00:00';
|
||||
@ -212,6 +223,8 @@ function displayFiles(files) {
|
||||
const problemLabel = file.isProblematic ? '<span class="problematic-label">⚠️</span> ' : '';
|
||||
|
||||
fileItem.innerHTML = `
|
||||
<div class="drag-handle">⋮⋮</div>
|
||||
<div class="episode-number" data-episode="${episodeNumber}">${episodeNumber}</div>
|
||||
<div class="file-name" data-file-path="${file.path}">${file.name}</div>
|
||||
<div class="file-duration">${duration}</div>
|
||||
<div class="file-quality">${quality}</div>
|
||||
@ -219,14 +232,23 @@ function displayFiles(files) {
|
||||
<div class="file-tags">
|
||||
<span class="tag-icon extra-tag" data-file-path="${file.path}" title="Mark as Extra">🏷️</span>
|
||||
<span class="tag-icon commentary-tag" data-file-path="${file.path}" title="Add Commentary">💬</span>
|
||||
<span class="tag-icon delete-tag" data-file-path="${file.path}" title="Mark for Deletion">🗑️</span>
|
||||
<span class="video-preview-btn" data-file-path="${file.path}" title="Preview Video">🎬</span>
|
||||
<button class="play-button" style="opacity: 0.3; cursor: default; flex-shrink: 0;" data-file-path="${file.path}" disabled>▶️</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add drag and drop event listeners
|
||||
fileItem.addEventListener('dragstart', handleDragStart);
|
||||
fileItem.addEventListener('dragend', handleDragEnd);
|
||||
fileItem.addEventListener('dragover', handleDragOver);
|
||||
fileItem.addEventListener('dragleave', handleDragLeave);
|
||||
fileItem.addEventListener('drop', handleDrop);
|
||||
|
||||
// Add click event to make file name editable
|
||||
const fileNameElement = fileItem.querySelector('.file-name');
|
||||
fileNameElement.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
makeEditable(e.target);
|
||||
});
|
||||
|
||||
@ -247,7 +269,16 @@ function displayFiles(files) {
|
||||
icon.addEventListener('click', function(e) {
|
||||
e.stopPropagation(); // Prevent event bubbling
|
||||
const filePath = this.dataset.filePath;
|
||||
const tagType = this.classList.contains('extra-tag') ? 'extra' : 'commentary';
|
||||
let tagType;
|
||||
if (this.classList.contains('extra-tag')) {
|
||||
tagType = 'extra';
|
||||
} else if (this.classList.contains('commentary-tag')) {
|
||||
tagType = 'commentary';
|
||||
} else if (this.classList.contains('delete-tag')) {
|
||||
tagType = 'delete';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if file is already tagged with this type
|
||||
const fileItem = this.closest('.file-item');
|
||||
@ -257,11 +288,12 @@ function displayFiles(files) {
|
||||
// Untag the file
|
||||
untagFile(filePath, tagType);
|
||||
} else {
|
||||
// If tagging this type, untag any existing tag of the other type
|
||||
const otherTagType = tagType === 'extra' ? 'commentary' : 'extra';
|
||||
if (fileItem.hasAttribute('data-tagged-' + otherTagType)) {
|
||||
untagFile(filePath, otherTagType);
|
||||
}
|
||||
// If tagging this type, untag any existing tag of other types
|
||||
['extra', 'commentary', 'delete'].forEach(otherType => {
|
||||
if (otherType !== tagType && fileItem.hasAttribute('data-tagged-' + otherType)) {
|
||||
untagFile(filePath, otherType);
|
||||
}
|
||||
});
|
||||
// Tag the file
|
||||
addTagToEpisode(filePath, tagType);
|
||||
}
|
||||
@ -284,8 +316,14 @@ function displayFiles(files) {
|
||||
|
||||
// Get the file item and tag type
|
||||
const fileItemEl = this.closest('.file-item');
|
||||
const tagType = fileItemEl.hasAttribute('data-tagged-extra') ? 'extra' :
|
||||
fileItemEl.hasAttribute('data-tagged-commentary') ? 'commentary' : null;
|
||||
let tagType = null;
|
||||
if (fileItemEl.hasAttribute('data-tagged-extra')) {
|
||||
tagType = 'extra';
|
||||
} else if (fileItemEl.hasAttribute('data-tagged-commentary')) {
|
||||
tagType = 'commentary';
|
||||
} else if (fileItemEl.hasAttribute('data-tagged-delete')) {
|
||||
tagType = 'delete';
|
||||
}
|
||||
|
||||
console.log('Tag type determined:', tagType);
|
||||
if (tagType) {
|
||||
@ -295,6 +333,8 @@ function displayFiles(files) {
|
||||
// Remove the item from the file list after successful move
|
||||
fileItemEl.remove();
|
||||
updateTaggedCount();
|
||||
updateEpisodeNumbers();
|
||||
checkEpisodeCountMatch();
|
||||
}
|
||||
} else {
|
||||
console.log('No tag type found for file');
|
||||
@ -302,6 +342,81 @@ function displayFiles(files) {
|
||||
});
|
||||
|
||||
fileListEl.appendChild(fileItem);
|
||||
episodeNumber++;
|
||||
});
|
||||
|
||||
// Check if episode count matches after displaying files
|
||||
checkEpisodeCountMatch();
|
||||
}
|
||||
|
||||
// Drag and drop handlers
|
||||
let draggedItem = null;
|
||||
|
||||
function handleDragStart(e) {
|
||||
draggedItem = this;
|
||||
this.classList.add('dragging');
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/plain', this.dataset.index);
|
||||
}
|
||||
|
||||
function handleDragEnd(e) {
|
||||
this.classList.remove('dragging');
|
||||
// Remove drag-over class from all items
|
||||
document.querySelectorAll('.file-item').forEach(item => {
|
||||
item.classList.remove('drag-over');
|
||||
});
|
||||
draggedItem = null;
|
||||
}
|
||||
|
||||
function handleDragOver(e) {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// Only show drag-over for non-folder items
|
||||
if (!this.classList.contains('folder-item') && this !== draggedItem) {
|
||||
this.classList.add('drag-over');
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragLeave(e) {
|
||||
this.classList.remove('drag-over');
|
||||
}
|
||||
|
||||
function handleDrop(e) {
|
||||
e.preventDefault();
|
||||
this.classList.remove('drag-over');
|
||||
|
||||
if (this === draggedItem || this.classList.contains('folder-item')) return;
|
||||
|
||||
// Get all media file items (not folders)
|
||||
const fileItems = Array.from(fileListEl.querySelectorAll('.file-item:not(.folder-item)'));
|
||||
const draggedIndex = fileItems.indexOf(draggedItem);
|
||||
const dropIndex = fileItems.indexOf(this);
|
||||
|
||||
if (draggedIndex === -1 || dropIndex === -1) return;
|
||||
|
||||
// Move the dragged item in the DOM
|
||||
if (draggedIndex < dropIndex) {
|
||||
this.parentNode.insertBefore(draggedItem, this.nextSibling);
|
||||
} else {
|
||||
this.parentNode.insertBefore(draggedItem, this);
|
||||
}
|
||||
|
||||
// Update episode numbers
|
||||
updateEpisodeNumbers();
|
||||
}
|
||||
|
||||
function updateEpisodeNumbers() {
|
||||
const fileItems = fileListEl.querySelectorAll('.file-item:not(.folder-item)');
|
||||
let episodeNum = 1;
|
||||
|
||||
fileItems.forEach(item => {
|
||||
const episodeEl = item.querySelector('.episode-number');
|
||||
if (episodeEl) {
|
||||
episodeEl.textContent = episodeNum;
|
||||
episodeEl.dataset.episode = episodeNum;
|
||||
episodeNum++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -384,13 +499,22 @@ function addTagToEpisode(filePath, tagType) {
|
||||
// Add visual indication of tagging using data attribute for CSS styling
|
||||
const tagIcon = item.querySelector(`.${tagType}-tag`);
|
||||
if (tagIcon) {
|
||||
// Make the icon fully saturated and highlight in bright yellow with very strong visual effect
|
||||
// Set color based on tag type
|
||||
let tagColor;
|
||||
if (tagType === 'extra') {
|
||||
tagColor = '#FFD700'; // Yellow
|
||||
} else if (tagType === 'commentary') {
|
||||
tagColor = '#17a2b8'; // Teal
|
||||
} else if (tagType === 'delete') {
|
||||
tagColor = '#dc3545'; // Red
|
||||
}
|
||||
|
||||
// Make the icon fully saturated and highlight
|
||||
tagIcon.style.opacity = '1';
|
||||
tagIcon.style.filter = 'none';
|
||||
tagIcon.style.color = tagType === 'extra' ? '#FFD700' : '#17a2b8'; // Brighter yellow or teal color
|
||||
tagIcon.style.textShadow = '0 0 15px rgba(255, 215, 0, 1)'; // Very bright yellow glow (for extra tags)
|
||||
tagIcon.style.transform = 'scale(1.3)'; // More pronounced enlargement
|
||||
tagIcon.style.boxShadow = '0 0 10px rgba(255, 215, 0, 0.8)'; // Additional glow effect
|
||||
tagIcon.style.color = tagColor;
|
||||
tagIcon.style.textShadow = `0 0 15px ${tagColor}`;
|
||||
tagIcon.style.transform = 'scale(1.3)';
|
||||
|
||||
// Add a data attribute to track that this file is tagged
|
||||
item.setAttribute('data-tagged-' + tagType, 'true');
|
||||
@ -448,7 +572,7 @@ function moveTaggedFile(filePath, tagType) {
|
||||
|
||||
// Function to move all tagged files at once
|
||||
async function moveAllTaggedFiles() {
|
||||
const taggedItems = document.querySelectorAll('.file-item[data-tagged-extra], .file-item[data-tagged-commentary]');
|
||||
const taggedItems = document.querySelectorAll('.file-item[data-tagged-extra], .file-item[data-tagged-commentary], .file-item[data-tagged-delete]');
|
||||
|
||||
if (taggedItems.length === 0) {
|
||||
console.log('[RENDERER] No tagged files to move');
|
||||
@ -467,7 +591,14 @@ async function moveAllTaggedFiles() {
|
||||
const results = [];
|
||||
for (const item of taggedItems) {
|
||||
const filePath = item.querySelector('.file-name').dataset.filePath;
|
||||
const tagType = item.hasAttribute('data-tagged-extra') ? 'extra' : 'commentary';
|
||||
let tagType;
|
||||
if (item.hasAttribute('data-tagged-extra')) {
|
||||
tagType = 'extra';
|
||||
} else if (item.hasAttribute('data-tagged-commentary')) {
|
||||
tagType = 'commentary';
|
||||
} else if (item.hasAttribute('data-tagged-delete')) {
|
||||
tagType = 'delete';
|
||||
}
|
||||
|
||||
const result = await moveTaggedFile(filePath, tagType);
|
||||
results.push(result);
|
||||
@ -486,6 +617,8 @@ async function moveAllTaggedFiles() {
|
||||
|
||||
// Update the tagged count after all moves
|
||||
updateTaggedCount();
|
||||
updateEpisodeNumbers();
|
||||
checkEpisodeCountMatch();
|
||||
|
||||
// Summary of results
|
||||
const successful = results.filter(r => r.success).length;
|
||||
@ -517,9 +650,20 @@ function displayCreatedFolder(folderName) {
|
||||
folderItem.className = 'folder-item';
|
||||
folderItem.setAttribute('data-folder-name', folderName);
|
||||
|
||||
const displayName = folderName === 'extra' ? 'extras' : 'commentary';
|
||||
const folderIcon = folderName === 'extra' ? '📁' : '💬';
|
||||
const folderColor = folderName === 'extra' ? '#FFD700' : '#17a2b8';
|
||||
let displayName, folderIcon, folderColor;
|
||||
if (folderName === 'extra') {
|
||||
displayName = 'extras';
|
||||
folderIcon = '📁';
|
||||
folderColor = '#FFD700';
|
||||
} else if (folderName === 'commentary') {
|
||||
displayName = 'commentary';
|
||||
folderIcon = '💬';
|
||||
folderColor = '#17a2b8';
|
||||
} else if (folderName === 'delete') {
|
||||
displayName = 'delete';
|
||||
folderIcon = '🗑️';
|
||||
folderColor = '#dc3545';
|
||||
}
|
||||
|
||||
folderItem.innerHTML = `
|
||||
<div class="folder-icon" style="font-size: 24px;">${folderIcon}</div>
|
||||
@ -533,7 +677,7 @@ function displayCreatedFolder(folderName) {
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
background-color: #f8f9fa;
|
||||
background-color: #16213e;
|
||||
border: 2px solid ${folderColor};
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
@ -579,13 +723,18 @@ function untagFile(filePath, tagType) {
|
||||
// Remove the data attribute
|
||||
item.removeAttribute('data-tagged-' + tagType);
|
||||
|
||||
// Disable the play button when untagging
|
||||
const playButton = item.querySelector('.play-button');
|
||||
if (playButton) {
|
||||
playButton.style.opacity = '0.3';
|
||||
playButton.style.cursor = 'default';
|
||||
playButton.disabled = true;
|
||||
playButton.style.pointerEvents = 'none';
|
||||
// Disable the play button when untagging (only if no other tags)
|
||||
const hasOtherTags = item.hasAttribute('data-tagged-extra') ||
|
||||
item.hasAttribute('data-tagged-commentary') ||
|
||||
item.hasAttribute('data-tagged-delete');
|
||||
if (!hasOtherTags) {
|
||||
const playButton = item.querySelector('.play-button');
|
||||
if (playButton) {
|
||||
playButton.style.opacity = '0.3';
|
||||
playButton.style.cursor = 'default';
|
||||
playButton.disabled = true;
|
||||
playButton.style.pointerEvents = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -601,7 +750,7 @@ function untagFile(filePath, tagType) {
|
||||
|
||||
// Function to update the tagged items count display
|
||||
function updateTaggedCount() {
|
||||
const taggedItems = document.querySelectorAll('.file-item[data-tagged-extra], .file-item[data-tagged-commentary]');
|
||||
const taggedItems = document.querySelectorAll('.file-item[data-tagged-extra], .file-item[data-tagged-commentary], .file-item[data-tagged-delete]');
|
||||
const count = taggedItems.length;
|
||||
const taggedCircle = document.getElementById('tagged-circle');
|
||||
const taggedCount = document.getElementById('tagged-count');
|
||||
@ -624,6 +773,24 @@ function updateTaggedCount() {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if media file count matches selected season episode count
|
||||
function checkEpisodeCountMatch() {
|
||||
const fileList = document.getElementById('file-list');
|
||||
// Count only actual media files (not folders)
|
||||
const mediaFiles = document.querySelectorAll('.file-item:not(.folder-item)');
|
||||
const mediaCount = mediaFiles.length;
|
||||
|
||||
if (selectedSeasonEpisodeCount > 0 && mediaCount === selectedSeasonEpisodeCount) {
|
||||
// Perfect match - add green outline
|
||||
fileList.style.border = '3px solid #28a745';
|
||||
fileList.style.boxShadow = '0 0 15px rgba(40, 167, 69, 0.4)';
|
||||
} else {
|
||||
// No match or no season selected - remove green outline
|
||||
fileList.style.border = '';
|
||||
fileList.style.boxShadow = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize tagged count on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
updateTaggedCount();
|
||||
@ -769,26 +936,13 @@ function displaySeasons(seasons) {
|
||||
seasonsContainer.innerHTML = '';
|
||||
|
||||
if (!seasons || seasons.length === 0) {
|
||||
seasonsContainer.innerHTML = '<p>No seasons available.</p>';
|
||||
seasonsContainer.innerHTML = '<p style="color: #888;">No seasons available.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container for seasons
|
||||
const seasonsList = document.createElement('div');
|
||||
seasonsList.style.marginTop = '10px';
|
||||
|
||||
seasons.forEach(season => {
|
||||
const seasonItem = document.createElement('div');
|
||||
seasonItem.className = 'season-item';
|
||||
seasonItem.style.padding = '12px';
|
||||
seasonItem.style.margin = '8px 0';
|
||||
seasonItem.style.border = '1px solid #ddd';
|
||||
seasonItem.style.borderRadius = '6px';
|
||||
seasonItem.style.cursor = 'pointer';
|
||||
seasonItem.style.backgroundColor = '#f8f9fa';
|
||||
seasonItem.style.display = 'flex';
|
||||
seasonItem.style.justifyContent = 'space-between';
|
||||
seasonItem.style.alignItems = 'center';
|
||||
|
||||
// Format season display with episode count
|
||||
let seasonDisplay = `Season ${season.number}`;
|
||||
@ -799,21 +953,25 @@ function displaySeasons(seasons) {
|
||||
const episodeCount = season.episodeCount || 0;
|
||||
|
||||
seasonItem.innerHTML = `
|
||||
<span>${seasonDisplay}</span>
|
||||
<span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 12px; font-size: 12px;">
|
||||
${episodeCount} episodes
|
||||
</span>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>${seasonDisplay}</span>
|
||||
<span style="background-color: #e94560; color: white; padding: 4px 8px; border-radius: 12px; font-size: 11px;">
|
||||
${episodeCount} eps
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add click event to fetch episodes
|
||||
seasonItem.addEventListener('click', () => {
|
||||
// Remove selected class from all seasons
|
||||
document.querySelectorAll('.season-item').forEach(item => item.classList.remove('selected'));
|
||||
// Add selected class to clicked season
|
||||
seasonItem.classList.add('selected');
|
||||
fetchAndDisplayEpisodes(currentShow.id, season.number);
|
||||
});
|
||||
|
||||
seasonsList.appendChild(seasonItem);
|
||||
seasonsContainer.appendChild(seasonItem);
|
||||
});
|
||||
|
||||
seasonsContainer.appendChild(seasonsList);
|
||||
}
|
||||
|
||||
// Fetch and display episodes for a season
|
||||
@ -849,70 +1007,50 @@ function displayEpisodes(episodes) {
|
||||
seasonsContainer.innerHTML = '';
|
||||
|
||||
if (!episodes || episodes.length === 0) {
|
||||
seasonsContainer.innerHTML = '<p>No episodes available.</p>';
|
||||
seasonsContainer.innerHTML = '<p style="color: #888;">No episodes available.</p>';
|
||||
selectedSeasonEpisodeCount = 0;
|
||||
checkEpisodeCountMatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container for episodes table
|
||||
const episodesTable = document.createElement('div');
|
||||
episodesTable.style.marginTop = '10px';
|
||||
episodesTable.style.border = '1px solid #ddd';
|
||||
episodesTable.style.borderRadius = '6px';
|
||||
episodesTable.style.overflow = 'hidden';
|
||||
// Store the episode count for this season
|
||||
selectedSeasonEpisodeCount = episodes.length;
|
||||
checkEpisodeCountMatch();
|
||||
|
||||
// Create table header
|
||||
const tableHeader = document.createElement('div');
|
||||
tableHeader.style.display = 'grid';
|
||||
tableHeader.style.gridTemplateColumns = '1fr 1fr';
|
||||
tableHeader.style.backgroundColor = '#f8f9fa';
|
||||
tableHeader.style.padding = '10px';
|
||||
tableHeader.style.fontWeight = 'bold';
|
||||
tableHeader.style.borderBottom = '1px solid #ddd';
|
||||
// Create a back button to return to seasons
|
||||
const backButton = document.createElement('div');
|
||||
backButton.className = 'season-item';
|
||||
backButton.innerHTML = '← Back to Seasons';
|
||||
backButton.addEventListener('click', () => {
|
||||
selectedSeasonEpisodeCount = 0;
|
||||
checkEpisodeCountMatch();
|
||||
displaySeasons(currentSeasons);
|
||||
});
|
||||
seasonsContainer.appendChild(backButton);
|
||||
|
||||
tableHeader.innerHTML = `
|
||||
<div>Episode Name</div>
|
||||
<div>Runtime</div>
|
||||
`;
|
||||
|
||||
episodesTable.appendChild(tableHeader);
|
||||
// Create episodes container
|
||||
const episodesContainer = document.createElement('div');
|
||||
episodesContainer.id = 'episodes-container';
|
||||
|
||||
episodes.forEach(episode => {
|
||||
const episodeRow = document.createElement('div');
|
||||
episodeRow.className = 'episode-item';
|
||||
episodeRow.style.display = 'grid';
|
||||
episodeRow.style.gridTemplateColumns = '1fr 1fr';
|
||||
episodeRow.style.padding = '10px';
|
||||
episodeRow.style.borderBottom = '1px solid #eee';
|
||||
episodeRow.style.backgroundColor = '#fff';
|
||||
episodeRow.style.cursor = 'pointer';
|
||||
episodeRow.style.transition = 'background-color 0.2s';
|
||||
|
||||
episodeRow.addEventListener('mouseenter', () => {
|
||||
episodeRow.style.backgroundColor = '#f5f5f5';
|
||||
});
|
||||
|
||||
episodeRow.addEventListener('mouseleave', () => {
|
||||
episodeRow.style.backgroundColor = '#fff';
|
||||
});
|
||||
|
||||
// Format episode display
|
||||
const episodeName = episode.name || 'Untitled';
|
||||
const episodeRuntime = episode.runtime ? `${episode.runtime} min` : 'N/A';
|
||||
const episodeRuntime = episode.runtime ? `${episode.runtime}m` : '';
|
||||
|
||||
episodeRow.innerHTML = `
|
||||
<div style="font-weight: 500;">Episode ${episode.number}: ${episodeName}</div>
|
||||
<div style="color: #666;">${episodeRuntime}</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span><strong>E${episode.number}</strong> ${episodeName}</span>
|
||||
${episodeRuntime ? `<span style="color: #888; font-size: 11px;">${episodeRuntime}</span>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add click event (could show more details)
|
||||
episodeRow.addEventListener('click', () => {
|
||||
alert(`Episode: ${episode.name || 'Untitled'}\nRuntime: ${episode.runtime || 'N/A'} min\nAired: ${episode.aired || 'N/A'}`);
|
||||
});
|
||||
|
||||
episodesTable.appendChild(episodeRow);
|
||||
episodesContainer.appendChild(episodeRow);
|
||||
});
|
||||
|
||||
seasonsContainer.appendChild(episodesTable);
|
||||
seasonsContainer.appendChild(episodesContainer);
|
||||
|
||||
// Also update the main file list to show episode matching information
|
||||
updateFileListWithEpisodeInfo(episodes);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user