import re, os, urllib.request import tkinter as tk from datetime import datetime from bs4 import BeautifulSoup from tkinter import filedialog, simpledialog, messagebox, scrolledtext from v2cmodules.common import * def extract_version(title): # Use regular expressions to extract the version number from the title match = re.search(r'(\d+\.\d+\.\d+)', title) if match: return match.group(1) return None def fetch_ini(): global INVALID_INI try: with open(CONFIG_FILE, 'w') as file: config_url = 'https://git.rolfsvaag.no/frarol96/Video2Crops/raw/branch/main/config.ini' with urllib.request.urlopen(config_url) as config_template: config_template = config_template.read().decode("utf-8") file.write(config_template) tk.messagebox.showinfo("Reset config file", f"Config file has been reset") debug('DEBUG', f"Config file was reset to default") INVALID_INI = False except: print(f"[ERROR] Unable to fetch the missing config.ini file!\nCheck directory permissions and network connectivity!") exit # Debug function def debug(state, message): if DEBUG: allowed_states = ['LOG EVENT', 'DEBUG', 'ERROR', 'INFO'] if state in allowed_states: debug_message = f"[{state}] {message}" print(f"{debug_message}") debugfile_path = os.path.join(os.getcwd(), 'debugfile.txt') with open(debugfile_path, 'a') as debugfile: log_debug_message = f"{datetime.now().strftime('%m-%d_%H:%M:%S')} | [{state}] {message}\n" debugfile.write(log_debug_message) else: raise ValueError(f"Invalid state '{state}'. Please choose from {', '.join(allowed_states)}.") def checkinifile(): global INVALID_INI # Check for the presence of sections and keys expected_sections = ['settings', 'debug'] expected_settings_keys = { 'frame_step': int, 'contrast_threshold': float, 'duplicate_threshold': int, 'save_output': bool, 'show_playback': bool, 'roi_size': int, 'window_size': str, 'user_defined_output_dir': str } expected_debug_keys = { 'debug_output': bool } CHECK_SECTIONS = True CHECK_KEYS = True CHECK_VALUES = False # Check for the presence of sections if CHECK_SECTIONS: for section in expected_sections: if section not in config.sections(): INVALID_INI = True print(f"Missing section: [{section}]") # Check for the presence of keys and their types in the 'settings' section if 'settings' in config.sections(): for key, expected_type in expected_settings_keys.items(): if key not in config['settings']: if CHECK_KEYS: INVALID_INI = True print(f"Missing key '{key}' in [settings] section") else: value = config.get('settings', key) if not isinstance(value, expected_type): if CHECK_VALUES: INVALID_INI = True print(f"Value of '{key}' in [settings] section is not of expected type {expected_type}") # Check for the presence of keys and their types in the 'debug' section if 'debug' in config.sections(): for key, expected_type in expected_debug_keys.items(): if key not in config['debug']: if CHECK_KEYS: INVALID_INI = True print(f"Missing key '{key}' in [debug] section") else: value = config.get('debug', key) if not isinstance(value, expected_type): if CHECK_VALUES: INVALID_INI = True print(f"Value of '{key}' in [debug] section is not of expected type {expected_type}") if INVALID_INI: fetch_ini() def write2ini(section, variable, value): config.set(section, str(variable), str(value)) # Save the changes to the INI file with open(CONFIG_FILE, 'w') as configfile: config.write(configfile) def extract_changelog(description): # Parse the HTML content using BeautifulSoup soup = BeautifulSoup(description, 'html.parser') # Find all changelog sections changelog_sections = soup.find_all('h3', id='user-content-changelog') changelogs = [] for changelog_section in changelog_sections: # Extract the changelog text as plain text changelog_text = changelog_section.find_next('ul').get_text() changelogs.append(changelog_text.strip()) # Join the changelogs into a single string changelog_str = '\n'.join(changelogs) return changelog_str if changelog_str else None def toggle_save(): if not processing: global SAVE SAVE = not SAVE write2ini('settings', 'save_output', SAVE) debug('DEBUG', f"Saving output: {SAVE}") else: messagebox.showinfo("Info", "Video2Crops is currently processing files!") def toggle_show(): if not processing: global SHOW SHOW = not SHOW write2ini('settings', 'show_playback', SHOW) debug('DEBUG', f"Displaying output: {SHOW}") else: messagebox.showinfo("Info", "Video2Crops is currently processing files!") # Function to create the output directory and return its path def create_output_directory(video_filename): debug('DEBUG', f"Creating output directory") base_directory = user_defined_output_dir video_filename_base, _ = os.path.splitext(os.path.basename(video_filename)) output_folder_path = os.path.join(base_directory, video_filename_base) # Create the output directory if it doesn't exist if not os.path.exists(output_folder_path): os.makedirs(output_folder_path) debug('DEBUG', f"Processing file: {video_filename}") debug('DEBUG', f"Created output directory at: {output_folder_path}") return output_folder_path def truncate_path(path, pre_max=None, post_max=None): # Split the path into components path_components = os.path.normpath(path).split(os.path.sep) # Ensure the first two components are displayed in full pre_part = os.path.sep.join(path_components[:2]) # Ensure the last two components are displayed in full post_part = os.path.sep.join(path_components[-2:]) # Truncate the pre-portion if pre_max is specified if pre_max is not None: pre_part = pre_part[:pre_max] # Truncate the post-portion if post_max is specified if post_max is not None: post_part = post_part[-post_max:] # Combine the parts with ellipsis in between truncated_path = f"{pre_part}...{post_part}" debug('DEBUG', f"Truncated the path \'{path}\' to \'{truncated_path}\'") return truncated_path