# validate_videos.py """ Video Collection Validator Checks NFC mappings against actual video files and provides detailed reports """ import sys from pathlib import Path from datetime import datetime import logging # Setup basic logging logging.basicConfig( level=logging.INFO, format='%(levelname)s: %(message)s' ) class VideoValidator: def __init__(self): self.base_dir = Path(__file__).parent self.videos_dir = self.base_dir / "videos" self.trailers_dir = self.videos_dir / "trailers" self.specific_dir = self.videos_dir / "specific" self.mapping_file = self.videos_dir / "key_mapping.txt" self.supported_formats = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'] self.results = { 'valid_mappings': [], 'missing_files': [], 'unmapped_videos': [], 'trailer_count': 0, 'specific_count': 0 } def print_header(self): """Print validation header""" print("\n" + "="*70) print("VIDEO COLLECTION VALIDATOR") print("="*70) print(f"Validation Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("="*70 + "\n") def check_directories(self): """Check if required directories exist""" print("Checking directories...") print("-" * 70) all_exist = True dirs = [ ("Videos", self.videos_dir), ("Trailers", self.trailers_dir), ("Specific Videos", self.specific_dir) ] for name, path in dirs: if path.exists(): print(f"✓ {name} directory found: {path}") else: print(f"✗ {name} directory MISSING: {path}") all_exist = False print() return all_exist def scan_video_files(self, directory, category): """Scan a directory for video files""" if not directory.exists(): return [] videos = [] for file_path in directory.iterdir(): if file_path.is_file() and file_path.suffix.lower() in self.supported_formats: size_mb = file_path.stat().st_size / (1024 * 1024) videos.append({ 'path': file_path, 'name': file_path.name, 'stem': file_path.stem, 'size_mb': size_mb }) return videos def load_nfc_mappings(self): """Load and parse NFC mappings""" if not self.mapping_file.exists(): print(f"✗ Key mapping file not found: {self.mapping_file}") return {} mappings = {} line_num = 0 try: with open(self.mapping_file, 'r', encoding='utf-8') as f: for line in f: line_num += 1 line = line.strip() # Skip comments and empty lines if not line or line.startswith('#'): continue if ',' not in line: print(f"⚠ Warning: Invalid format at line {line_num}: {line}") continue parts = line.split(',', 1) if len(parts) != 2: continue nfc_id = parts[0].strip() movie_name = parts[1].strip() if nfc_id and movie_name: mappings[nfc_id] = movie_name except Exception as e: print(f"✗ Error reading mapping file: {e}") return {} return mappings def find_video_match(self, movie_name, video_files): """Find matching video file for a movie name""" movie_lower = movie_name.lower() # Strategy 1: Exact match (stem) for video in video_files: if video['stem'].lower() == movie_lower: return video, 'exact' # Strategy 2: Movie name in filename for video in video_files: if movie_lower in video['stem'].lower(): return video, 'substring' # Strategy 3: Filename in movie name (reverse check) for video in video_files: if video['stem'].lower() in movie_lower: return video, 'reverse' return None, None def validate_mappings(self): """Validate NFC mappings against video files""" print("Validating NFC mappings...") print("-" * 70) # Load mappings mappings = self.load_nfc_mappings() if not mappings: print("✗ No valid mappings found in key_mapping.txt") print(" Create mappings in format: NFC_ID,Movie_Name") print() return print(f"Found {len(mappings)} NFC mappings to validate") print() # Scan specific video files specific_videos = self.scan_video_files(self.specific_dir, "specific") self.results['specific_count'] = len(specific_videos) print(f"Found {len(specific_videos)} video files in specific folder") print() # Check each mapping print("Checking mappings:") print() for nfc_id, movie_name in mappings.items(): match, match_type = self.find_video_match(movie_name, specific_videos) if match: self.results['valid_mappings'].append({ 'nfc_id': nfc_id, 'movie_name': movie_name, 'file': match['name'], 'size_mb': match['size_mb'], 'match_type': match_type }) print(f"✓ {nfc_id} → '{movie_name}'") print(f" Matched: {match['name']} ({match['size_mb']:.1f} MB) [{match_type}]") else: self.results['missing_files'].append({ 'nfc_id': nfc_id, 'movie_name': movie_name }) print(f"✗ {nfc_id} → '{movie_name}'") print(f" ERROR: No matching video file found!") print() def check_unmapped_videos(self): """Find videos that don't have NFC mappings""" print("Checking for unmapped videos...") print("-" * 70) mappings = self.load_nfc_mappings() specific_videos = self.scan_video_files(self.specific_dir, "specific") # Get list of mapped movie names (lowercase for comparison) mapped_names = set(name.lower() for name in mappings.values()) unmapped = [] for video in specific_videos: # Check if this video matches any mapping video_matched = False for movie_name in mappings.values(): if (movie_name.lower() in video['stem'].lower() or video['stem'].lower() in movie_name.lower()): video_matched = True break if not video_matched: unmapped.append(video) self.results['unmapped_videos'].append(video) if unmapped: print(f"Found {len(unmapped)} unmapped videos:") print() for video in unmapped: print(f" • {video['name']} ({video['size_mb']:.1f} MB)") print() print("Consider adding NFC mappings for these videos!") else: print("✓ All specific videos have NFC mappings") print() def check_trailers(self): """Check trailer video collection""" print("Checking trailer collection...") print("-" * 70) trailers = self.scan_video_files(self.trailers_dir, "trailers") self.results['trailer_count'] = len(trailers) if len(trailers) == 0: print("✗ No trailer videos found!") print(" Add trailer videos to: " + str(self.trailers_dir)) elif len(trailers) < 5: print(f"⚠ Only {len(trailers)} trailers found") print(" Consider adding more trailers for variety") print() for trailer in trailers: print(f" • {trailer['name']} ({trailer['size_mb']:.1f} MB)") else: print(f"✓ Found {len(trailers)} trailer videos") total_size = sum(t['size_mb'] for t in trailers) print(f" Total size: {total_size:.1f} MB") print() print("Sample trailers:") for trailer in trailers[:5]: print(f" • {trailer['name']} ({trailer['size_mb']:.1f} MB)") if len(trailers) > 5: print(f" ... and {len(trailers) - 5} more") print() def print_summary(self): """Print validation summary""" print("\n" + "="*70) print("VALIDATION SUMMARY") print("="*70) valid_count = len(self.results['valid_mappings']) missing_count = len(self.results['missing_files']) unmapped_count = len(self.results['unmapped_videos']) trailer_count = self.results['trailer_count'] specific_count = self.results['specific_count'] print(f"\nTrailer Videos: {trailer_count}") print(f"Specific Videos: {specific_count}") print(f"Valid NFC Mappings: {valid_count}") print(f"Missing Files: {missing_count}") print(f"Unmapped Videos: {unmapped_count}") # Overall status print("\n" + "-"*70) if missing_count == 0 and trailer_count > 0: print("✓ VALIDATION PASSED - System ready!") elif missing_count == 0 and trailer_count == 0: print("⚠ WARNING - No trailer videos found") else: print("✗ VALIDATION FAILED - Fix missing files") print("-"*70) # Recommendations print("\nRECOMMENDATIONS:") if missing_count > 0: print(f" • Fix {missing_count} missing video file(s)") if trailer_count == 0: print(" • Add trailer videos to trailers folder") elif trailer_count < 5: print(f" • Add more trailers (currently only {trailer_count})") if unmapped_count > 0: print(f" • Add NFC mappings for {unmapped_count} unmapped video(s)") if missing_count == 0 and trailer_count >= 5 and unmapped_count == 0: print(" • Everything looks good! Your system is ready to use.") print() def save_report(self): """Save detailed report to file""" report_file = self.base_dir / f"validation_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt" try: with open(report_file, 'w', encoding='utf-8') as f: f.write("VIDEO COLLECTION VALIDATION REPORT\n") f.write("="*70 + "\n") f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write("="*70 + "\n\n") f.write("VALID MAPPINGS:\n") f.write("-"*70 + "\n") for item in self.results['valid_mappings']: f.write(f"NFC: {item['nfc_id']}\n") f.write(f" Mapping: '{item['movie_name']}'\n") f.write(f" File: {item['file']}\n") f.write(f" Size: {item['size_mb']:.1f} MB\n") f.write(f" Match: {item['match_type']}\n\n") if self.results['missing_files']: f.write("\nMISSING FILES:\n") f.write("-"*70 + "\n") for item in self.results['missing_files']: f.write(f"NFC: {item['nfc_id']}\n") f.write(f" Looking for: '{item['movie_name']}'\n") f.write(f" Status: FILE NOT FOUND\n\n") if self.results['unmapped_videos']: f.write("\nUNMAPPED VIDEOS:\n") f.write("-"*70 + "\n") for video in self.results['unmapped_videos']: f.write(f"File: {video['name']}\n") f.write(f" Size: {video['size_mb']:.1f} MB\n") f.write(f" Status: No NFC mapping\n\n") print(f"Detailed report saved to: {report_file}") except Exception as e: print(f"Warning: Could not save report file: {e}") def run(self): """Run complete validation""" self.print_header() # Check directories if not self.check_directories(): print("\n✗ Required directories are missing!") print(" Please run setup or create the required directories.") return 1 # Check trailers self.check_trailers() # Validate mappings self.validate_mappings() # Check unmapped videos self.check_unmapped_videos() # Print summary self.print_summary() # Save report self.save_report() # Return exit code if len(self.results['missing_files']) > 0: return 1 return 0 def main(): """Main entry point""" try: validator = VideoValidator() exit_code = validator.run() print("\n" + "="*70) print("Press any key to exit...") input() return exit_code except KeyboardInterrupt: print("\n\nValidation cancelled by user") return 1 except Exception as e: print(f"\n✗ Validation error: {e}") logging.exception("Validation failed") return 1 if __name__ == "__main__": sys.exit(main())