1+ import os
2+ import json
3+ import subprocess
4+ from pathlib import Path
5+ from PIL import Image
6+ from datetime import datetime
7+ from selenium import webdriver
8+ from selenium .webdriver .chrome .options import Options
9+ import time
10+
11+ def get_recently_modified_paths_git (project_root , num_entries = 10 ):
12+ """Get recently modified files using git"""
13+ try :
14+ # Get the list of files modified in the most recent commits
15+ result = subprocess .run (
16+ ["git" , "-C" , str (project_root ), "log" , "--name-only" , "--pretty=format:" , "-n" , "50" ],
17+ capture_output = True , text = True , check = True
18+ )
19+
20+ # Parse the output to get unique file paths
21+ modified_files = [line .strip () for line in result .stdout .split ('\n ' ) if line .strip ()]
22+
23+ # Filter to include only relevant paths (HTML, JS, or other web content)
24+ relevant_files = []
25+ for file_path in modified_files :
26+ if file_path .endswith (('.html' , '.htm' , '.js' , '.css' , '.php' )) and os .path .exists (project_root / file_path ):
27+ relevant_files .append (file_path )
28+
29+ # Remove duplicates and limit to requested number
30+ unique_files = list (dict .fromkeys (relevant_files ))
31+ return unique_files [:num_entries ]
32+ except Exception as e :
33+ print (f"Error using git to find modified files: { str (e )} " )
34+ return []
35+
36+ def get_recently_modified_paths_timestamp (project_root , num_entries = 10 ):
37+ """Get recently modified files using file timestamps"""
38+ try :
39+ # Get all HTML, JS, and other web content files
40+ all_files = []
41+ for ext in ['.html' , '.htm' , '.js' , '.css' , '.php' ]:
42+ all_files .extend (list (project_root .glob (f"**/*{ ext } " )))
43+
44+ # Sort by modification time (newest first)
45+ modified_files = sorted (all_files , key = lambda x : os .path .getmtime (x ), reverse = True )
46+
47+ # Convert to relative paths
48+ relative_paths = [str (path .relative_to (project_root )) for path in modified_files ]
49+
50+ return relative_paths [:num_entries ]
51+ except Exception as e :
52+ print (f"Error finding modified files by timestamp: { str (e )} " )
53+ return []
54+
55+ def update_screenshots (entries_file , modified_paths = None , max_entries = 10 , base_url = "http://localhost:80" ):
56+ """
57+ Update screenshots for recently modified entries in a JSON file
58+
59+ Args:
60+ entries_file: Path to the JSON file with entries
61+ modified_paths: List of recently modified file paths
62+ max_entries: Maximum number of entries to update
63+ base_url: Base URL for the local server
64+ """
65+ # Get absolute project root path (2 levels up from the script location)
66+ script_dir = Path (__file__ ).resolve ().parent
67+ project_root = script_dir .parent
68+
69+ # Convert relative path to absolute
70+ entries_path = project_root / entries_file
71+
72+ print (f"Processing file: { entries_path } " )
73+
74+ # Load JSON data with UTF-8 encoding
75+ with open (entries_path , 'r' , encoding = 'utf-8' ) as f :
76+ try :
77+ data = json .load (f )
78+ except json .JSONDecodeError :
79+ print (f"Error: Could not parse JSON file { entries_path } " )
80+ return
81+
82+ # Handle different JSON structures
83+ if isinstance (data , dict ) and "entries" in data :
84+ entries = data ["entries" ]
85+ elif isinstance (data , list ):
86+ entries = data
87+ else :
88+ entries = [data ]
89+
90+ # Filter entries to only those recently modified
91+ if modified_paths :
92+ filtered_entries = []
93+ for entry in entries :
94+ if "url" in entry :
95+ # Check if this entry's URL matches any of the modified paths
96+ for path in modified_paths :
97+ if entry ["url" ] in path or path in entry ["url" ]:
98+ filtered_entries .append (entry )
99+ break
100+
101+ # If no matching entries found, take the most recent ones by date field
102+ if not filtered_entries and entries and "date" in entries [0 ]:
103+ sorted_entries = sorted (entries , key = lambda x : x .get ("date" , "" ), reverse = True )
104+ filtered_entries = sorted_entries [:max_entries ]
105+ print (f"No matching files found. Using { len (filtered_entries )} most recent entries by date." )
106+
107+ entries = filtered_entries [:max_entries ]
108+
109+ if not entries :
110+ print ("No entries to update." )
111+ return
112+
113+ print (f"Updating screenshots for { len (entries )} entries..." )
114+
115+ # Set up headless browser with CSS to hide scrollbars
116+ chrome_options = Options ()
117+ chrome_options .add_argument ("--headless" )
118+ chrome_options .add_argument ("--hide-scrollbars" ) # Hide scrollbars in Chrome
119+ driver = webdriver .Chrome (options = chrome_options )
120+ driver .set_window_size (800 , 800 )
121+
122+ # Update screenshots for each entry
123+ for i , entry in enumerate (entries ):
124+ if "url" not in entry or "screenshot" not in entry :
125+ print (f"Skipping entry { i + 1 } : Missing url or screenshot" )
126+ continue
127+
128+ url = entry ["url" ]
129+ screenshot_path = entry ["screenshot" ]
130+
131+ # Convert screenshot path to absolute path
132+ abs_screenshot_path = project_root / screenshot_path
133+
134+ print (f"[{ i + 1 } /{ len (entries )} ] Updating: { entry .get ('title' , url )} " )
135+
136+ try :
137+ # Ensure screenshot directory exists
138+ abs_screenshot_path .parent .mkdir (parents = True , exist_ok = True )
139+
140+ # Take screenshot
141+ full_url = f"{ base_url } /{ url } " if not url .startswith ("http" ) else url
142+ print (f" Loading: { full_url } " )
143+ driver .get (full_url )
144+
145+ # Wait for page to load
146+ time .sleep (1 )
147+
148+ # Hide scrollbars, #sidebar-toggle and a.logo elements before taking screenshot
149+ driver .execute_script ("""
150+ // Hide scrollbars
151+ document.documentElement.style.overflow = 'hidden';
152+ document.body.style.overflow = 'hidden';
153+ document.documentElement.style.msOverflowStyle = 'none';
154+ document.documentElement.style.scrollbarWidth = 'none';
155+
156+ // Hide sidebar-toggle
157+ const sidebarToggle = document.getElementById('sidebar-toggle');
158+ if (sidebarToggle) {
159+ sidebarToggle.style.display = 'none';
160+ }
161+
162+ // Hide logo links
163+ const logoLinks = document.querySelectorAll('a.logo');
164+ logoLinks.forEach(link => {
165+ link.style.display = 'none';
166+ });
167+ """ )
168+
169+ # Take screenshot
170+ driver .save_screenshot (str (abs_screenshot_path ))
171+
172+ # Resize screenshot
173+ image = Image .open (abs_screenshot_path )
174+ resized_image = image .resize ((260 , 260 ), Image .LANCZOS )
175+ resized_image .save (abs_screenshot_path )
176+
177+ print (f" ✓ Screenshot updated: { screenshot_path } " )
178+ except Exception as e :
179+ print (f" ✗ Error updating screenshot: { str (e )} " )
180+
181+ driver .quit ()
182+ print (f"Screenshot update completed for { entries_path } " )
183+
184+ if __name__ == "__main__" :
185+ print ("Screenshot Update Tool" )
186+ print ("---------------------" )
187+
188+ # Get absolute project root path
189+ script_dir = Path (__file__ ).resolve ().parent
190+ project_root = script_dir .parent
191+
192+ # Ask for method to determine recent changes
193+ print ("\n How would you like to identify recently changed files?" )
194+ print ("1: Use git history (recommended)" )
195+ print ("2: Use file timestamps" )
196+ print ("3: Update all entries" )
197+
198+ choice = input ("Enter choice (default: 1): " ) or "1"
199+
200+ # Default base URL
201+ base_url = input ("\n Enter the base URL (default: http://localhost:80): " ) or "http://localhost:80"
202+
203+ # Get number of entries to update
204+ max_entries = int (input ("\n Enter maximum number of entries to update (default: 10): " ) or "10" )
205+
206+ # Get modified paths based on chosen method
207+ modified_paths = None
208+ if choice == "1" :
209+ print ("\n Finding recently modified files using git..." )
210+ modified_paths = get_recently_modified_paths_git (project_root , max_entries )
211+ print (f"Found { len (modified_paths )} recently modified files" )
212+ elif choice == "2" :
213+ print ("\n Finding recently modified files using timestamps..." )
214+ modified_paths = get_recently_modified_paths_timestamp (project_root , max_entries )
215+ print (f"Found { len (modified_paths )} recently modified files" )
216+ else :
217+ print ("\n Updating all entries..." )
218+
219+ # Show modified paths
220+ if modified_paths :
221+ print ("\n Recently modified files:" )
222+ for path in modified_paths :
223+ print (f" - { path } " )
224+
225+ # Process games and tools
226+ update_screenshots ("data/games.json" , modified_paths , max_entries , base_url )
227+ update_screenshots ("data/tools.json" , modified_paths , max_entries , base_url )
228+
229+ print ("\n Screenshot update process completed!" )
0 commit comments