diff --git a/coverage/AppState.js.html b/coverage/AppState.js.html deleted file mode 100644 index 1fc60d4..0000000 --- a/coverage/AppState.js.html +++ /dev/null @@ -1,697 +0,0 @@ - - - - -
-- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 | 1x - -1x -1x -1x -1x -1x -22x -22x -22x -22x -22x -22x -22x -22x -22x -22x -22x -22x - -1x -1x -1x -1x -1x -4x -4x - -1x -1x -1x -1x -1x -4x -4x - -1x -1x -1x -1x -1x -3x -3x - -1x -1x -1x -1x -1x -4x -4x - -1x -1x -1x -1x -1x -2x -2x - -1x -1x -1x -1x -1x -3x -3x - -1x -1x -1x -1x -1x -2x -2x - -1x -1x -1x -1x -1x -3x -3x - -1x -1x -1x -1x -1x -2x -2x - -1x -1x -1x -1x -1x -3x -3x - -1x -1x -1x -1x -1x -2x -2x - -1x -1x -1x -1x -1x -3x -3x - -1x -1x -1x -1x -1x - - - -1x -1x -1x -1x -1x -3x -3x - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x -1x -22x -22x -22x -22x -22x - -1x -1x -1x -1x -1x -5x -3x -3x -3x -3x -2x -5x - -1x -1x -1x -1x -1x -9x -9x - -1x -1x -1x -1x -1x -2x -2x - -1x -1x -1x -1x -1x -5x -5x - -1x -1x -1x -1x -1x -6x -6x -1x - -1x | const path = require('path');
-
-/**
- * AppState - Manages application state
- */
-class AppState {
- constructor() {
- this.currentDirectory = null;
- this.currentFiles = [];
- this.currentShow = null;
- this.currentSeasons = [];
- this.currentEpisodes = [];
- this.selectedSeasonEpisodeCount = 0;
- this.isUpdatingEpisodeNumbers = false;
-
- // Navigation stack for folder navigation
- this.navigationStack = [];
- this.currentDepth = 0;
- }
-
- /**
- * Set the current directory
- * @param {string} directory - Directory path
- */
- setCurrentDirectory(directory) {
- this.currentDirectory = directory;
- }
-
- /**
- * Get the current directory
- * @returns {string|null} Current directory path
- */
- getCurrentDirectory() {
- return this.currentDirectory;
- }
-
- /**
- * Set current files
- * @param {Array} files - Array of file objects
- */
- setCurrentFiles(files) {
- this.currentFiles = files;
- }
-
- /**
- * Get current files
- * @returns {Array} Array of file objects
- */
- getCurrentFiles() {
- return this.currentFiles;
- }
-
- /**
- * Set current show
- * @param {Object} show - Show object
- */
- setCurrentShow(show) {
- this.currentShow = show;
- }
-
- /**
- * Get current show
- * @returns {Object|null} Current show object
- */
- getCurrentShow() {
- return this.currentShow;
- }
-
- /**
- * Set current seasons
- * @param {Array} seasons - Array of season objects
- */
- setCurrentSeasons(seasons) {
- this.currentSeasons = seasons;
- }
-
- /**
- * Get current seasons
- * @returns {Array} Array of season objects
- */
- getCurrentSeasons() {
- return this.currentSeasons;
- }
-
- /**
- * Set current episodes
- * @param {Array} episodes - Array of episode objects
- */
- setCurrentEpisodes(episodes) {
- this.currentEpisodes = episodes;
- }
-
- /**
- * Get current episodes
- * @returns {Array} Array of episode objects
- */
- getCurrentEpisodes() {
- return this.currentEpisodes;
- }
-
- /**
- * Set selected season episode count
- * @param {number} count - Episode count
- */
- setSelectedSeasonEpisodeCount(count) {
- this.selectedSeasonEpisodeCount = count;
- }
-
- /**
- * Get selected season episode count
- * @returns {number} Episode count
- */
- getSelectedSeasonEpisodeCount() {
- return this.selectedSeasonEpisodeCount;
- }
-
- /**
- * Check if episode numbers are currently updating
- * @returns {boolean} True if updating
- */
- isUpdatingEpisodeNumbers() {
- return this.isUpdatingEpisodeNumbers;
- }
-
- /**
- * Set episode numbers updating flag
- * @param {boolean} updating - Updating state
- */
- setUpdatingEpisodeNumbers(updating) {
- this.isUpdatingEpisodeNumbers = updating;
- }
-
- /**
- * Reset state to initial values
- */
- reset() {
- this.currentDirectory = null;
- this.currentFiles = [];
- this.currentShow = null;
- this.currentSeasons = [];
- this.currentEpisodes = [];
- this.selectedSeasonEpisodeCount = 0;
- this.isUpdatingEpisodeNumbers = false;
- this.navigationStack = [];
- this.currentDepth = 0;
- }
-
- /**
- * Add directory to navigation stack
- * @param {string} directory - Directory path to add
- */
- addToNavigationStack(directory) {
- // Remove any forward history if navigating from middle of stack
- this.navigationStack = this.navigationStack.slice(0, this.currentDepth + 1);
- this.navigationStack.push(directory);
- this.currentDepth = this.navigationStack.length - 1;
- }
-
- /**
- * Go back to previous directory in navigation stack
- * @returns {string|null} Previous directory path or null if at root
- */
- goBack() {
- if (this.canGoBack()) {
- this.navigationStack.pop();
- this.currentDepth--;
- return this.navigationStack[this.currentDepth];
- }
- return null;
- }
-
- /**
- * Check if can go back in navigation history
- * @returns {boolean} True if can go back
- */
- canGoBack() {
- return this.currentDepth > 0;
- }
-
- /**
- * Get current directory from navigation stack
- * @returns {string|null} Current directory path
- */
- getCurrentDirectoryFromStack() {
- return this.navigationStack[this.currentDepth] || null;
- }
-
- /**
- * Get navigation depth
- * @returns {number} Current navigation depth
- */
- getNavigationDepth() {
- return this.currentDepth;
- }
-
- /**
- * Get navigation stack
- * @returns {Array} Array of visited directories
- */
- getNavigationStack() {
- return this.navigationStack;
- }
-}
-
-module.exports = AppState; |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 | 1x -1x -1x -1x -1x -21x -21x - -1x -1x -1x -1x -1x -6x -1x -1x -1x - -5x - -5x -5x -5x - -5x -6x -6x -6x -6x - -6x -6x - -6x -6x -6x -6x -6x -5x -5x -5x -5x -6x - -1x -1x -1x -1x -1x -2x - -1x -1x -1x - -1x -1x -1x - -1x -1x -1x -1x -1x - -1x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -1x - -1x - - - - -1x - -1x -2x - -1x -1x -1x -1x -1x -1x -1x -4x -4x - -4x - -3x -4x -4x - -4x -4x - -4x -4x -2x -2x -1x -1x -4x -1x -1x -1x - -3x -3x -3x - -3x -3x - -3x -3x -3x -3x -3x -3x -3x -3x -3x - -4x - -3x -4x -1x - - - - - - - - - - - -4x -2x -2x -2x - - - - - - - - - - - -2x - -3x -3x -4x - -1x -1x -1x -1x -1x -1x -1x -2x - -1x - -1x -3x -3x - -3x -3x - -3x -2x -2x -3x -1x -2x - -1x -1x -1x -1x -1x -2x - -1x - -1x -1x -1x -2x - -1x -1x -1x -1x -1x -1x -4x -4x -4x -4x - -1x -1x -1x -1x -1x -1x -2x -2x - -2x -2x -2x -2x -2x -2x -2x - -2x -2x - -1x -1x -1x -1x -1x -1x -2x -2x - -2x -1x -1x -1x -1x -1x -1x -1x -2x - -2x -2x - -1x -1x -1x -1x -1x -4x -4x - -1x -1x -1x -1x -1x -3x -3x -1x - -1x | /**
- * EpisodeManager - Manages episode number editing and highlighting
- */
-class EpisodeManager {
- constructor() {
- this.isUpdatingEpisodeNumbers = false;
- }
-
- /**
- * Update episode numbers in the file list
- * @param {HTMLElement} fileListEl - File list element
- */
- updateEpisodeNumbers(fileListEl) {
- if (this.isUpdatingEpisodeNumbers) {
- console.log('[updateEpisodeNumbers] Already updating, skipping re-entrant call');
- return;
- }
-
- this.isUpdatingEpisodeNumbers = true;
-
- try {
- const fileItems = fileListEl.querySelectorAll('.file-item:not(.folder-item)');
- let episodeNum = 1;
-
- fileItems.forEach((item, index) => {
- const episodeEl = item.querySelector('.episode-number');
- if (episodeEl) {
- const storedStart = episodeEl.dataset.episodeStart;
- const storedEnd = episodeEl.dataset.episodeEnd;
-
- const start = storedStart ? parseInt(storedStart) : episodeNum;
- const end = storedEnd ? parseInt(storedEnd) : start;
-
- const rangeText = start === end ? `${start}` : `${start}-${end}`;
- episodeEl.textContent = rangeText;
- episodeEl.dataset.episode = episodeNum;
- episodeNum = end + 1;
- }
- });
- } finally {
- this.isUpdatingEpisodeNumbers = false;
- }
- }
-
- /**
- * Make episode range editable
- * @param {HTMLElement} element - Episode number element
- */
- makeEpisodeRangeEditable(element) {
- if (element.contentEditable === 'true') return;
-
- const originalText = element.textContent;
- const originalStart = element.dataset.episodeStart;
- const originalEnd = element.dataset.episodeEnd;
-
- element.contentEditable = 'true';
- element.focus();
- element.classList.add('editing');
-
- const range = document.createRange();
- range.selectNodeContents(element);
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
-
- const saveEdit = () => {
- const newText = element.textContent.trim();
-
- if (newText !== originalText) {
- let start, end;
-
- if (newText.includes('-')) {
- const parts = newText.split('-').map(p => parseInt(p.trim()));
- start = parts[0];
- end = parts[1] || start;
- } else {
- const num = parseInt(newText);
- start = num;
- end = num;
- }
-
- if (!isNaN(start) && !isNaN(end)) {
- element.dataset.episodeStart = start;
- element.dataset.episodeEnd = end;
- console.log(`Episode range updated: ${start}-${end}`);
- this.updateEpisodeNumbers(element.closest('#file-list'));
- } else {
- element.textContent = originalText;
- element.dataset.episodeStart = originalStart;
- element.dataset.episodeEnd = originalEnd;
- }
- }
-
- element.contentEditable = 'false';
- element.classList.remove('editing');
- };
-
- element.addEventListener('keydown', function(e) {
- if (e.key === 'Enter') {
- e.preventDefault();
- saveEdit();
- }
- }, { once: true });
-
- element.addEventListener('blur', saveEdit, { once: true });
- }
-
- /**
- * Handle episode arrow click
- * @param {HTMLElement} element - Arrow element
- * @param {string} direction - Direction (left or right)
- * @param {HTMLElement} fileListEl - File list element
- */
- handleEpisodeArrowClick(element, direction, fileListEl) {
- const fileItem = element.closest('.file-item');
- const episodeEl = fileItem.querySelector('.episode-number');
-
- if (!episodeEl) return;
-
- // Get current values before updating
- let start = parseInt(episodeEl.dataset.episodeStart) || 1;
- let end = parseInt(episodeEl.dataset.episodeEnd) || start;
-
- const oldStart = start;
- const oldEnd = end;
-
- // Update the episode range
- if (direction === 'left') {
- // Decrease range - move start back by 1
- if (start > 1) {
- start--;
- }
- } else {
- // Increase range - move end forward by 1
- end++;
- }
-
- // Update this episode's data attributes
- episodeEl.dataset.episodeStart = start;
- episodeEl.dataset.episodeEnd = end;
-
- // Get all media file items (not folders) in order
- const allFileItems = Array.from(fileListEl.querySelectorAll('.file-item:not(.folder-item)'));
-
- // Find current index by matching file path
- const filePath = fileItem.dataset.filePath;
- let currentIndex = -1;
- for (let i = 0; i < allFileItems.length; i++) {
- if (allFileItems[i].dataset.filePath === filePath) {
- currentIndex = i;
- break;
- }
- }
-
- if (currentIndex === -1) return;
-
- // For right arrow: shift all subsequent episodes forward by 1
- if (direction === 'right') {
- for (let i = currentIndex + 1; i < allFileItems.length; i++) {
- const nextItem = allFileItems[i];
- const nextEpisodeEl = nextItem.querySelector('.episode-number');
-
- if (nextEpisodeEl) {
- let nextStart = parseInt(nextEpisodeEl.dataset.episodeStart) || 1;
- const nextEnd = parseInt(nextEpisodeEl.dataset.episodeEnd) || nextStart;
-
- nextEpisodeEl.dataset.episodeStart = nextStart + 1;
- nextEpisodeEl.dataset.episodeEnd = nextEnd + 1;
- }
- }
- } else {
- // For left arrow: all subsequent episodes shift down
- const shiftAmount = oldStart - start;
- for (let i = currentIndex + 1; i < allFileItems.length; i++) {
- const nextItem = allFileItems[i];
- const nextEpisodeEl = nextItem.querySelector('.episode-number');
-
- if (nextEpisodeEl) {
- let nextStart = parseInt(nextEpisodeEl.dataset.episodeStart) || 1;
- const nextEnd = parseInt(nextEpisodeEl.dataset.episodeEnd) || nextStart;
-
- nextEpisodeEl.dataset.episodeStart = nextStart - shiftAmount;
- nextEpisodeEl.dataset.episodeEnd = nextEnd - shiftAmount;
- }
- }
- }
-
- // Force immediate DOM update
- this.updateEpisodeNumbers(fileListEl);
- }
-
- /**
- * Highlight episodes in the episodes container
- * @param {number} startEpisode - Start episode number
- * @param {number} endEpisode - End episode number
- * @param {HTMLElement} episodesContainer - Episodes container element
- */
- highlightEpisodes(startEpisode, endEpisode, episodesContainer) {
- if (!episodesContainer) return;
-
- const episodeItems = episodesContainer.querySelectorAll('.episode-item');
-
- episodeItems.forEach(item => {
- const episodeText = item.textContent.trim();
- const episodeMatch = episodeText.match(/E(\d+)/);
-
- if (episodeMatch) {
- const episodeNum = parseInt(episodeMatch[1]);
-
- if (episodeNum >= startEpisode && episodeNum <= endEpisode) {
- item.classList.add('highlighted-episode');
- }
- }
- });
- }
-
- /**
- * Remove highlights from all episodes
- * @param {HTMLElement} episodesContainer - Episodes container element
- */
- removeEpisodeHighlights(episodesContainer) {
- if (!episodesContainer) return;
-
- const episodeItems = episodesContainer.querySelectorAll('.episode-item');
-
- episodeItems.forEach(item => {
- item.classList.remove('highlighted-episode');
- });
- }
-
- /**
- * Get episode range from element
- * @param {HTMLElement} episodeEl - Episode element
- * @returns {Object} Episode range object
- */
- getEpisodeRange(episodeEl) {
- const episodeStart = parseInt(episodeEl.dataset.episodeStart) || 1;
- const episodeEnd = parseInt(episodeEl.dataset.episodeEnd) || episodeStart;
- return { start: episodeStart, end: episodeEnd };
- }
-
- /**
- * Calculate total episode count from file items
- * @param {HTMLElement} fileListEl - File list element
- * @returns {number} Total episode count
- */
- calculateTotalEpisodeCount(fileListEl) {
- const mediaFiles = fileListEl.querySelectorAll('.file-item:not(.folder-item)');
- let totalEpisodeCount = 0;
-
- mediaFiles.forEach(item => {
- const episodeEl = item.querySelector('.episode-number');
- if (episodeEl) {
- const { start, end } = this.getEpisodeRange(episodeEl);
- totalEpisodeCount += (end - start + 1);
- }
- });
-
- return totalEpisodeCount;
- }
-
- /**
- * Get last episode end from file items
- * @param {HTMLElement} fileListEl - File list element
- * @returns {number} Last episode end number
- */
- getLastEpisodeEnd(fileListEl) {
- const mediaFiles = fileListEl.querySelectorAll('.file-item:not(.folder-item)');
- let lastEpisodeEnd = 0;
-
- mediaFiles.forEach((item, index) => {
- const episodeEl = item.querySelector('.episode-number');
- if (episodeEl) {
- const episodeEnd = parseInt(episodeEl.dataset.episodeEnd) || 1;
- if (index === 0 || episodeEnd > lastEpisodeEnd) {
- lastEpisodeEnd = episodeEnd;
- }
- }
- });
-
- return lastEpisodeEnd;
- }
-
- /**
- * Get updating episode numbers flag
- * @returns {boolean} Updating state
- */
- getIsUpdatingEpisodeNumbers() {
- return this.isUpdatingEpisodeNumbers;
- }
-
- /**
- * Set updating episode numbers flag
- * @param {boolean} updating - Updating state
- */
- setIsUpdatingEpisodeNumbers(updating) {
- this.isUpdatingEpisodeNumbers = updating;
- }
-}
-
-module.exports = EpisodeManager; |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 | 1x -1x -1x -1x -1x -18x -18x -18x -18x -18x -18x - -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x -1x -14x -14x -14x -14x - -14x -1x -1x -1x - -13x -13x -13x - -13x -13x - -13x -13x -6x -13x - -13x -13x -16x -16x -13x -14x - -1x -1x -1x -1x -1x -1x -6x -6x - -6x -6x -6x - -6x -6x -6x - -6x -6x - -6x -6x - -6x -6x - -6x -6x - -6x - -6x -6x -6x -1x - - -1x -1x -1x -6x - -6x -6x - -1x -1x -1x -1x -1x -1x -1x -16x -16x -16x -16x -16x - -16x -16x -16x - -16x -16x -16x - -16x -16x - -16x -16x -16x -16x - -16x -16x -16x -16x -16x - -16x -16x -16x -16x - -16x - -16x -16x -16x - -16x -1x -1x -1x -1x -1x - -16x -16x -16x - -16x -16x -16x - -16x -16x -16x - -16x -16x - -16x -16x -16x -16x - -16x -16x -16x -16x - -16x -16x -16x -16x - -16x -16x -16x -16x - -16x -16x -16x -16x -16x - -16x - -16x - -16x -16x -16x -16x -16x - -16x -16x - -1x -1x -1x -1x -1x -1x - - - - - - - -1x -1x -1x -1x -1x -1x - - - - - - - - - -1x -1x -1x -1x -1x -1x - - - - - - - - - - -1x -1x -1x -1x -1x -1x - - - - -1x -1x -1x -1x -1x - - - - - - - - - - - - - - - - - - - - - - - - -1x -1x -1x -1x -1x -1x - -1x -3x -3x -3x -3x - -3x -3x - -3x -3x -3x -3x -3x -1x -1x - -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x -1x -9x -9x - -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x -1x -32x -32x - - - - - - - - - - - - - - - - -32x - -32x - - - - - - - - - - -32x -32x - -1x -1x -1x -1x -1x -1x -3x -3x -6x -6x - -6x -6x - -6x -6x - -6x -3x -3x -3x -3x -3x -3x - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x | /**
- * FileListManager - Handles file list display and manipulation
- */
-class FileListManager {
- constructor(fileListEl, onFolderClick = null) {
- this.fileListEl = fileListEl;
- this.draggedItem = null;
- this.onFolderClick = onFolderClick || null;
- this.hoveredEpisodeRange = null;
- this._setupHoverHandlers();
- }
-
- /**
- * Set callback for folder click events
- * @param {Function} callback - Function to call when folder is clicked
- */
- setFolderClickCallback(callback) {
- this.onFolderClick = callback;
- }
-
- /**
- * Display files in the UI
- * @param {Array} files - Array of file objects
- */
- displayFiles(files) {
- // Re-setup hover handlers when displaying files
- this._setupHoverHandlers();
-
- this.fileListEl.innerHTML = '';
-
- if (files.length === 0) {
- this.fileListEl.innerHTML = '<p>No media files found in this directory.</p>';
- return;
- }
-
- // 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 => {
- this._createFolderElement(file);
- });
-
- // Display media files with episode numbers and drag/drop
- mediaFiles.forEach((file, index) => {
- this._createMediaFileElement(file, episodeNumber, index);
- episodeNumber++;
- });
- }
-
- /**
- * Create folder element
- * @param {Object} file - File object
- * @private
- */
- _createFolderElement(file) {
- const fileItem = document.createElement('div');
- fileItem.className = 'file-item folder-item';
-
- const icon = document.createElement('div');
- icon.style.cssText = 'font-size: 18px; margin-right: 10px;';
- icon.textContent = '\uD83D\uDCC1';
-
- const nameEl = document.createElement('div');
- nameEl.className = 'file-name folder-name';
- nameEl.textContent = file.name;
-
- const durationEl = document.createElement('div');
- durationEl.className = 'file-duration';
-
- const qualityEl = document.createElement('div');
- qualityEl.className = 'file-quality';
-
- const fpsEl = document.createElement('div');
- fpsEl.className = 'file-fps';
-
- const tagsEl = document.createElement('div');
- tagsEl.className = 'file-tags';
-
- fileItem.append(icon, nameEl, durationEl, qualityEl, fpsEl, tagsEl);
-
- const filePath = file.path;
- const fileName = file.name;
- fileItem.addEventListener('click', (e) => {
- if (e.target !== fileItem && e.target.className !== 'file-name folder-name') {
- return;
- }
- if (this.onFolderClick) {
- this.onFolderClick(filePath, fileName);
- }
- });
-
- this.fileListEl.appendChild(fileItem);
- }
-
- /**
- * Create media file element
- * @param {Object} file - File object
- * @param {number} episodeNumber - Episode number
- * @param {number} index - File index
- */
- _createMediaFileElement(file, episodeNumber, index) {
- const fileItem = document.createElement('div');
- fileItem.className = 'file-item';
- fileItem.draggable = true;
- fileItem.dataset.index = index;
- fileItem.dataset.filePath = file.path;
-
- const duration = file.duration || '00:00';
- const quality = file.quality || 'unknown';
- const fps = file.fps || 'unknown';
-
- const dragHandle = document.createElement('div');
- dragHandle.className = 'drag-handle';
- dragHandle.textContent = '\u22EE\u22EE';
-
- const episodeContainer = document.createElement('div');
- episodeContainer.className = 'episode-number-container';
-
- const leftArrow = document.createElement('button');
- leftArrow.className = 'episode-arrow episode-arrow-left';
- leftArrow.title = 'Decrease episode range';
- leftArrow.textContent = '\u25C0';
-
- const episodeNum = document.createElement('div');
- episodeNum.className = 'episode-number';
- episodeNum.dataset.episodeStart = episodeNumber;
- episodeNum.dataset.episodeEnd = episodeNumber;
- episodeNum.textContent = episodeNumber;
-
- const rightArrow = document.createElement('button');
- rightArrow.className = 'episode-arrow episode-arrow-right';
- rightArrow.title = 'Increase episode range';
- rightArrow.textContent = '\u25B6';
-
- episodeContainer.append(leftArrow, episodeNum, rightArrow);
-
- const nameEl = document.createElement('div');
- nameEl.className = 'file-name';
- nameEl.textContent = file.name;
-
- if (file.isProblematic) {
- const warn = document.createElement('span');
- warn.className = 'problematic-label';
- warn.textContent = '\u26A0\ufe0f';
- nameEl.prepend(warn, ' ');
- }
-
- const durEl = document.createElement('div');
- durEl.className = 'file-duration';
- durEl.textContent = duration;
-
- const qualEl = document.createElement('div');
- qualEl.className = 'file-quality';
- qualEl.textContent = quality;
-
- const fpsEl = document.createElement('div');
- fpsEl.className = 'file-fps';
- fpsEl.textContent = fps;
-
- const tagsEl = document.createElement('div');
- tagsEl.className = 'file-tags';
-
- const extraTag = document.createElement('span');
- extraTag.className = 'tag-icon extra-tag';
- extraTag.title = 'Mark as Extra';
- extraTag.textContent = '\uD83C\uDFF7\uFE0F';
-
- const btsTag = document.createElement('span');
- btsTag.className = 'tag-icon behind-the-scenes-tag';
- btsTag.title = 'Add Behind the Scenes';
- btsTag.textContent = '\uD83C\uDFA5';
-
- const delTag = document.createElement('span');
- delTag.className = 'tag-icon delete-tag';
- delTag.title = 'Mark for Deletion';
- delTag.textContent = '\uD83D\uDDD1\uFE0F';
-
- const previewBtn = document.createElement('span');
- previewBtn.className = 'video-preview-btn';
- previewBtn.title = 'Preview Video';
- previewBtn.textContent = '\uD83C\uDFAC';
-
- const playBtn = document.createElement('button');
- playBtn.className = 'play-button';
- playBtn.style.cssText = 'opacity: 0.3; cursor: default; flex-shrink: 0;';
- playBtn.disabled = true;
- playBtn.textContent = '\u25B6\uFE0F';
-
- tagsEl.append(extraTag, btsTag, delTag, previewBtn, playBtn);
-
- fileItem.append(dragHandle, episodeContainer, nameEl, durEl, qualEl, fpsEl, tagsEl);
-
- fileItem.addEventListener('dragstart', this._handleDragStart.bind(this));
- fileItem.addEventListener('dragend', this._handleDragEnd.bind(this));
- fileItem.addEventListener('dragover', this._handleDragOver.bind(this));
- fileItem.addEventListener('dragleave', this._handleDragLeave.bind(this));
- fileItem.addEventListener('drop', this._handleDrop.bind(this));
-
- this.fileListEl.appendChild(fileItem);
- }
-
- /**
- * Handle drag start
- * @param {Event} e - Drag event
- * @private
- */
- _handleDragStart(e) {
- const fileItem = this;
- this.draggedItem = fileItem;
- fileItem.classList.add('dragging');
- e.dataTransfer.effectAllowed = 'move';
- e.dataTransfer.setData('text/plain', fileItem.dataset.index);
- }
-
- /**
- * Handle drag end
- * @param {Event} e - Drag event
- * @private
- */
- _handleDragEnd(e) {
- const fileItem = this;
- fileItem.classList.remove('dragging');
- // Remove drag-over class from all items
- document.querySelectorAll('.file-item').forEach(item => {
- item.classList.remove('drag-over');
- });
- this.draggedItem = null;
- }
-
- /**
- * Handle drag over
- * @param {Event} e - Drag event
- * @private
- */
- _handleDragOver(e) {
- const fileItem = this;
- e.preventDefault();
- e.dataTransfer.dropEffect = 'move';
-
- // Only show drag-over for non-folder items
- if (!fileItem.classList.contains('folder-item') && fileItem !== this.draggedItem) {
- fileItem.classList.add('drag-over');
- }
- }
-
- /**
- * Handle drag leave
- * @param {Event} e - Drag event
- * @private
- */
- _handleDragLeave(e) {
- const fileItem = this;
- fileItem.classList.remove('drag-over');
- }
-
- /**
- * Handle drop
- * @param {Event} e - Drop event
- */
- _handleDrop(e) {
- const fileItem = this;
- e.preventDefault();
- fileItem.classList.remove('drag-over');
-
- if (fileItem === this.draggedItem || fileItem.classList.contains('folder-item')) return;
-
- // Get all media file items (not folders)
- const fileItems = Array.from(this.fileListEl.querySelectorAll('.file-item:not(.folder-item)'));
- const draggedIndex = fileItems.indexOf(this.draggedItem);
- const dropIndex = fileItems.indexOf(fileItem);
-
- if (draggedIndex === -1 || dropIndex === -1) return;
-
- // Move the dragged item in the DOM
- if (draggedIndex < dropIndex) {
- fileItem.parentNode.insertBefore(this.draggedItem, fileItem.nextSibling);
- } else {
- fileItem.parentNode.insertBefore(this.draggedItem, fileItem);
- }
-
- // Update episode numbers
- this.updateEpisodeNumbers();
- }
-
- /**
- * Update episode numbers
- */
- updateEpisodeNumbers() {
- const fileItems = this.fileListEl.querySelectorAll('.file-item:not(.folder-item)');
- let episodeNum = 1;
-
- fileItems.forEach((item, index) => {
- const episodeEl = item.querySelector('.episode-number');
- if (episodeEl) {
- const storedStart = episodeEl.dataset.episodeStart;
- const storedEnd = episodeEl.dataset.episodeEnd;
-
- const start = storedStart ? parseInt(storedStart) : episodeNum;
- const end = storedEnd ? parseInt(storedEnd) : start;
-
- const rangeText = start === end ? `${start}` : `${start}-${end}`;
- episodeEl.textContent = rangeText;
- episodeEl.dataset.episode = episodeNum;
- episodeNum = end + 1;
- }
- });
- }
-
- /**
- * Clear the file list
- */
- clear() {
- this.fileListEl.innerHTML = '';
- }
-
- /**
- * Get all media file items
- * @returns {NodeList} Media file items
- */
- getMediaFileItems() {
- return this.fileListEl.querySelectorAll('.file-item:not(.folder-item)');
- }
-
- /**
- * Get folder items
- * @returns {NodeList} Folder items
- */
- getFolderItems() {
- return this.fileListEl.querySelectorAll('.folder-item');
- }
-
- /**
- * Set up hover handlers for file items
- * @private
- */
- _setupHoverHandlers() {
- // Use event delegation on the file list
- this.fileListEl.addEventListener('mouseenter', (e) => {
- const fileItem = e.target.closest('.file-item');
- if (!fileItem || fileItem.classList.contains('folder-item')) return;
-
- const episodeEl = fileItem.querySelector('.episode-number');
- if (!episodeEl) return;
-
- const startEpisode = parseInt(episodeEl.dataset.episodeStart) || 1;
- const endEpisode = parseInt(episodeEl.dataset.episodeEnd) || startEpisode;
-
- // Store hovered range for later use
- this.hoveredEpisodeRange = { start: startEpisode, end: endEpisode };
-
- // Notify UIManager to highlight episodes
- if (this.onHoverStart && typeof this.onHoverStart === 'function') {
- this.onHoverStart(startEpisode, endEpisode, fileItem);
- }
- });
-
- this.fileListEl.addEventListener('mouseleave', (e) => {
- const fileItem = e.target.closest('.file-item');
- if (!fileItem) return;
-
- // Remove from current file item
- fileItem.classList.remove('hovered-file');
-
- // Notify UIManager to remove highlights
- if (this.onHoverEnd && typeof this.onHoverEnd === 'function') {
- this.onHoverEnd(fileItem);
- }
- });
- }
-
- /**
- * Highlight files matching episode range
- * @param {number} startEpisode - Start episode number
- * @param {number} endEpisode - End episode number
- */
- highlightFilesMatchingEpisode(startEpisode, endEpisode) {
- const mediaFiles = this.getMediaFileItems();
- mediaFiles.forEach(fileItem => {
- const episodeEl = fileItem.querySelector('.episode-number');
- if (!episodeEl) return;
-
- const fileStart = parseInt(episodeEl.dataset.episodeStart) || 1;
- const fileEnd = parseInt(episodeEl.dataset.episodeEnd) || fileStart;
-
- // Check if the file's episode range overlaps with the hovered range
- const overlaps = !(endEpisode < fileStart || startEpisode > fileEnd);
-
- if (overlaps) {
- fileItem.classList.add('hovered-file');
- } else {
- fileItem.classList.remove('hovered-file');
- }
- });
- }
-
- /**
- * Remove file highlights
- */
- removeFileHighlights() {
- const mediaFiles = this.getMediaFileItems();
- mediaFiles.forEach(fileItem => {
- fileItem.classList.remove('hovered-file');
- });
- }
-}
-
-module.exports = FileListManager; |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 | 1x -1x -1x -1x -1x -9x -9x -9x -9x - -1x -1x -1x -1x -1x -2x -2x -2x -2x -2x -2x - -1x -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x -1x -1x -1x -2x -2x -2x -2x -2x -2x - -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x -1x -1x -2x -2x -2x -2x -2x - -1x -1x -1x -1x -1x -1x -1x -1x - -1x | /**
- * ProgressManager - Manages progress display and feedback
- */
-class ProgressManager {
- constructor(progressContainer, progressText, progressCount) {
- this.progressContainer = progressContainer;
- this.progressText = progressText;
- this.progressCount = progressCount;
- }
-
- /**
- * Show progress indicator
- * @param {string} message - Progress message
- */
- showProgress(message = 'Scanning directory...') {
- if (this.progressContainer) {
- this.progressContainer.style.display = 'block';
- this.progressText.textContent = message;
- this.progressCount.textContent = '';
- }
- }
-
- /**
- * Hide progress indicator
- */
- hideProgress() {
- if (this.progressContainer) {
- this.progressContainer.style.display = 'none';
- }
- }
-
- /**
- * Update progress with current/total counts
- * @param {number} current - Current count
- * @param {number} total - Total count
- * @param {string} fileName - Current file name
- */
- updateProgress(current, total, fileName) {
- if (this.progressContainer) {
- const displayCurrent = current + 1;
- this.progressText.textContent = `Processing: ${fileName}`;
- this.progressCount.textContent = `${displayCurrent} of ${total} files`;
- }
- }
-
- /**
- * Update progress with custom text
- * @param {string} text - Progress text
- */
- updateProgressText(text) {
- if (this.progressText) {
- this.progressText.textContent = text;
- }
- }
-
- /**
- * Update progress count display
- * @param {number} current - Current count
- * @param {number} total - Total count
- */
- updateProgressCount(current, total) {
- if (this.progressCount) {
- const displayCurrent = current + 1;
- this.progressCount.textContent = `${displayCurrent} of ${total} files`;
- }
- }
-
- /**
- * Show directory feedback
- * @param {string} folderName - Folder name
- */
- showDirectoryFeedback(folderName) {
- console.log(`Directory "${folderName}" created and file moved`);
- }
-}
-
-module.exports = ProgressManager; |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| AppState.js | -
-
- |
- 98.9% | -180/182 | -100% | -24/24 | -95.45% | -21/22 | -98.9% | -180/182 | -
| EpisodeManager.js | -
-
- |
- 81.27% | -204/251 | -85.1% | -40/47 | -84.61% | -11/13 | -81.27% | -204/251 | -
| FileListManager.js | -
-
- |
- 80.88% | -275/340 | -78.37% | -29/37 | -70.58% | -12/17 | -80.88% | -275/340 | -
| ProgressManager.js | -
-
- |
- 100% | -70/70 | -100% | -7/7 | -100% | -7/7 | -100% | -70/70 | -