Showing 8 changed files with 949 additions and 177 deletions
BIN
.DS_Store
Binary file not shown.
+1 -1
.gitignore
@@ -1,3 +1,3 @@
1 1
 # Exclude samples directory from version control
2
-samples/
2
+sample/
3 3
 tests/
+0 -17
CHANGELOG.md
@@ -1,18 +0,0 @@
1
-# Changelog
2
-
3
-## [Unreleased]
4
-### Added
5
-- `--collect-unsortable` argument: Files with no EXIF or media date can be moved to an `unsortable` folder in the destination.
6
-
7
-### Changed
8
-- The script now **requires** EXIF or media date for processing files. If no date is found, files are skipped and left in place by default.
9
-- No fallback to filesystem modification date or other heuristics; only EXIF or media date is used for sorting and renaming.
10
-
11
-### Fixed
12
-- Improved argument parsing to support new options.
13
-- Improved warnings and debug output handling.
14
-
15
-
16
-## [Earlier versions]
17
-- See previous commits for details.
+280 -0
DEVELOPMENT.md
@@ -0,0 +1,280 @@
1
+# Development Guide
2
+
3
+## 1. Objectives
4
+
5
+The Media Importer project aims to provide a robust, efficient solution for organizing media files by date with proper timezone handling and conflict - **Naming Convention**: `{s/f}_{YYYYMMDD_HHMMSS}_{TestName}.md` format where:
6
+  - `s` = success, `f` = failure
7
+  - Followed by timestamp and test name (spaces converted to underscores)esolution.
8
+
9
+Key objectives:
10
+- Reliable EXIF/metadata extraction and date parsing
11
+- Proper UTC time conversion for QuickTime/Apple media files
12
+- Flexible organization patterns (year/month/day/hour)
13
+- Safe file operations with dry-run capabilities
14
+- Cross-platform compatibility (macOS/Linux)
15
+
16
+## 2. Guide
17
+
18
+### Development Workflow
19
+
20
+When making changes to the project, follow this structured approach:
21
+
22
+### Changelog Entries Format
23
+
24
+All changelog entries should follow this format:
25
+
26
+```
27
+- Date time
28
+- Bug/Feature description  
29
+- Changes made
30
+```
31
+
32
+Example:
33
+```
34
+- 2025-09-07 10:30
35
+- Fixed data loss issue when processing already-sorted folders
36
+- Added exclusion patterns for sorted/organized/processed folders
37
+- Added --include-sorted flag to override exclusions when needed
38
+```
39
+
40
+### File Move Confirmation
41
+
42
+Every file move operation should be confirmed:
43
+- After moving a file, the script must check that the file exists at the destination.
44
+- If the file is not present at the destination after the move operation, the script should immediately stop and report an error.
45
+- This ensures data integrity and prevents silent data loss.
46
+
47
+### Destination Inside Source Handling
48
+
49
+- Given the script's purpose, the destination folder may be inside the source folder.
50
+- In this case, all files within the destination folder must be excluded from scanning and processing.
51
+- For extra safety: before processing any file, if its path matches (or is inside) the destination path, the script must report an error and stop immediately.
52
+- This prevents accidental re-processing or moving of files that have already been sorted, ensuring data integrity.
53
+
54
+### Testing
55
+
56
+Testing is essential to ensure the script's reliability and data safety. The following methodology should be used:
57
+
58
+#### Test Environment Setup
59
+- The `samples` directory contains a variety of media files for testing.
60
+- Create a dedicated working directory named `test` for each test run.
61
+- Copy selected files from `samples` into the `test` directory to simulate real-world scenarios.
62
+- Perform import operations using the script, targeting the `test` directory as the source and a subdirectory (e.g., `test/sorted`) as the destination.
63
+
64
+#### Test Execution and Documentation
65
+- Before and after each import operation, run `find` on both the source and destination directories to capture the file structure:
66
+  - Example: `find ./test > test/source_before.txt`
67
+  - Example: `find ./test/sorted > test/dest_before.txt`
68
+- Log all results, including script output and directory listings, into a dedicated log file for each test.
69
+
70
+#### Test Report Format
71
+Each test must generate a comprehensive Markdown report in `test/test_report.md` with the following structure:
72
+
73
+```markdown
74
+# Test Report: [Test Name/Scenario]
75
+
76
+## Test Information
77
+- **Date**: $(date)
78
+- **Scenario**: [Brief description of what is being tested]
79
+- **Objective**: [What specific functionality/behavior is being verified]
80
+- **Files Used**: [List of test files and their characteristics]
81
+
82
+## Pre-Test State
83
+### Source Directory Structure
84
+\`\`\`
85
+[Contents of source_before.txt]
86
+\`\`\`
87
+
88
+### Destination Directory Structure
89
+\`\`\`
90
+[Contents of dest_before.txt]
91
+\`\`\`
92
+
93
+## Test Execution
94
+### Command Used
95
+\`\`\`bash
96
+[Exact command executed]
97
+\`\`\`
98
+
99
+### Script Output
100
+\`\`\`
101
+[Full script output from import_log.txt]
102
+\`\`\`
103
+
104
+## Post-Test State
105
+### Source Directory Structure
106
+\`\`\`
107
+[Contents of source_after.txt]
108
+\`\`\`
109
+
110
+### Destination Directory Structure
111
+\`\`\`
112
+[Contents of dest_after.txt]
113
+\`\`\`
114
+
115
+## Analysis and Verification
116
+### Expected Results
117
+- [List what should happen]
118
+
119
+### Actual Results
120
+- [List what actually happened]
121
+
122
+### Issues Found
123
+- [Any problems, errors, or unexpected behavior]
124
+- [Include error messages, incorrect file placements, etc.]
125
+
126
+### Protections Verified
127
+- [ ] Destination exclusion working
128
+- [ ] Move confirmation functional
129
+- [ ] No data loss detected
130
+- [ ] UTC conversion correct (for QuickTime files)
131
+- [ ] Unimportable files handling (if applicable)
132
+
133
+## Corrective Actions
134
+### Issues Identified
135
+- [Detailed description of problems found]
136
+
137
+### Fixes Applied
138
+- [Code changes made]
139
+- [Configuration adjustments]
140
+- [Process improvements]
141
+
142
+### Re-test Results
143
+- [Results after applying fixes]
144
+
145
+## Conclusion
146
+### Test Result
147
+- [ ] PASSED
148
+- [ ] FAILED
149
+- [ ] PARTIAL (with notes)
150
+
151
+### Notes
152
+[Any additional observations, recommendations, or follow-up actions needed]
153
+
154
+### Files Generated
155
+- `test/source_before.txt` - Pre-test source structure
156
+- `test/dest_before.txt` - Pre-test destination structure
157
+- `test/source_after.txt` - Post-test source structure
158
+- `test/dest_after.txt` - Post-test destination structure
159
+- `test/import_log.txt` - Full script execution log
160
+- `test/test_report.md` - This report
161
+```
162
+
163
+#### Automated Test Runner
164
+A comprehensive test runner script (`test_runner.sh`) is available to automate the testing process:
165
+
166
+```bash
167
+./test_runner.sh
168
+```
169
+
170
+The script provides:
171
+- **Pre-configured test scenarios** for common use cases
172
+- **Automatic report generation** in Markdown format
173
+- **State capture** before and after test execution
174
+- **Protection verification** with checkboxes
175
+- **Custom test support** for specific scenarios
176
+
177
+#### Test Categories
178
+The test runner provides the following pre-configured test scenarios:
179
+
180
+1. **Basic Functionality Test**: Tests processing of files with valid EXIF data to verify correct sorting and organization
181
+2. **Unimportable Files Test**: Tests handling of files without EXIF data in both root and subfolders, without --collect-unimportable flag
182
+3. **Mixed Content Test**: Tests processing of sortable and unimportable files in separate folders to verify cleanup behavior
183
+4. **Safety Protections Test**: Tests destination exclusion and move confirmation mechanisms to prevent data loss
184
+5. **UTC Conversion Test**: Tests UTC timestamp conversion for QuickTime/Apple EXIF data
185
+6. **Subdirectory Processing Test**: Tests processing of files in nested subdirectories to ensure recursive file discovery
186
+7. **Custom Test**: Allows user-defined test scenarios with custom file sets and commands
187
+
188
+#### Test Result Persistence
189
+The test runner includes automatic result persistence:
190
+
191
+- **Archival Location**: Test results are saved as individual Markdown files in `test_reports/` directory
192
+- **Naming Convention**: `{TestName}_{YYYYMMDD_HHMMSS}.md` format for easy identification
193
+- **Contents Preserved**: Single self-contained Markdown file with:
194
+  - Complete test information and directory structures
195
+  - Full script execution output embedded inline (ANSI codes stripped for readability)
196
+  - Import log content included directly in the report
197
+- **Excluded Files**: No separate files - everything is consolidated in the Markdown report
198
+- **Historical Tracking**: Maintains complete test history for debugging and regression testing
199
+
200
+#### Cleanup
201
+- Review the test report and verify all aspects are documented
202
+- Clean up the `test` directory after each test run to ensure a fresh environment for subsequent tests
203
+- Archive important test reports in a `test_reports/` directory for future reference
204
+
205
+## 3. Changelog
206
+
207
+### 2025-09-07 21:15 - Test 2 and 3 Enhancements
208
+- Updated Test 2 (Unimportable Files Test) to include files in both root and subfolder
209
+- Removed --collect-unimportable flag from Test 2 to test default behavior
210
+- Updated Test 3 (Mixed Content Test) to use separate folders for sortable vs unimportable files
211
+- Test 3 now verifies that folders with only sortable files are cleaned up while folders with unimportable files are preserved
212
+- Updated menu descriptions to reflect the changes
213
+- Tests now verify proper handling of unimportable files without collection flag
214
+
215
+---
216
+
217
+### 2025-09-07 21:25 - Documentation Enhancement
218
+- Added comprehensive documentation for --collect-unimportable flag in README.md
219
+- Added Example 4 showing how to use --collect-unimportable flag
220
+- Updated Features section to mention unimportable files handling
221
+- Updated Configuration section to explain default behavior for unimportable files
222
+- Added usage example for --collect-unimportable in Basic Usage section
223
+
224
+---
225
+
226
+### 2025-09-07 21:30 - Git Ignore Enhancement
227
+- Added test_reports/ to .gitignore to exclude generated test reports from version control
228
+- Test reports are generated files that don't need to be tracked in Git
229
+- Prevents large numbers of timestamped report files from cluttering the repository
230
+- Added sample/ to .gitignore to exclude test media files from version control
231
+
232
+---
233
+
234
+### 2025-09-07 20:40 - Source Only Test Addition
235
+- Added Test 8: Source Only Test to test runner
236
+- Tests processing with only source parameter (creates sorted subdirectory automatically)
237
+- Verifies that when no destination is specified, files are sorted into source/sorted/
238
+- Updated menu and command line options for new test
239
+
240
+---
241
+
242
+### 2025-09-07 20:45 - Test 7 Refinement
243
+- Updated Test 7 to test --keep-empty-dirs functionality instead of cleanup
244
+- Since cleanup is now default behavior, Test 7 now verifies empty directory preservation
245
+- Renamed from "Cleanup Empty Directories Test" to "Keep Empty Directories Test"
246
+- Updated test scenario to validate --keep-empty-dirs flag behavior
247
+- Added command line option "keep-empty-dirs" for test 7
248
+
249
+---
250
+
251
+### 2025-09-07 19:30 - Test Runner Directory Separation
252
+- Adapted test runner to use separate source and destination directories
253
+- Changed from test/ as source to test/source/ and test/destination/
254
+- Updated all test functions to use proper directory separation
255
+- Improved test isolation and clarity
256
+
257
+---
258
+
259
+### 2025-09-07 19:00 - Default Cleanup Behavior
260
+- Made --cleanup-empty-dirs the default behavior (implicit option)
261
+- Added --keep-empty-dirs flag to disable cleanup if needed
262
+- Updated help text and configuration display to reflect new default
263
+- Cleanup now runs automatically unless explicitly disabled
264
+
265
+---
266
+
267
+### 2025-09-07 18:56 - Cleanup Empty Directories Feature
268
+- Added --cleanup-empty-dirs option to remove empty directories from source after processing
269
+- Added cleanup_empty_directories() function with safe empty directory detection
270
+- Updated final report to show cleanup status
271
+- Maintains safety by not removing source root directories
272
+- Works correctly with dry-run mode
273
+
274
+## 4. Todo
275
+
276
+Key areas for future development:
277
+- GPS metadata integration for timezone detection
278
+- Enhanced duplicate detection
279
+- Performance optimizations for large file sets
280
+- Additional organization patterns
+0 -139
TEST_PLAN.md
@@ -1,140 +0,0 @@
1
-# Media Importer Test Plan
2
-
3
-## Purpose
4
-This test plan ensures the reliability and correctness of `media-importer.sh` by covering key scenarios and edge cases.
5
-
6
-## Test Areas
7
-1. **Basic Functionality**
8
-   - Organize media files by date using all supported organization patterns (`y`, `m`, `d`, `h`).
9
-   - Move and copy modes (with and without `--keep-originals`).
10
-   - Dry run mode (`--dry-run`).
11
-
12
-2. **Source and Destination Handling**
13
-   - Single and multiple source patterns (folders, globs, files).
14
-   - Destination folder creation and error handling.
15
-   - Exclusion of destination from source search.
16
-   - Symlinked source directories.
17
-
18
-3. **Error Handling**
19
-   - Missing or invalid organization pattern.
20
-   - Missing required dependencies (e.g., exiftool).
21
-   - Unwritable destination directory.
22
-   - Files with no EXIF/media date metadata (should be skipped or collected as unsortable).
23
-   - Fatal errors (e.g., cannot generate destination path).
24
-   - Recoverable errors (e.g., failed copy/move).
25
-
26
-4. **Performance and Edge Cases**
27
-   - Large number of files (10k+).
28
-   - Files with conflicting names (should be renamed).
29
-   - Mixed media types and extensions.
30
-   - Files with unusual or missing extensions.
31
-
32
-5. **Reporting**
33
-   - Correct summary of processed, skipped, and error files.
34
-   - Accurate size and speed reporting.
35
-   - Verbose and non-verbose output modes.
36
-
37
-
38
-
39
-## Sample Data Note
40
-The `samples/media` directory contains files both with and without EXIF date metadata. Any test that does not correctly detect and handle files lacking EXIF dates (i.e., skips or collects them as unsortable) is considered failed.
41
-
42
-## Test Directory Usage
43
-To facilitate testing, the `tests` directory will be used as the working directory for all test scenarios. Before each test, relevant files will be copied from `samples/media` into `tests`. After inspecting the results, the `tests` directory will be cleaned to ensure a fresh state for the next test.
44
-
45
-## Specific Test Cases
46
-
47
-### TC01: Basic Organization Patterns
48
-- **Objective**: Test all organization patterns (y, m, d, h)
49
-- **Setup**: Copy sample files from `samples/media` to `tests/media`
50
-- **Commands**: 
51
-  - `./media-importer.sh -s ./tests/media -d ./tests/sorted_y -o y`
52
-  - `./media-importer.sh -s ./tests/media -d ./tests/sorted_m -o m`
53
-  - `./media-importer.sh -s ./tests/media -d ./tests/sorted_d -o d`
54
-  - `./media-importer.sh -s ./tests/media -d ./tests/sorted_h -o h`
55
-- **Expected**: Files organized according to each pattern
56
-- **Cleanup**: `rm -rf ./tests/*`
57
-
58
-### TC02: Copy vs Move Mode
59
-- **Objective**: Test --keep-originals flag
60
-- **Setup**: Copy sample files to `tests/media`
61
-- **Commands**:
62
-  - `./media-importer.sh -s ./tests/media -d ./tests/copied -k`
63
-  - `./media-importer.sh -s ./tests/media -d ./tests/moved`
64
-- **Expected**: First command copies files, second moves them
65
-- **Cleanup**: `rm -rf ./tests/*`
66
-
67
-### TC03: Dry Run Mode
68
-- **Objective**: Test --dry-run functionality
69
-- **Setup**: Copy sample files to `tests/media`
70
-- **Command**: `./media-importer.sh -s ./tests/media -d ./tests/dry --dry-run`
71
-- **Expected**: Shows what would be done without moving files
72
-- **Cleanup**: `rm -rf ./tests/*`
73
-
74
-### TC04: Symlinked Source
75
-- **Objective**: Test symlinked source directory
76
-- **Setup**: 
77
-  - `mkdir -p tests/media`
78
-  - `ln -sf ../samples/media tests/linked_media`
79
-- **Command**: `./media-importer.sh -s ./tests/linked_media -d ./tests/from_symlink`
80
-- **Expected**: Files from symlinked directory are processed
81
-- **Cleanup**: `rm -rf ./tests/*`
82
-
83
-### TC05: Error Handling - Invalid Pattern
84
-- **Objective**: Test invalid organization pattern
85
-- **Command**: `./media-importer.sh -o z`
86
-- **Expected**: Error message and exit code 1
87
-- **Cleanup**: None needed
88
-
89
-### TC06: Same Source and Destination (Fatal Error)
90
-- **Objective**: Test fatal error handling
91
-- **Setup**: Copy sample files to `tests/media`
92
-- **Command**: `./media-importer.sh -s ./tests -d ./tests`
93
-- **Expected**: Fatal error stops processing immediately
94
-- **Cleanup**: `rm -rf ./tests/*`
95
-
96
-## New Feature: --full-date Argument & Default Flat Naming
97
-
98
-### TC07: --full-date Argument (EXIF Date Required)
99
-- **Objective**: Verify that only files with EXIF/media dates are processed and named with full date format.
100
-- **Setup**: Copy sample files to `tests/media`
101
-- **Command**: `./media-importer.sh --full-date -s ./tests/media -d ./tests/flat`
102
-- **Expected**: Only files with EXIF dates in `tests/flat` named as `yyyy-mm-dd_hh-mm-ss.ext` (no subfolders). Files without EXIF dates remain in source.
103
-- **Cleanup**: `rm -rf ./tests/*`
104
-
105
-### TC08: No Organization Directive (EXIF Date Required)
106
-- **Objective**: Verify that omitting the organization directive results in flat full-date naming for files with EXIF dates only.
107
-- **Setup**: Copy sample files to `tests/media`
108
-- **Command**: `./media-importer.sh -s ./tests/media -d ./tests/flat_default`
109
-- **Expected**: Only files with EXIF dates in `tests/flat_default` named as `yyyy-mm-dd_hh-mm-ss.ext` (no subfolders). Files without EXIF dates remain in source.
110
-- **Cleanup**: `rm -rf ./tests/*`
111
-
112
-### TC09: Organization Directive with --full-date (EXIF Date Required)
113
-- **Objective**: Verify that --full-date overrides any organization directive and processes only files with EXIF dates.
114
-- **Setup**: Copy sample files to `tests/media`
115
-- **Command**: `./media-importer.sh --full-date -o y -s ./tests/media -d ./tests/flat_override`
116
-- **Expected**: Only files with EXIF dates in `tests/flat_override` named as `yyyy-mm-dd_hh-mm-ss.ext` (no subfolders). Files without EXIF dates remain in source.
117
-- **Cleanup**: `rm -rf ./tests/*`
118
-
119
-### TC10: --collect-unsortable Flag
120
-- **Objective**: Verify that files without EXIF dates are moved to unsortable folder when flag is used.
121
-- **Setup**: Copy sample files to `tests/media`
122
-- **Command**: `./media-importer.sh --full-date --collect-unsortable -s ./tests/media -d ./tests/flat`
123
-- **Expected**: Files with EXIF dates in `tests/flat` named as `yyyy-mm-dd_hh-mm-ss.ext`. Files without EXIF dates in `tests/flat/unsortable/`.
124
-- **Cleanup**: `rm -rf ./tests/*`
125
-
126
-### TC11: Basic Organization with EXIF Requirement
127
-- **Objective**: Test that organization patterns only process files with EXIF dates.
128
-- **Setup**: Copy sample files to `tests/media`
129
-- **Command**: `./media-importer.sh -o y -s ./tests/media -d ./tests/sorted_y`
130
-- **Expected**: Only files with EXIF dates organized by year. Files without EXIF dates remain in source.
131
-- **Cleanup**: `rm -rf ./tests/*`
132
-
133
-## Test Execution Framework
134
-- Before each test: Prepare test data from `samples/media`
135
-- Run test command and capture output
136
-- Verify expected behavior and results
137
-- After each test: Clean `tests` directory with `rm -rf ./tests/*`
138
-
139
-This plan should be updated as new features or bug fixes are added.
+0 -15
TODO.md
@@ -1,16 +0,0 @@
1
-# TODO
2
-
3
-## Tasks
4
-
5
-1. **QuickTime Local Time from GPS Metadata**
6
-   - When processing QuickTime files, use GPS metadata (if available) to determine the local time zone and convert the UTC timestamp to local time accordingly.
7
-   - Fallback to system timezone if GPS data is not present.
8
-
9
-2. **Implicit Behavior Selection**
10
-   - Define and document the script's default behavior when no explicit directives/arguments are provided.
11
-   - Allow users to override defaults via command-line arguments.
12
-   - Ensure stability and predictability in all cases.
13
-
14
-
15
-(Feel free to expand with subtasks or implementation notes.)
+20 -5
media-importer.sh
@@ -18,6 +18,7 @@ DESTINATION=""
18 18
 KEEP_ORIGINALS=0
19 19
 DRY_RUN=0
20 20
 VERBOSE=0
21
+CLEANUP_EMPTY_DIRS=1
21 22
 
22 23
 # Counters and statistics
23 24
 TOTAL_FILES=0
@@ -94,9 +95,10 @@ Options:
94 95
     -F, --filename-mode MODE     auto|full|orig    (default: full)
95 96
     -s, --source PATH            File or directory to process (repeatable). Default: cwd
96 97
     -d, --destination PATH       Destination folder. Required when multiple -s are given.
97
-        -k, --keep-originals         Copy files instead of moving
98
+    -k, --keep-originals         Copy files instead of moving
98 99
     --collect-unsortable         Put files without dates into DEST/unsortable
99
-    --dry-run                   Show actions without changing files
100
+    --keep-empty-dirs            Keep empty directories after processing
101
+    --dry-run                    Show actions without changing files
100 102
     -v, --verbose                Verbose output
101 103
     -h, --help                   Show this help
102 104
     --version                    Show version
@@ -420,7 +422,7 @@ generate_destination_path() {
420 422
                 dir_path="$base_destination/${year}-${month}"
421 423
                 filename="${day}_${hour}-${minute}-${second}.${lowercase_ext}"
422 424
                 ;;
423
-            "ymd"|"ymd-")
425
+            "ymd")
424 426
                 # Single folder per day named yyyy-mm-dd; filename is time
425 427
                 dir_path="$base_destination/${year}-${month}-${day}"
426 428
                 filename="${hour}-${minute}-${second}.${lowercase_ext}"
@@ -666,8 +668,8 @@ while [[ $# -gt 0 ]]; do
666 668
     case $1 in
667 669
         -o|--organization)
668 670
             ORGANIZATION="$2"
669
-            # Accept new patterns: ym, ymd and ymd- as well as single-letter ones
670
-            if [[ ! "$ORGANIZATION" =~ ^(y|m|d|h|ym|ymd|ymd-)$ ]]; then
671
+            # Accept new patterns: ym, ymd as well as single-letter ones
672
+            if [[ ! "$ORGANIZATION" =~ ^(y|m|d|h|ym|ymd)$ ]]; then
671 673
                 print_color "$RED" "Error: Invalid organization pattern. Must be one of: y, m, d, h, ym, ymd"
672 674
                 exit 1
673 675
             fi
@@ -687,6 +689,10 @@ while [[ $# -gt 0 ]]; do
687 689
             COLLECT_UNSORTABLE=1
688 690
             shift
689 691
             ;;
692
+        --keep-empty-dirs)
693
+            CLEANUP_EMPTY_DIRS=0
694
+            shift
695
+            ;;
690 696
         -s|--source)
691 697
             SOURCE_PATTERNS+=("$2")
692 698
             shift 2
@@ -782,6 +788,7 @@ echo "  Organization pattern: $ORGANIZATION"
782 788
 echo "  Destination:         $DESTINATION"
783 789
 echo "  Keep originals:      $([ $KEEP_ORIGINALS -eq 1 ] && echo "Yes" || echo "No")"
784 790
 echo "  Dry run:             $([ $DRY_RUN -eq 1 ] && echo "Yes" || echo "No")"
791
+echo "  Keep empty dirs:     $([ $CLEANUP_EMPTY_DIRS -eq 0 ] && echo "Yes" || echo "No")"
785 792
 echo "  Verbose:             $([ $VERBOSE -eq 1 ] && echo "Yes" || echo "No")"
786 793
 
787 794
 if [[ ${#SOURCE_PATTERNS[@]} -gt 0 ]]; then
@@ -836,6 +843,14 @@ for file in "${files[@]}"; do
836 843
     fi
837 844
 done
838 845
 
846
+# Clean up empty directories if requested (default behavior)
847
+if [[ $CLEANUP_EMPTY_DIRS -eq 1 && $DRY_RUN -eq 0 ]]; then
848
+    print_color "$BLUE" "Cleaning up empty directories..."
849
+    # Find and remove empty directories under destination, but don't remove the destination itself
850
+    find "$DESTINATION" -type d -empty -not -path "$DESTINATION" -delete 2>/dev/null || true
851
+    print_color "$GREEN" "Empty directory cleanup completed"
852
+fi
853
+
839 854
 # Show final report
840 855
 show_report
841 856
 
+648 -0
test_runner.sh
@@ -0,0 +1,648 @@
1
+#!/bin/bash
2
+
3
+# Media Importer Test Runner
4
+# Comprehensive testing framework based on Development.md specifications
5
+
6
+set -e
7
+
8
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+TEST_DIR="$SCRIPT_DIR/test"
10
+SOURCE_DIR="$TEST_DIR/source"
11
+DEST_DIR="$TEST_DIR/destination"
12
+SAMPLES_DIR="$SCRIPT_DIR/sample"  # Fixed: was 'samples' but should be 'sample'
13
+MEDIA_IMPORTER="$SCRIPT_DIR/media-importer.sh"
14
+TEST_REPORTS_DIR="$SCRIPT_DIR/test_reports"
15
+
16
+# Colors for output
17
+RED='\033[0;31m'
18
+GREEN='\033[0;32m'
19
+YELLOW='\033[1;33m'
20
+BLUE='\033[0;34m'
21
+NC='\033[0m' # No Color
22
+
23
+# Function to print colored output
24
+print_color() {
25
+    local color="$1"
26
+    local message="$2"
27
+    echo -e "${color}${message}${NC}"
28
+}
29
+
30
+# Function to create test directories
31
+create_test_dirs() {
32
+    print_color "$BLUE" "Creating test directories..."
33
+    mkdir -p "$SOURCE_DIR"
34
+    mkdir -p "$DEST_DIR"
35
+    mkdir -p "$TEST_REPORTS_DIR"
36
+}
37
+
38
+# Function to clean test directory
39
+clean_test_dir() {
40
+    print_color "$BLUE" "Cleaning test directory..."
41
+    rm -rf "$TEST_DIR"/*
42
+}
43
+
44
+# Function to copy sample files
45
+copy_sample_files() {
46
+    local source_pattern="$1"
47
+    local target_dir="$2"
48
+
49
+    if [[ -d "$SAMPLES_DIR/$source_pattern" ]]; then
50
+        # Copy all files recursively from source to target directory
51
+        find "$SAMPLES_DIR/$source_pattern" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.tiff" -o -iname "*.tif" -o -iname "*.cr2" -o -iname "*.nef" -o -iname "*.arw" -o -iname "*.dng" -o -iname "*.raw" -o -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mts" -o -iname "*.m2ts" -o -iname "*.mkv" -o -iname "*.wmv" -o -iname "*.3gp" -o -iname "*.m4v" \) -exec cp {} "$target_dir/" \; 2>/dev/null || true
52
+        print_color "$GREEN" "Copied $source_pattern files to $target_dir"
53
+    else
54
+        print_color "$YELLOW" "Warning: $SAMPLES_DIR/$source_pattern not found. Path: $SAMPLES_DIR/$source_pattern"
55
+    fi
56
+}
57
+
58
+# Function to capture directory state
59
+capture_state() {
60
+    local dir_path="$1"
61
+    local output_file="$2"
62
+    local label="$3"
63
+
64
+    print_color "$BLUE" "Capturing $label directory state..."
65
+    if [[ -d "$dir_path" ]]; then
66
+        find "$dir_path" -type f | sort > "$output_file"
67
+    else
68
+        echo "# Directory does not exist or is empty" > "$output_file"
69
+    fi
70
+}
71
+
72
+# Function to run test command and capture output
73
+run_test_command() {
74
+    local command="$1"
75
+    local log_file="$2"
76
+
77
+    print_color "$YELLOW" "Executing: $command"
78
+    echo "$command" > "$log_file"
79
+    echo "" >> "$log_file"
80
+    echo "=== SCRIPT OUTPUT ===" >> "$log_file"
81
+
82
+    # Run command and capture both stdout and stderr
83
+    if eval "$command" >> "$log_file" 2>&1; then
84
+        echo "" >> "$log_file"
85
+        echo "=== COMMAND COMPLETED SUCCESSFULLY ===" >> "$log_file"
86
+        return 0
87
+    else
88
+        echo "" >> "$log_file"
89
+        echo "=== COMMAND FAILED ===" >> "$log_file"
90
+        return 1
91
+    fi
92
+}
93
+
94
+# Function to generate test report
95
+generate_test_report() {
96
+    local test_name="$1"
97
+    local scenario="$2"
98
+    local objective="$3"
99
+    local files_used="$4"
100
+    local command="$5"
101
+    local result_status="$6"
102
+
103
+    # Determine success/failure prefix
104
+    local status_prefix
105
+    if [[ $result_status -eq 0 ]]; then
106
+        status_prefix="s"
107
+    else
108
+        status_prefix="f"
109
+    fi
110
+
111
+    # Format date as YYYYMMDD
112
+    local date_stamp=$(date '+%Y%m%d')
113
+
114
+    # Create filename with new format: [s|f]_date_restul_numelei
115
+    local report_file="$TEST_REPORTS_DIR/${status_prefix}_${date_stamp}_${test_name}.md"
116
+
117
+    print_color "$BLUE" "Generating test report: $report_file"
118
+
119
+    cat > "$report_file" << EOF
120
+# Test Report: $test_name
121
+
122
+## Test Information
123
+- **Date**: $(date)
124
+- **Scenario**: $scenario
125
+- **Objective**: $objective
126
+- **Files Used**: $files_used
127
+
128
+## Pre-Test State
129
+### Source Directory Structure
130
+\`\`\`
131
+$(cat "$TEST_DIR/source_before.txt" 2>/dev/null || echo "# No pre-test source state captured")
132
+\`\`\`
133
+
134
+### Destination Directory Structure
135
+\`\`\`
136
+$(cat "$TEST_DIR/dest_before.txt" 2>/dev/null || echo "# No pre-test destination state captured")
137
+\`\`\`
138
+
139
+## Test Execution
140
+### Command Used
141
+\`\`\`bash
142
+$command
143
+\`\`\`
144
+
145
+### Script Output
146
+\`\`\`
147
+$(cat "$TEST_DIR/import_log.txt" 2>/dev/null || echo "# No script output captured")
148
+\`\`\`
149
+
150
+## Post-Test State
151
+### Source Directory Structure
152
+\`\`\`
153
+$(cat "$TEST_DIR/source_after.txt" 2>/dev/null || echo "# No post-test source state captured")
154
+\`\`\`
155
+
156
+### Destination Directory Structure
157
+\`\`\`
158
+$(cat "$TEST_DIR/dest_after.txt" 2>/dev/null || echo "# No post-test destination state captured")
159
+\`\`\`
160
+
161
+## Analysis and Verification
162
+### Expected Results
163
+- Files should be processed according to the test scenario
164
+- No data loss should occur
165
+- Proper error handling for edge cases
166
+
167
+### Actual Results
168
+- Test execution completed with status: $result_status
169
+
170
+### Issues Found
171
+- [ ] No issues detected
172
+- [ ] Issues found (see notes below)
173
+
174
+### Protections Verified
175
+- [ ] Destination exclusion working
176
+- [ ] Move confirmation functional
177
+- [ ] No data loss detected
178
+- [ ] UTC conversion correct (for QuickTime files)
179
+- [ ] Unimportable files handling (if applicable)
180
+
181
+## Conclusion
182
+### Test Result
183
+- [ ] PASSED
184
+- [ ] FAILED
185
+- [ ] PARTIAL (with notes)
186
+
187
+### Notes
188
+Test completed as per Development.md specifications.
189
+
190
+### Files Generated
191
+- \`test/source_before.txt\` - Pre-test source structure
192
+- \`test/dest_before.txt\` - Pre-test destination structure
193
+- \`test/source_after.txt\` - Post-test source structure
194
+- \`test/dest_after.txt\` - Post-test destination structure
195
+- \`test/import_log.txt\` - Full script execution log
196
+- \`test/test_report.md\` - This report
197
+EOF
198
+
199
+    print_color "$GREEN" "Test report generated: $report_file"
200
+
201
+    # Clean up old reports, keeping only the last 10
202
+    cleanup_old_reports
203
+}
204
+
205
+# Function to clean up old test reports, keeping only the last 10
206
+cleanup_old_reports() {
207
+    if [[ -d "$TEST_REPORTS_DIR" ]]; then
208
+        # Count total reports
209
+        local total_reports=$(find "$TEST_REPORTS_DIR" -name "*.md" | wc -l)
210
+
211
+        if [[ $total_reports -gt 10 ]]; then
212
+            print_color "$BLUE" "Cleaning up old test reports (keeping last 10)..."
213
+
214
+            # Find and remove oldest reports, keeping the 10 most recent
215
+            find "$TEST_REPORTS_DIR" -name "*.md" -type f -printf '%T@ %p\n' | \
216
+                sort -n | \
217
+                head -n -$((10)) | \
218
+                cut -d' ' -f2- | \
219
+                xargs -r rm
220
+
221
+            local remaining=$(find "$TEST_REPORTS_DIR" -name "*.md" | wc -l)
222
+            print_color "$GREEN" "Kept $remaining most recent test reports"
223
+        fi
224
+    fi
225
+}
226
+
227
+# Test Case 0: Basic Functionality Test
228
+run_basic_functionality_test() {
229
+    print_color "$GREEN" "=== Running Test 0: Basic Functionality Test ==="
230
+
231
+    clean_test_dir
232
+    create_test_dirs
233
+
234
+    # Setup test files
235
+    copy_sample_files "media/sortable" "$SOURCE_DIR"
236
+
237
+    # Capture pre-test state
238
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
239
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
240
+
241
+    # Run test
242
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v"
243
+    local result=0
244
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
245
+
246
+    # Capture post-test state
247
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
248
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
249
+
250
+    # Generate report
251
+    generate_test_report \
252
+        "Basic_Functionality" \
253
+        "Processing files with valid EXIF data" \
254
+        "Verify correct sorting and organization of media files" \
255
+        "Sample media files with EXIF data from samples/media/sortable/" \
256
+        "$command" \
257
+        "$result"
258
+}
259
+
260
+# Test Case 1: Unimportable Files Test
261
+run_unimportable_files_test() {
262
+    print_color "$GREEN" "=== Running Test 1: Unimportable Files Test ==="
263
+
264
+    clean_test_dir
265
+    create_test_dirs
266
+
267
+    # Setup test files - mix of sortable and unimportable
268
+    copy_sample_files "media/sortable" "$SOURCE_DIR"
269
+    copy_sample_files "media/unimportable" "$SOURCE_DIR"
270
+
271
+    # Capture pre-test state
272
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
273
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
274
+
275
+    # Run test (without --collect-unimportable flag)
276
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v"
277
+    local result=0
278
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
279
+
280
+    # Capture post-test state
281
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
282
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
283
+
284
+    # Generate report
285
+    generate_test_report \
286
+        "Unimportable_Files_Handling" \
287
+        "Testing files without EXIF data in root and subfolders" \
288
+        "Verify proper handling of unimportable files without collection flag" \
289
+        "Mixed sortable and unimportable files from samples/media/" \
290
+        "$command" \
291
+        "$result"
292
+}
293
+
294
+# Test Case 2: Mixed Content Test
295
+run_mixed_content_test() {
296
+    print_color "$GREEN" "=== Running Test 2: Mixed Content Test ==="
297
+
298
+    clean_test_dir
299
+    create_test_dirs
300
+
301
+    # Setup separate folders for sortable vs unimportable
302
+    mkdir -p "$SOURCE_DIR/sortable" "$SOURCE_DIR/unimportable"
303
+    copy_sample_files "media/sortable" "$SOURCE_DIR/sortable"
304
+    copy_sample_files "media/unimportable" "$SOURCE_DIR/unimportable"
305
+
306
+    # Capture pre-test state
307
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
308
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
309
+
310
+    # Run test
311
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v"
312
+    local result=0
313
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
314
+
315
+    # Capture post-test state
316
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
317
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
318
+
319
+    # Generate report
320
+    generate_test_report \
321
+        "Mixed_Content" \
322
+        "Processing sortable and unimportable files in separate folders" \
323
+        "Verify cleanup behavior for folders with different file types" \
324
+        "Separate folders: sortable/ and unimportable/ with respective file types" \
325
+        "$command" \
326
+        "$result"
327
+}
328
+
329
+# Test Case 3: Safety Protections Test
330
+run_safety_protections_test() {
331
+    print_color "$GREEN" "=== Running Test 3: Safety Protections Test ==="
332
+
333
+    clean_test_dir
334
+    create_test_dirs
335
+
336
+    # Setup test files
337
+    copy_sample_files "media/sortable" "$SOURCE_DIR"
338
+
339
+    # Create a file in destination to test exclusion
340
+    echo "test file" > "$DEST_DIR/test.txt"
341
+
342
+    # Capture pre-test state
343
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
344
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
345
+
346
+    # Run test
347
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v"
348
+    local result=0
349
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
350
+
351
+    # Capture post-test state
352
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
353
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
354
+
355
+    # Generate report
356
+    generate_test_report \
357
+        "Safety_Protections" \
358
+        "Testing destination exclusion and move confirmation" \
359
+        "Verify data integrity protections prevent data loss" \
360
+        "Sample files with pre-existing destination content" \
361
+        "$command" \
362
+        "$result"
363
+}
364
+
365
+# Test Case 4: UTC Conversion Test
366
+run_utc_conversion_test() {
367
+    print_color "$GREEN" "=== Running Test 4: UTC Conversion Test ==="
368
+
369
+    clean_test_dir
370
+    create_test_dirs
371
+
372
+    # Setup test files (assuming QuickTime files are available)
373
+    copy_sample_files "media/sortable" "$SOURCE_DIR"
374
+
375
+    # Capture pre-test state
376
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
377
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
378
+
379
+    # Run test
380
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v"
381
+    local result=0
382
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
383
+
384
+    # Capture post-test state
385
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
386
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
387
+
388
+    # Generate report
389
+    generate_test_report \
390
+        "UTC_Conversion" \
391
+        "Testing UTC timestamp conversion for QuickTime files" \
392
+        "Verify correct UTC to local time conversion" \
393
+        "QuickTime/Apple media files with UTC timestamps" \
394
+        "$command" \
395
+        "$result"
396
+}
397
+
398
+# Test Case 5: Subdirectory Processing Test
399
+run_subdirectory_processing_test() {
400
+    print_color "$GREEN" "=== Running Test 5: Subdirectory Processing Test ==="
401
+
402
+    clean_test_dir
403
+    create_test_dirs
404
+
405
+    # Create nested directory structure
406
+    mkdir -p "$SOURCE_DIR/level1/level2"
407
+    copy_sample_files "media/sortable" "$SOURCE_DIR/level1/level2"
408
+
409
+    # Capture pre-test state
410
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
411
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
412
+
413
+    # Run test
414
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v"
415
+    local result=0
416
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
417
+
418
+    # Capture post-test state
419
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
420
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
421
+
422
+    # Generate report
423
+    generate_test_report \
424
+        "Subdirectory_Processing" \
425
+        "Testing processing of files in nested subdirectories" \
426
+        "Verify recursive file discovery and processing" \
427
+        "Files in nested directory structure (level1/level2/)" \
428
+        "$command" \
429
+        "$result"
430
+}
431
+
432
+# Test Case 6: Keep Empty Directories Test
433
+run_keep_empty_dirs_test() {
434
+    print_color "$GREEN" "=== Running Test 6: Keep Empty Directories Test ==="
435
+
436
+    clean_test_dir
437
+    create_test_dirs
438
+
439
+    # Create directory structure with some empty dirs
440
+    mkdir -p "$SOURCE_DIR/sortable" "$SOURCE_DIR/empty1" "$SOURCE_DIR/empty2"
441
+    copy_sample_files "media/sortable" "$SOURCE_DIR/sortable"
442
+
443
+    # Capture pre-test state
444
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
445
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination"
446
+
447
+    # Run test with --keep-empty-dirs
448
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" --keep-empty-dirs -v"
449
+    local result=0
450
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
451
+
452
+    # Capture post-test state
453
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
454
+    capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination"
455
+
456
+    # Generate report
457
+    generate_test_report \
458
+        "Keep_Empty_Directories" \
459
+        "Testing --keep-empty-dirs functionality" \
460
+        "Verify empty directory preservation when flag is used" \
461
+        "Directory structure with sortable files and empty directories" \
462
+        "$command" \
463
+        "$result"
464
+}
465
+
466
+# Test Case 7: Source Only Test
467
+run_source_only_test() {
468
+    print_color "$GREEN" "=== Running Test 7: Source Only Test ==="
469
+
470
+    clean_test_dir
471
+    create_test_dirs
472
+
473
+    # Setup test files
474
+    copy_sample_files "media/sortable" "$SOURCE_DIR"
475
+
476
+    # Capture pre-test state
477
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source"
478
+    capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_before.txt" "auto-destination"
479
+
480
+    # Run test with only source parameter
481
+    local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -v"
482
+    local result=0
483
+    run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$?
484
+
485
+    # Capture post-test state
486
+    capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source"
487
+    capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_after.txt" "auto-destination"
488
+
489
+    # Generate report
490
+    generate_test_report \
491
+        "Source_Only_Test" \
492
+        "Testing processing with only source parameter" \
493
+        "Verify automatic creation of sorted subdirectory" \
494
+        "Sample media files, no explicit destination specified" \
495
+        "$command" \
496
+        "$result"
497
+}
498
+
499
+# Function to show menu
500
+show_menu() {
501
+    echo ""
502
+    print_color "$GREEN" "Media Importer Test Runner"
503
+    echo "=========================="
504
+    echo ""
505
+    echo "Available Tests:"
506
+    echo "0. Basic Functionality Test"
507
+    echo "1. Unimportable Files Test"
508
+    echo "2. Mixed Content Test"
509
+    echo "3. Safety Protections Test"
510
+    echo "4. UTC Conversion Test"
511
+    echo "5. Subdirectory Processing Test"
512
+    echo "6. Keep Empty Directories Test"
513
+    echo "7. Source Only Test"
514
+    echo "8. Run All Tests"
515
+    echo "q. Quit"
516
+    echo ""
517
+    echo -n "Select test to run (0-8, q to quit): "
518
+}
519
+
520
+# Function to run all tests
521
+run_all_tests() {
522
+    print_color "$GREEN" "Running all tests sequentially..."
523
+
524
+    run_basic_functionality_test
525
+    echo ""
526
+    run_unimportable_files_test
527
+    echo ""
528
+    run_mixed_content_test
529
+    echo ""
530
+    run_safety_protections_test
531
+    echo ""
532
+    run_utc_conversion_test
533
+    echo ""
534
+    run_subdirectory_processing_test
535
+    echo ""
536
+    run_keep_empty_dirs_test
537
+    echo ""
538
+    run_source_only_test
539
+
540
+    print_color "$GREEN" "All tests completed!"
541
+}
542
+
543
+# Main menu loop
544
+main() {
545
+    while true; do
546
+        show_menu
547
+        read -r choice
548
+
549
+        case $choice in
550
+            0)
551
+                run_basic_functionality_test
552
+                ;;
553
+            1)
554
+                run_unimportable_files_test
555
+                ;;
556
+            2)
557
+                run_mixed_content_test
558
+                ;;
559
+            3)
560
+                run_safety_protections_test
561
+                ;;
562
+            4)
563
+                run_utc_conversion_test
564
+                ;;
565
+            5)
566
+                run_subdirectory_processing_test
567
+                ;;
568
+            6)
569
+                run_keep_empty_dirs_test
570
+                ;;
571
+            7)
572
+                run_source_only_test
573
+                ;;
574
+            8)
575
+                run_all_tests
576
+                ;;
577
+            q|Q)
578
+                print_color "$GREEN" "Exiting test runner."
579
+                exit 0
580
+                ;;
581
+            *)
582
+                print_color "$RED" "Invalid choice. Please select 0-8 or q to quit."
583
+                ;;
584
+        esac
585
+
586
+        echo ""
587
+        echo -n "Press Enter to continue..."
588
+        read -r
589
+    done
590
+}
591
+
592
+# Check if script exists
593
+if [[ ! -f "$MEDIA_IMPORTER" ]]; then
594
+    print_color "$RED" "Error: $MEDIA_IMPORTER not found!"
595
+    exit 1
596
+fi
597
+
598
+# Check if samples directory exists
599
+if [[ ! -d "$SAMPLES_DIR" ]]; then
600
+    print_color "$YELLOW" "Warning: $SAMPLES_DIR not found. Some tests may not work properly."
601
+fi
602
+
603
+# Run main menu if no arguments provided
604
+if [[ $# -eq 0 ]]; then
605
+    main
606
+else
607
+    # Handle command line arguments
608
+    case "$1" in
609
+        "basic"|"0")
610
+            run_basic_functionality_test
611
+            ;;
612
+        "unimportable"|"1")
613
+            run_unimportable_files_test
614
+            ;;
615
+        "mixed"|"2")
616
+            run_mixed_content_test
617
+            ;;
618
+        "safety"|"3")
619
+            run_safety_protections_test
620
+            ;;
621
+        "utc"|"4")
622
+            run_utc_conversion_test
623
+            ;;
624
+        "subdir"|"5")
625
+            run_subdirectory_processing_test
626
+            ;;
627
+        "keep-empty"|"6")
628
+            run_keep_empty_dirs_test
629
+            ;;
630
+        "source-only"|"7")
631
+            run_source_only_test
632
+            ;;
633
+        "all"|"8")
634
+            run_all_tests
635
+            ;;
636
+        "clean")
637
+            clean_test_dir
638
+            ;;
639
+        "setup")
640
+            create_test_dirs
641
+            ;;
642
+        *)
643
+            print_color "$RED" "Unknown test: $1"
644
+            echo "Usage: $0 [basic|unimportable|mixed|safety|utc|subdir|keep-empty|source-only|all|clean|setup] or [0-8]"
645
+            exit 1
646
+            ;;
647
+    esac
648
+fi