Start on 1.2.0
- + .ini configuration file read/write - + .ini configuration file verification and reset function - + Frame de-duplication function (discard similar frames based on MSE - + Minor QoL changes/improvements - - "Cancel" button - - "Cancel & Clear" button
This commit is contained in:
		
							parent
							
								
									ac4894f318
								
							
						
					
					
						commit
						16b60efe0b
					
				
							
								
								
									
										401
									
								
								Video2Crops.py
									
									
									
									
									
								
							
							
						
						
									
										401
									
								
								Video2Crops.py
									
									
									
									
									
								
							@ -14,19 +14,142 @@ import platform
 | 
				
			|||||||
import urllib.request
 | 
					import urllib.request
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
from bs4 import BeautifulSoup
 | 
					from bs4 import BeautifulSoup
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					import configparser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print(f"New start\n")
 | 
					DEBUG = True
 | 
				
			||||||
 | 
					STARTING = True
 | 
				
			||||||
 | 
					INVALID_INI = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Setup config file
 | 
				
			||||||
 | 
					config_file = 'config.ini'
 | 
				
			||||||
 | 
					config = configparser.ConfigParser()
 | 
				
			||||||
 | 
					if not os.path.exists(config_file):
 | 
				
			||||||
 | 
					    fetch_ini()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					config.read(config_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def checkinifile():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    expected_debug_keys = {
 | 
				
			||||||
 | 
					        'debug_output': bool
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CHECK_SECTIONS = True
 | 
				
			||||||
 | 
					    CHECK_KEYS = True
 | 
				
			||||||
 | 
					    CHECK_VALUES = False
 | 
				
			||||||
 | 
					    global INVALID_INI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					checkinifile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					debug('INFO', "Video2Crops starting ...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# START INITIATING PROGRAM VARIABLES FROM HERE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Constants
 | 
					# Constants
 | 
				
			||||||
v2cversion1 = 1
 | 
					v2cv = [1, 2, 0]
 | 
				
			||||||
v2cversion2 = 1
 | 
					v2cversion1 = v2cv[0]
 | 
				
			||||||
v2cversion3 = 0
 | 
					v2cversion2 = v2cv[1]
 | 
				
			||||||
 | 
					v2cversion3 = v2cv[2]
 | 
				
			||||||
v2cversion = f"{v2cversion1}.{v2cversion2}.{v2cversion3}"
 | 
					v2cversion = f"{v2cversion1}.{v2cversion2}.{v2cversion3}"
 | 
				
			||||||
frame_step = 100
 | 
					 | 
				
			||||||
value_threshold = 11.0
 | 
					 | 
				
			||||||
SAVE = True
 | 
					 | 
				
			||||||
SHOW = True
 | 
					 | 
				
			||||||
processing = False
 | 
					processing = False
 | 
				
			||||||
 | 
					processing_button_state = "start"
 | 
				
			||||||
 | 
					# Load ini settings
 | 
				
			||||||
 | 
					frame_step = config.get('settings', 'frame_step')
 | 
				
			||||||
 | 
					contrast_threshold = config.get('settings', 'contrast_threshold')
 | 
				
			||||||
 | 
					duplicate_threshold = config.get('settings', 'duplicate_threshold')
 | 
				
			||||||
 | 
					SAVE = config.get('settings', 'save_output')
 | 
				
			||||||
 | 
					SHOW = config.get('settings', 'show_playback')
 | 
				
			||||||
 | 
					roi_size = config.get('settings', 'roi_size')
 | 
				
			||||||
 | 
					tksize = config.get('settings', 'window_size')
 | 
				
			||||||
 | 
					DEBUG = config.get('debug', 'debug_output')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Define the RSS feed URL
 | 
					# Define the RSS feed URL
 | 
				
			||||||
rss_feed_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases.rss"
 | 
					rss_feed_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases.rss"
 | 
				
			||||||
@ -34,13 +157,20 @@ rss_feed_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases.rss"
 | 
				
			|||||||
# Create the root window
 | 
					# Create the root window
 | 
				
			||||||
root = TkinterDnD.Tk()
 | 
					root = TkinterDnD.Tk()
 | 
				
			||||||
root.title("Video2Crops")
 | 
					root.title("Video2Crops")
 | 
				
			||||||
root.geometry("300x450")
 | 
					root.geometry(tksize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Function to clear the queue
 | 
				
			||||||
 | 
					def clear_queue():
 | 
				
			||||||
 | 
					    selected_files_listbox.delete(0, tk.END)
 | 
				
			||||||
 | 
					    debug('DEBUG', 'Cleared work queue')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def open_changelog_window():
 | 
					def open_changelog_window():
 | 
				
			||||||
 | 
					    debug('DEBUG', 'Loading changelog window')
 | 
				
			||||||
    changelog_window = tk.Toplevel(root)
 | 
					    changelog_window = tk.Toplevel(root)
 | 
				
			||||||
    changelog_window.title("Video2Crops Changelog")
 | 
					    changelog_window.title("Video2Crops Changelog")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Reading the RSS feed from: {rss_feed_url}")
 | 
				
			||||||
        # Parse the RSS feed
 | 
					        # Parse the RSS feed
 | 
				
			||||||
        feed = feedparser.parse(rss_feed_url)
 | 
					        feed = feedparser.parse(rss_feed_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,6 +184,8 @@ def open_changelog_window():
 | 
				
			|||||||
        scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
 | 
					        scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
 | 
				
			||||||
        scrolled_text.pack(fill=tk.BOTH, expand=True)
 | 
					        scrolled_text.pack(fill=tk.BOTH, expand=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Done collecting RSS data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        entry_num = 0
 | 
					        entry_num = 0
 | 
				
			||||||
        for entry in feed.entries:
 | 
					        for entry in feed.entries:
 | 
				
			||||||
            entry_num += 1
 | 
					            entry_num += 1
 | 
				
			||||||
@ -82,10 +214,13 @@ def open_changelog_window():
 | 
				
			|||||||
                changelog_text = version_info + changelog_md + f"\n--------------------\n"
 | 
					                changelog_text = version_info + changelog_md + f"\n--------------------\n"
 | 
				
			||||||
                scrolled_text.insert(tk.END, changelog_text)
 | 
					                scrolled_text.insert(tk.END, changelog_text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Finished processing changelog")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
 | 
					        scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
 | 
				
			||||||
        scrolled_text.pack(fill=tk.BOTH, expand=True)
 | 
					        scrolled_text.pack(fill=tk.BOTH, expand=True)
 | 
				
			||||||
        scrolled_text.insert(tk.END, f"An error occurred: {str(e)}")
 | 
					        scrolled_text.insert(tk.END, f"An error occurred: {str(e)}")
 | 
				
			||||||
 | 
					        debug('ERROR', f"An error occured while processing changelog:\n{str(e)}\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def extract_version(title):
 | 
					def extract_version(title):
 | 
				
			||||||
    # Use regular expressions to extract the version number from the title
 | 
					    # Use regular expressions to extract the version number from the title
 | 
				
			||||||
@ -115,32 +250,33 @@ def extract_changelog(description):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Function for updating Video2Crops
 | 
					# Function for updating Video2Crops
 | 
				
			||||||
def check_for_updates():
 | 
					def check_for_updates():
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Checking for updates")
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        # Parse the RSS feed
 | 
					        # Parse the RSS feed
 | 
				
			||||||
        feed = feedparser.parse(rss_feed_url)
 | 
					        feed = feedparser.parse(rss_feed_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not feed.entries:
 | 
					        if not feed.entries and not STARTING:
 | 
				
			||||||
            response = tk.messagebox.showinfo(
 | 
					            response = tk.messagebox.showinfo(
 | 
				
			||||||
                "No updates found",
 | 
					                "No updates found",
 | 
				
			||||||
                f"Latest version: ({v2cversion})\n"
 | 
					                f"Latest version: ({v2cversion})\n"
 | 
				
			||||||
                f"Current version: ({v2cversion})\n"
 | 
					                f"Current version: ({v2cversion})\n"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            debug('DEBUG', f"No updates found")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Get the latest release
 | 
					        # Get the latest release
 | 
				
			||||||
        latest_release = feed.entries[0]
 | 
					        latest_release = feed.entries[0]
 | 
				
			||||||
        latest_version_str = extract_version(latest_release.link)
 | 
					        latest_version_str = extract_version(latest_release.link)
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if latest_version_str is None:
 | 
					        if latest_version_str is None and not STARTING:
 | 
				
			||||||
            tk.messagebox.showinfo(
 | 
					            tk.messagebox.showinfo(
 | 
				
			||||||
                "No update Available",
 | 
					                "No update Available",
 | 
				
			||||||
                f"Latest version: ({v2cversion})\n"
 | 
					                f"Latest version: ({v2cversion})\n"
 | 
				
			||||||
                f"Current version: ({v2cversion})\n"
 | 
					                f"Current version: ({v2cversion})\n"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            debug('DEBUG', f"No updates available")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print(f"RSS Version: {latest_version_str}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Extract version numbers from the latest release title
 | 
					        # Extract version numbers from the latest release title
 | 
				
			||||||
        latest_version = tuple(map(int, latest_version_str.split('.')))
 | 
					        latest_version = tuple(map(int, latest_version_str.split('.')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -153,46 +289,66 @@ def check_for_updates():
 | 
				
			|||||||
                f"Current version: ({v2cversion})\n"
 | 
					                f"Current version: ({v2cversion})\n"
 | 
				
			||||||
                "Do you want to update?"
 | 
					                "Do you want to update?"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            debug('DEBUG', f"Found new update, prompting user")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if response:
 | 
					            if response:
 | 
				
			||||||
 | 
					                debug('DEBUG', f"User chose to update ...")
 | 
				
			||||||
                # Determine the platform
 | 
					                # Determine the platform
 | 
				
			||||||
                current_platform = platform.system().lower()
 | 
					                current_platform = platform.system().lower()
 | 
				
			||||||
                if current_platform == "windows":
 | 
					                if current_platform == "windows":
 | 
				
			||||||
 | 
					                    debug('DEBUG', f"Detected Windows system, grabbing installer")
 | 
				
			||||||
                    # On Windows, download and run the installer
 | 
					                    # On Windows, download and run the installer
 | 
				
			||||||
                    installer_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases/download/v{}/Video2Crops-windows-installer.exe".format(latest_version_str)
 | 
					                    installer_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases/download/v{}/Video2Crops-windows-installer.exe".format(latest_version_str)
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        urllib.request.urlretrieve(installer_url, "Video2Crops-windows-installer.exe")
 | 
					                        urllib.request.urlretrieve(installer_url, "Video2Crops-windows-installer.exe")
 | 
				
			||||||
                        subprocess.Popen("Video2Crops-windows-installer.exe", shell=True)
 | 
					                        subprocess.Popen("Video2Crops-windows-installer.exe", shell=True)
 | 
				
			||||||
 | 
					                        debug('DEBUG', f"Downloaded new installer, running it ...")
 | 
				
			||||||
                        root.quit()
 | 
					                        root.quit()
 | 
				
			||||||
                    except:
 | 
					                    except:
 | 
				
			||||||
                        tk.messagebox.showinfo(
 | 
					                        tk.messagebox.showinfo(
 | 
				
			||||||
                            "Installer not found",
 | 
					                            "Installer not found",
 | 
				
			||||||
                            f"The installer file was not found.\nPlease check your antivirus and network connectivity"
 | 
					                            f"The installer file was not found.\nPlease check your antivirus and network connectivity"
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
 | 
					                        debug('ERROR', f"Windows installer not found")
 | 
				
			||||||
                elif current_platform == "linux":
 | 
					                elif current_platform == "linux":
 | 
				
			||||||
 | 
					                    debug('DEBUG', f"Detected Linux system, grabbing executable")
 | 
				
			||||||
                    # On Linux, download and run the executable
 | 
					                    # On Linux, download and run the executable
 | 
				
			||||||
                    executable_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases/download/v{}/Video2Crops-linux".format(latest_version_str)
 | 
					                    executable_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases/download/v{}/Video2Crops-linux".format(latest_version_str)
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        urllib.request.urlretrieve(executable_url, "Video2Crops-linux")
 | 
					                        urllib.request.urlretrieve(executable_url, "Video2Crops-linux")
 | 
				
			||||||
                        os.chmod("Video2Crops-linux", 0o755)
 | 
					                        os.chmod("Video2Crops-linux", 0o755)
 | 
				
			||||||
                        subprocess.Popen("./Video2Crops-linux", shell=True)
 | 
					                        subprocess.Popen("./Video2Crops-linux", shell=True)
 | 
				
			||||||
 | 
					                        debug('DEBUG', f"Downloaded new executable, running it ...")
 | 
				
			||||||
                        root.quit()
 | 
					                        root.quit()
 | 
				
			||||||
                    except:
 | 
					                    except:
 | 
				
			||||||
                        tk.messagebox.showinfo(
 | 
					                        tk.messagebox.showinfo(
 | 
				
			||||||
                            "Executable not found",
 | 
					                            "Executable not found",
 | 
				
			||||||
                            f"The executable file was not found.\nPlease check your antivirus and network connectivity"
 | 
					                            f"The executable file was not found.\nPlease check your antivirus and network connectivity"
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
 | 
					                        debug('ERROR', f"Linux executable not found")
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
 | 
					                    debug('DEBUG', f"Non-supported platform found: {current_platform}")
 | 
				
			||||||
 | 
					                    tk.messagebox.showinfo(
 | 
				
			||||||
 | 
					                            "Non-supported platform",
 | 
				
			||||||
 | 
					                            f"Video2Crops detected non-supported platform: {current_platform}.\nCurrently, only Windows and Linux are supported.\n\nYou can build from source if you need to run Video2Crops on other platforms."
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                debug('INFO', 'User closed prompt window')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if not STARTING:
 | 
				
			||||||
                tk.messagebox.showinfo(
 | 
					                tk.messagebox.showinfo(
 | 
				
			||||||
                    "No update Available",
 | 
					                    "No update Available",
 | 
				
			||||||
                    f"Latest version: ({v2cversion})\n"
 | 
					                    f"Latest version: ({v2cversion})\n"
 | 
				
			||||||
                    f"Current version: ({v2cversion})\n"
 | 
					                    f"Current version: ({v2cversion})\n"
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					            debug('DEBUG', f"No update available")
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        tk.messagebox.showerror("Error", f"An error occurred: {str(e)}")
 | 
					        tk.messagebox.showerror("Error", f"An error occurred: {str(e)}")
 | 
				
			||||||
 | 
					        debug('ERROR', f"An error occured while checking for updates:\n{str(e)}\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Function to create the output directory and return its path
 | 
					# Function to create the output directory and return its path
 | 
				
			||||||
def create_output_directory(video_filename):
 | 
					def create_output_directory(video_filename):
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Creating output directory")
 | 
				
			||||||
    base_directory = os.getcwd()  # You can change this to specify a different base directory
 | 
					    base_directory = os.getcwd()  # You can change this to specify a different base directory
 | 
				
			||||||
    video_filename_base, _ = os.path.splitext(os.path.basename(video_filename))
 | 
					    video_filename_base, _ = os.path.splitext(os.path.basename(video_filename))
 | 
				
			||||||
    output_folder_path = os.path.join(base_directory, video_filename_base)
 | 
					    output_folder_path = os.path.join(base_directory, video_filename_base)
 | 
				
			||||||
@ -201,6 +357,8 @@ def create_output_directory(video_filename):
 | 
				
			|||||||
    if not os.path.exists(output_folder_path):
 | 
					    if not os.path.exists(output_folder_path):
 | 
				
			||||||
        os.makedirs(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
 | 
					    return output_folder_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Function to calculate ROI position based on alignment and size settings
 | 
					# Function to calculate ROI position based on alignment and size settings
 | 
				
			||||||
@ -229,26 +387,47 @@ def calculate_roi(video_width, video_height, vertical_alignment, horizontal_alig
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Create a function to process video files
 | 
					# Create a function to process video files
 | 
				
			||||||
def process_video(video_filenames, main_window):
 | 
					def process_video(video_filenames, main_window):
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Starting video processing ...")
 | 
				
			||||||
    global processing
 | 
					    global processing
 | 
				
			||||||
    processing = True
 | 
					    debug('DEBUG', f"Processing state: {processing}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def write_log(message):
 | 
					    def write_log(message):
 | 
				
			||||||
        logfile_path = os.path.join(output_folder, 'logfile.txt')
 | 
					        logfile_path = os.path.join(output_folder, 'logfile.txt')
 | 
				
			||||||
        with open(logfile_path, 'a') as logfile:
 | 
					        with open(logfile_path, 'a') as logfile:
 | 
				
			||||||
            log_message = f"{datetime.now().strftime('%m-%d_%H:%M:%S')} - {message}\n"
 | 
					            log_message = f"{datetime.now().strftime('%m-%d_%H:%M:%S')} - {message}\n"
 | 
				
			||||||
            logfile.write(log_message)
 | 
					            logfile.write(log_message)
 | 
				
			||||||
            print(log_message)
 | 
					            debug('LOG EVENT', f"{log_message}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def calc_value(image):
 | 
					    def calc_value(image):
 | 
				
			||||||
        grayscale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
 | 
					        grayscale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
 | 
				
			||||||
        return np.std(grayscale)
 | 
					        return np.std(grayscale)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    def check_duplicate(ref_frame, frame):
 | 
				
			||||||
 | 
					        frame_is_duplicate = True
 | 
				
			||||||
 | 
					        # Calculate the Absolute Difference between the frames
 | 
				
			||||||
 | 
					        frame_diff = cv2.absdiff(ref_frame, frame)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Calculate the Mean Squared Error (MSE) between the frames
 | 
				
			||||||
 | 
					        mse = np.mean(frame_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if mse > duplicate_threshold:
 | 
				
			||||||
 | 
					            # The frame is significantly different, store it
 | 
				
			||||||
 | 
					            ref_frame = frame.copy()
 | 
				
			||||||
 | 
					            # Process the frame here or store it for later processing
 | 
				
			||||||
 | 
					            frame_is_duplicate = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return frame_is_duplicate, mse, ref_frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    corner1 = (-1, -1)
 | 
					    corner1 = (-1, -1)
 | 
				
			||||||
    corner2 = (-1, -1)
 | 
					    corner2 = (-1, -1)
 | 
				
			||||||
    drawing = False
 | 
					    drawing = False
 | 
				
			||||||
    cropped_image = None
 | 
					    cropped_image = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for videofilename in video_filenames:
 | 
					    for videofilename in video_filenames:
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Processing video file: {videofilename}")
 | 
				
			||||||
 | 
					        if not processing:
 | 
				
			||||||
 | 
					            debug('DEBUG', f"Breaking loop due to processing = {processing}")
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        main_window.title(f"Video Processing App")
 | 
					        main_window.title(f"Video Processing App")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -283,6 +462,7 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
        vertical_alignment = vertical_alignment_var.get()
 | 
					        vertical_alignment = vertical_alignment_var.get()
 | 
				
			||||||
        horizontal_alignment = horizontal_alignment_var.get()
 | 
					        horizontal_alignment = horizontal_alignment_var.get()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Displaying video player window: {SHOW}")
 | 
				
			||||||
        if SHOW:
 | 
					        if SHOW:
 | 
				
			||||||
            vp_title = "Video2Crops Video Player"
 | 
					            vp_title = "Video2Crops Video Player"
 | 
				
			||||||
            # Get the screen resolution
 | 
					            # Get the screen resolution
 | 
				
			||||||
@ -297,6 +477,9 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
            cv2.namedWindow(vp_title, cv2.WINDOW_NORMAL)
 | 
					            cv2.namedWindow(vp_title, cv2.WINDOW_NORMAL)
 | 
				
			||||||
            cv2.resizeWindow(vp_title, initial_width, initial_height)
 | 
					            cv2.resizeWindow(vp_title, initial_width, initial_height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Store the first frame as reference for the duplicate check
 | 
				
			||||||
 | 
					        ret, reference_frame = cap.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
            ret, frame = cap.read()
 | 
					            ret, frame = cap.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -324,7 +507,8 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            key = cv2.waitKey(10)
 | 
					            key = cv2.waitKey(10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if key == ord('q'):
 | 
					            if key == ord('q') or (not processing):
 | 
				
			||||||
 | 
					                debug('DEBUG', f"Breaking loop due to cancel event")
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if cropped_image is not None and frame_cnt % frame_step == 0:
 | 
					            if cropped_image is not None and frame_cnt % frame_step == 0:
 | 
				
			||||||
@ -333,8 +517,10 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
                    cv2.namedWindow(ci_title, cv2.WINDOW_NORMAL)
 | 
					                    cv2.namedWindow(ci_title, cv2.WINDOW_NORMAL)
 | 
				
			||||||
                    cv2.imshow(ci_title, cropped_image)
 | 
					                    cv2.imshow(ci_title, cropped_image)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                frame_is_duplicate, frame_duplicate_mse, reference_frame = check_duplicate(reference_frame, cropped_image)
 | 
				
			||||||
                frame_value = calc_value(cropped_image)
 | 
					                frame_value = calc_value(cropped_image)
 | 
				
			||||||
                if frame_value >= value_threshold:
 | 
					
 | 
				
			||||||
 | 
					                if frame_value >= contrast_threshold and not frame_is_duplicate:
 | 
				
			||||||
                    cropped_image_status = "Accepted"
 | 
					                    cropped_image_status = "Accepted"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    filename = videofilename_base + '_' + str(counter).zfill(4) + ".png"
 | 
					                    filename = videofilename_base + '_' + str(counter).zfill(4) + ".png"
 | 
				
			||||||
@ -343,7 +529,7 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
                    if SAVE:
 | 
					                    if SAVE:
 | 
				
			||||||
                        cv2.imwrite(image_filename, cropped_image)
 | 
					                        cv2.imwrite(image_filename, cropped_image)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    cropped_image_info = f"Image on frame {frame_cnt} ({cropped_image_status}) - {frame_value} / {value_threshold} - image nr: {str(str(counter)).zfill(4)}"
 | 
					                    cropped_image_info = f"Image on frame {frame_cnt} ({cropped_image_status} | {frame_duplicate_mse}) - {frame_value} / {contrast_threshold} - image nr: {str(str(counter)).zfill(4)}"
 | 
				
			||||||
                    write_log(cropped_image_info)
 | 
					                    write_log(cropped_image_info)
 | 
				
			||||||
                    counter += 1
 | 
					                    counter += 1
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
@ -352,14 +538,20 @@ def process_video(video_filenames, main_window):
 | 
				
			|||||||
                    if SAVE:
 | 
					                    if SAVE:
 | 
				
			||||||
                        cv2.imwrite(image_filename, cropped_image)
 | 
					                        cv2.imwrite(image_filename, cropped_image)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    cropped_image_info = f"Image on frame {frame_cnt} ({cropped_image_status}) - {frame_value} / {value_threshold} - image nr: {str(str(counter)).zfill(4)}"
 | 
					                    cropped_image_info = f"Image on frame {frame_cnt} ({cropped_image_status} | {frame_duplicate_mse}) - {frame_value} / {contrast_threshold} - image nr: {str(str(counter)).zfill(4)}"
 | 
				
			||||||
                    write_log(cropped_image_info)
 | 
					                    write_log(cropped_image_info)
 | 
				
			||||||
                    discarded_frames += 1
 | 
					                    discarded_frames += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            frame_cnt += 1
 | 
					            frame_cnt += 1
 | 
				
			||||||
 | 
					            if frame_cnt == 1:
 | 
				
			||||||
 | 
					                debug('DEBUG', f"Video size: {video_width}x{video_height}")
 | 
				
			||||||
 | 
					                debug('DEBUG', f"ROI location: {vertical_alignment}-{horizontal_alignment}")
 | 
				
			||||||
 | 
					                debug('DEBUG', f"ROI size: {roi_size}")
 | 
				
			||||||
 | 
					                debug('DEBUG', f"ROI position (left, top, right, bottom): {x1}, {y1}, {x2}, {y2}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cap.release()
 | 
					        cap.release()
 | 
				
			||||||
        cv2.destroyAllWindows()
 | 
					        cv2.destroyAllWindows()
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Destroyed all current CV2 windows")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        write_log(f"Video2Crops run complete!\nSaved {counter} frames.\nDiscarded {discarded_frames} frames.")
 | 
					        write_log(f"Video2Crops run complete!\nSaved {counter} frames.\nDiscarded {discarded_frames} frames.")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -372,35 +564,75 @@ def add_files():
 | 
				
			|||||||
    if file_paths:
 | 
					    if file_paths:
 | 
				
			||||||
        for file_path in file_paths:
 | 
					        for file_path in file_paths:
 | 
				
			||||||
            selected_files_listbox.insert(tk.END, file_path)
 | 
					            selected_files_listbox.insert(tk.END, file_path)
 | 
				
			||||||
 | 
					            debug('DEBUG', f"Queued video file: {file_path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a button to remove the selected file from the Listbox
 | 
					# Create a button to remove the selected file from the Listbox
 | 
				
			||||||
def remove_file():
 | 
					def remove_file():
 | 
				
			||||||
    selected_index = selected_files_listbox.curselection()
 | 
					    selected_index = selected_files_listbox.curselection()
 | 
				
			||||||
    if selected_index:
 | 
					    if selected_index:
 | 
				
			||||||
        selected_files_listbox.delete(selected_index)
 | 
					        selected_files_listbox.delete(selected_index)
 | 
				
			||||||
 | 
					        debug('DEBUG', f"Removed file from queue: {selected_index}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a function to clear the queue
 | 
					# Create a function to clear the queue
 | 
				
			||||||
def clear_queue():
 | 
					def clear_queue():
 | 
				
			||||||
    selected_files_listbox.delete(0, tk.END)
 | 
					    selected_files_listbox.delete(0, tk.END)
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Cleared the queue")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a function to cancel processing
 | 
					# Create a function to cancel processing
 | 
				
			||||||
def cancel_processing():
 | 
					def cancel_processing():
 | 
				
			||||||
    global processing
 | 
					    global processing
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Attempting cancelation ...")
 | 
				
			||||||
    if processing:
 | 
					    if processing:
 | 
				
			||||||
        processing = False
 | 
					        processing = False
 | 
				
			||||||
 | 
					        if processing_thread.is_alive():
 | 
				
			||||||
 | 
					            processing_thread.join()  # Wait for the processing thread to finish cleanly
 | 
				
			||||||
        messagebox.showinfo("Processing Canceled", "Video processing has been canceled.")
 | 
					        messagebox.showinfo("Processing Canceled", "Video processing has been canceled.")
 | 
				
			||||||
 | 
					    elif not processing:
 | 
				
			||||||
 | 
					        processing = False
 | 
				
			||||||
 | 
					        messagebox.showinfo("Nothing to cancel", "Video2Crops isn't processing anything.")
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Cancel complete. Processing state: {processing}")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
# Create a button to process the selected files
 | 
					# Create a button to process the selected files
 | 
				
			||||||
def process_files():
 | 
					def process_files():
 | 
				
			||||||
    global processing
 | 
					    debug('DEBUG', f"Starting file processing ...")
 | 
				
			||||||
 | 
					    global processing, processing_thread
 | 
				
			||||||
    if not processing:
 | 
					    if not processing:
 | 
				
			||||||
 | 
					        processing = True
 | 
				
			||||||
        selected_files = selected_files_listbox.get(0, tk.END)
 | 
					        selected_files = selected_files_listbox.get(0, tk.END)
 | 
				
			||||||
        for file_path in selected_files:
 | 
					        processing_thread = threading.Thread(target=process_video, args=(selected_files, root))
 | 
				
			||||||
            process_video([file_path], root)
 | 
					        processing_thread.daemon = True  # Set the thread as a daemon so it terminates when the main program exits
 | 
				
			||||||
 | 
					        processing_thread.start()
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					    debug('DEBUG', f"File processing done")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Define dynamic process/cancel button
 | 
				
			||||||
 | 
					def process_cancel_toggle():
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Toggling process/cancel button state")
 | 
				
			||||||
 | 
					    global processing, processing_thread, processing_button_state
 | 
				
			||||||
 | 
					    if processing_button_state == "start":
 | 
				
			||||||
 | 
					        if not processing:
 | 
				
			||||||
 | 
					            processing = True
 | 
				
			||||||
 | 
					            selected_files = selected_files_listbox.get(0, tk.END)
 | 
				
			||||||
 | 
					            processing_thread = threading.Thread(target=process_video, args=(selected_files, root))
 | 
				
			||||||
 | 
					            processing_thread.daemon = True
 | 
				
			||||||
 | 
					            processing_thread.start()
 | 
				
			||||||
 | 
					            processing_button.config(text="Cancel")
 | 
				
			||||||
 | 
					            processing_button_state = "cancel"
 | 
				
			||||||
 | 
					    elif processing_button_state == "cancel":
 | 
				
			||||||
 | 
					        if processing:
 | 
				
			||||||
 | 
					            processing = False
 | 
				
			||||||
 | 
					            if processing_thread.is_alive():
 | 
				
			||||||
 | 
					                processing_thread.join()
 | 
				
			||||||
 | 
					            processing_button.config(text="Process Files")
 | 
				
			||||||
 | 
					            processing_button_state = "start"
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Processing/Cancel button state switch finished")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a function to open the preferences window
 | 
					# Create a function to open the preferences window
 | 
				
			||||||
def open_preferences():
 | 
					def open_preferences():
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Opening preferences window")
 | 
				
			||||||
    preferences_window = tk.Toplevel(root)
 | 
					    preferences_window = tk.Toplevel(root)
 | 
				
			||||||
    preferences_window.title("Preferences")
 | 
					    preferences_window.title("Preferences")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -411,73 +643,116 @@ def open_preferences():
 | 
				
			|||||||
                frame_step_var.set(str(new_frame_step))
 | 
					                frame_step_var.set(str(new_frame_step))
 | 
				
			||||||
                global frame_step
 | 
					                global frame_step
 | 
				
			||||||
                frame_step = new_frame_step
 | 
					                frame_step = new_frame_step
 | 
				
			||||||
 | 
					                write2ini('settings', 'frame_step', new_frame_step)
 | 
				
			||||||
 | 
					                debug('DEBUG', f"Set frame stepping to {frame_step}")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def change_value_threshold():
 | 
					    def change_contrast_threshold():
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            new_value_threshold = simpledialog.askfloat("Value Threshold", "Enter the new value threshold:")
 | 
					            new_contrast_threshold = simpledialog.askfloat("Contrast Threshold", "Enter the new value threshold:")
 | 
				
			||||||
            if new_value_threshold is not None:
 | 
					            if new_contrast_threshold is not None:
 | 
				
			||||||
                value_threshold_var.set(str(new_value_threshold))
 | 
					                contrast_threshold_var.set(str(new_contrast_threshold))
 | 
				
			||||||
 | 
					                write2ini('settings', 'contrast_threshold', new_contrast_threshold)
 | 
				
			||||||
 | 
					                debug('DEBUG', f"Set contrast discard threshold to {new_contrast_threshold}")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def change_duplicate_threshold():
 | 
				
			||||||
 | 
					        if not processing:
 | 
				
			||||||
 | 
					            new_duplicate_threshold = simpledialog.askfloat("Duplicate Threshold", "Enter the new value threshold:")
 | 
				
			||||||
 | 
					            if new_duplicate_threshold is not None:
 | 
				
			||||||
 | 
					                duplicate_threshold_var.set(str(new_duplicate_threshold))
 | 
				
			||||||
 | 
					                write2ini('settings', 'duplicate_threshold', new_duplicate_threshold)
 | 
				
			||||||
 | 
					                debug('DEBUG', f"Set duplicate discard threshold to {new_duplicate_threshold}")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def toggle_save():
 | 
					    def toggle_save():
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            global SAVE
 | 
					            global SAVE
 | 
				
			||||||
            SAVE = not 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():
 | 
					    def toggle_show():
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            global SHOW
 | 
					            global SHOW
 | 
				
			||||||
            SHOW = not SHOW
 | 
					            SHOW = not SHOW
 | 
				
			||||||
 | 
					            write2ini('settings', 'show_playback', SHOW)
 | 
				
			||||||
 | 
					            debug('DEBUG', f"Displaying output: {SHOW}")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def apply_roi_size():
 | 
					    def apply_roi_size():
 | 
				
			||||||
        if not processing:
 | 
					        if not processing:
 | 
				
			||||||
            new_roi_size = simpledialog.askinteger("ROI Size", "Enter the new ROI size:")
 | 
					            new_roi_size = simpledialog.askinteger("ROI Size", "Enter the new ROI size:")
 | 
				
			||||||
            if new_roi_size is not None:
 | 
					            if new_roi_size is not None:
 | 
				
			||||||
                roi_size_var.set(str(new_roi_size))
 | 
					                roi_size_var.set(str(new_roi_size))
 | 
				
			||||||
 | 
					                write2ini('settings', 'roi_size', new_roi_size)
 | 
				
			||||||
 | 
					                debug('DEBUG', f"Set new ROI size to {new_roi_size}")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messagebox.showinfo("Info", "Video2Crops is currently processing files!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Creating preference fields ...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Create preference fields
 | 
					    # Create preference fields
 | 
				
			||||||
    frame_step_label = tk.Label(preferences_window, text="Skip n frames:")
 | 
					    frame_step_label = tk.Label(preferences_window, text="Skip n frames:")
 | 
				
			||||||
    frame_step_label.grid(row=0, column=0, padx=20, pady=10)
 | 
					    frame_step_label.grid(row=0, column=0, padx=20, pady=5)
 | 
				
			||||||
    frame_step_value = tk.Label(preferences_window, textvariable=frame_step_var)
 | 
					    frame_step_value = tk.Label(preferences_window, textvariable=frame_step_var)
 | 
				
			||||||
    frame_step_value.grid(row=0, column=1, padx=20, pady=10)
 | 
					    frame_step_value.grid(row=0, column=1, padx=20, pady=5)
 | 
				
			||||||
    frame_step_button = tk.Button(preferences_window, text="Edit", command=change_frame_step)
 | 
					    frame_step_button = tk.Button(preferences_window, text="Edit", command=change_frame_step)
 | 
				
			||||||
    frame_step_button.grid(row=0, column=2, padx=20, pady=10)
 | 
					    frame_step_button.grid(row=0, column=2, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    value_threshold_label = tk.Label(preferences_window, text="Discard Threshold:")
 | 
					    contrast_threshold_label = tk.Label(preferences_window, text="Contrast Threshold:")
 | 
				
			||||||
    value_threshold_label.grid(row=1, column=0, padx=20, pady=10)
 | 
					    contrast_threshold_label.grid(row=1, column=0, padx=20, pady=5)
 | 
				
			||||||
    value_threshold_value = tk.Label(preferences_window, textvariable=value_threshold_var)
 | 
					    contrast_threshold_value = tk.Label(preferences_window, textvariable=contrast_threshold_var)
 | 
				
			||||||
    value_threshold_value.grid(row=1, column=1, padx=20, pady=10)
 | 
					    contrast_threshold_value.grid(row=1, column=1, padx=20, pady=5)
 | 
				
			||||||
    value_threshold_button = tk.Button(preferences_window, text="Edit", command=change_value_threshold)
 | 
					    contrast_threshold_button = tk.Button(preferences_window, text="Edit", command=change_contrast_threshold)
 | 
				
			||||||
    value_threshold_button.grid(row=1, column=2, padx=20, pady=10)
 | 
					    contrast_threshold_button.grid(row=1, column=2, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    duplicate_threshold_label = tk.Label(preferences_window, text="Duplicate Threshold:")
 | 
				
			||||||
 | 
					    duplicate_threshold_label.grid(row=2, column=0, padx=20, pady=5)
 | 
				
			||||||
 | 
					    duplicate_threshold_value = tk.Label(preferences_window, textvariable=duplicate_threshold_var)
 | 
				
			||||||
 | 
					    duplicate_threshold_value.grid(row=2, column=1, padx=20, pady=5)
 | 
				
			||||||
 | 
					    duplicate_threshold_button = tk.Button(preferences_window, text="Edit", command=change_duplicate_threshold)
 | 
				
			||||||
 | 
					    duplicate_threshold_button.grid(row=2, column=2, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    save_checkbox = tk.Checkbutton(preferences_window, text="Save Output Images", variable=save_var, command=toggle_save)
 | 
					    save_checkbox = tk.Checkbutton(preferences_window, text="Save Output Images", variable=save_var, command=toggle_save)
 | 
				
			||||||
    save_checkbox.grid(row=2, column=0, columnspan=3, padx=20, pady=10)
 | 
					    save_checkbox.grid(row=3, column=0, columnspan=3, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    show_checkbox = tk.Checkbutton(preferences_window, text="Display Playback", variable=show_var, command=toggle_show)
 | 
					    show_checkbox = tk.Checkbutton(preferences_window, text="Display Playback", variable=show_var, command=toggle_show)
 | 
				
			||||||
    show_checkbox.grid(row=3, column=0, columnspan=3, padx=20, pady=10)
 | 
					    show_checkbox.grid(row=4, column=0, columnspan=3, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Section divider
 | 
					    # Section divider
 | 
				
			||||||
    divider_label = tk.Label(preferences_window, text="ROI Settings", font=("Helvetica", 12, "bold"))
 | 
					    divider_label = tk.Label(preferences_window, text="ROI Settings", font=("Helvetica", 12, "bold"))
 | 
				
			||||||
    divider_label.grid(row=4, column=0, columnspan=3, padx=20, pady=(20, 10))
 | 
					    divider_label.grid(row=5, column=0, columnspan=3, padx=20, pady=(20, 10))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Create ROI fields
 | 
					    # Create ROI fields
 | 
				
			||||||
    vertical_alignment_label = tk.Label(preferences_window, text="Vertical Alignment:")
 | 
					    vertical_alignment_label = tk.Label(preferences_window, text="Vertical Alignment:")
 | 
				
			||||||
    vertical_alignment_label.grid(row=5, column=0, padx=20, pady=10)
 | 
					    vertical_alignment_label.grid(row=6, column=0, padx=20, pady=5)
 | 
				
			||||||
    vertical_alignment_var.set("Center")  # Default alignment
 | 
					    vertical_alignment_var.set("Center")  # Default alignment
 | 
				
			||||||
    vertical_alignment_menu = tk.OptionMenu(preferences_window, vertical_alignment_var, "Top", "Center", "Bottom")
 | 
					    vertical_alignment_menu = tk.OptionMenu(preferences_window, vertical_alignment_var, "Top", "Center", "Bottom")
 | 
				
			||||||
    vertical_alignment_menu.grid(row=5, column=1, padx=20, pady=10)
 | 
					    vertical_alignment_menu.grid(row=6, column=1, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    horizontal_alignment_label = tk.Label(preferences_window, text="Horizontal Alignment:")
 | 
					    horizontal_alignment_label = tk.Label(preferences_window, text="Horizontal Alignment:")
 | 
				
			||||||
    horizontal_alignment_label.grid(row=6, column=0, padx=20, pady=10)
 | 
					    horizontal_alignment_label.grid(row=7, column=0, padx=20, pady=5)
 | 
				
			||||||
    horizontal_alignment_var.set("Center")  # Default alignment
 | 
					    horizontal_alignment_var.set("Center")  # Default alignment
 | 
				
			||||||
    horizontal_alignment_menu = tk.OptionMenu(preferences_window, horizontal_alignment_var, "Left", "Center", "Right")
 | 
					    horizontal_alignment_menu = tk.OptionMenu(preferences_window, horizontal_alignment_var, "Left", "Center", "Right")
 | 
				
			||||||
    horizontal_alignment_menu.grid(row=6, column=1, padx=20, pady=10)
 | 
					    horizontal_alignment_menu.grid(row=7, column=1, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    roi_size_label = tk.Label(preferences_window, text="ROI Size:")
 | 
					    roi_size_label = tk.Label(preferences_window, text="ROI Size:")
 | 
				
			||||||
    roi_size_label.grid(row=7, column=0, padx=20, pady=10)
 | 
					    roi_size_label.grid(row=8, column=0, padx=20, pady=5)
 | 
				
			||||||
    roi_size_value = tk.Label(preferences_window, textvariable=roi_size_var)
 | 
					    roi_size_value = tk.Label(preferences_window, textvariable=roi_size_var)
 | 
				
			||||||
    roi_size_value.grid(row=7, column=1, padx=20, pady=10)
 | 
					    roi_size_value.grid(row=8, column=1, padx=20, pady=5)
 | 
				
			||||||
    roi_size_button = tk.Button(preferences_window, text="Edit", command=apply_roi_size)
 | 
					    roi_size_button = tk.Button(preferences_window, text="Edit", command=apply_roi_size)
 | 
				
			||||||
    roi_size_button.grid(row=7, column=2, padx=20, pady=10)
 | 
					    roi_size_button.grid(row=8, column=2, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    debug('DEBUG', f"Done creating preference fields")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					debug('DEBUG', 'Functions defined')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a menu
 | 
					# Create a menu
 | 
				
			||||||
menu = tk.Menu(root)
 | 
					menu = tk.Menu(root)
 | 
				
			||||||
@ -485,7 +760,8 @@ root.config(menu=menu)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Variables for preferences
 | 
					# Variables for preferences
 | 
				
			||||||
frame_step_var = tk.StringVar()
 | 
					frame_step_var = tk.StringVar()
 | 
				
			||||||
value_threshold_var = tk.StringVar()
 | 
					contrast_threshold_var = tk.StringVar()
 | 
				
			||||||
 | 
					duplicate_threshold_var = tk.StringVar()
 | 
				
			||||||
save_var = tk.BooleanVar()
 | 
					save_var = tk.BooleanVar()
 | 
				
			||||||
show_var = tk.BooleanVar()
 | 
					show_var = tk.BooleanVar()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -493,13 +769,14 @@ show_var = tk.BooleanVar()
 | 
				
			|||||||
vertical_alignment_var = tk.StringVar()
 | 
					vertical_alignment_var = tk.StringVar()
 | 
				
			||||||
horizontal_alignment_var = tk.StringVar()
 | 
					horizontal_alignment_var = tk.StringVar()
 | 
				
			||||||
roi_size_var = tk.StringVar()
 | 
					roi_size_var = tk.StringVar()
 | 
				
			||||||
roi_size_var.set("400")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Set initial values for preference variables
 | 
					# Set initial values for preference variables
 | 
				
			||||||
frame_step_var.set(str(frame_step))
 | 
					frame_step_var.set(str(frame_step))
 | 
				
			||||||
value_threshold_var.set(str(value_threshold))
 | 
					contrast_threshold_var.set(str(contrast_threshold))
 | 
				
			||||||
 | 
					duplicate_threshold_var.set(str(duplicate_threshold))
 | 
				
			||||||
save_var.set(SAVE)
 | 
					save_var.set(SAVE)
 | 
				
			||||||
show_var.set(SHOW)
 | 
					show_var.set(SHOW)
 | 
				
			||||||
 | 
					roi_size_var.set(roi_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a "Queue" submenu
 | 
					# Create a "Queue" submenu
 | 
				
			||||||
queue_menu = tk.Menu(menu, tearoff=0)
 | 
					queue_menu = tk.Menu(menu, tearoff=0)
 | 
				
			||||||
@ -507,40 +784,46 @@ menu.add_cascade(label="Queue", menu=queue_menu)
 | 
				
			|||||||
queue_menu.add_command(label="Add file", command=add_files)
 | 
					queue_menu.add_command(label="Add file", command=add_files)
 | 
				
			||||||
queue_menu.add_command(label="Clear queue", command=clear_queue)
 | 
					queue_menu.add_command(label="Clear queue", command=clear_queue)
 | 
				
			||||||
queue_menu.add_separator()
 | 
					queue_menu.add_separator()
 | 
				
			||||||
queue_menu.add_command(label="Cancel", command=cancel_processing)
 | 
					queue_menu.add_command(label="Cancel", state="disabled", command=cancel_processing)
 | 
				
			||||||
queue_menu.add_command(label="Cancel & Clear", command=lambda: [cancel_processing(), clear_queue()])
 | 
					queue_menu.add_command(label="Cancel & Clear", state="disabled", command=lambda: [cancel_processing(), clear_queue()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a "Preferences" submenu
 | 
					# Create a "Preferences" submenu
 | 
				
			||||||
menu.add_command(label="Preferences", command=open_preferences)
 | 
					menu.add_command(label="Preferences", command=open_preferences)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create an "Info" submenu
 | 
					# Create an "Info" submenu
 | 
				
			||||||
info_menu = tk.Menu(menu, tearoff=0)
 | 
					info_menu = tk.Menu(menu, tearoff=0, disabledforeground="#000")
 | 
				
			||||||
menu.add_cascade(label="Info", menu=info_menu)
 | 
					menu.add_cascade(label="Info", menu=info_menu)
 | 
				
			||||||
info_menu.add_command(label="Project Folder", command=lambda: webbrowser.open(os.getcwd()))
 | 
					info_menu.add_command(label="Project Folder", command=lambda: webbrowser.open(os.getcwd()))
 | 
				
			||||||
info_menu.add_separator()
 | 
					info_menu.add_separator()
 | 
				
			||||||
info_menu.add_command(label="About", command=lambda: webbrowser.open("https://git.rolfsvaag.no/frarol96/Video2Crops/wiki"))
 | 
					info_menu.add_command(label="About", command=lambda: webbrowser.open("https://git.rolfsvaag.no/frarol96/Video2Crops/wiki"))
 | 
				
			||||||
info_menu.add_command(label=f"Version: {str(v2cversion)}")
 | 
					 | 
				
			||||||
info_menu.add_command(label=f"Check for updates", command=check_for_updates)
 | 
					info_menu.add_command(label=f"Check for updates", command=check_for_updates)
 | 
				
			||||||
info_menu.add_command(label=f"View Changelog", command=open_changelog_window)
 | 
					info_menu.add_command(label=f"View Changelog", command=open_changelog_window)
 | 
				
			||||||
 | 
					info_menu.add_separator()
 | 
				
			||||||
 | 
					info_menu.add_command(state="disabled", label=f"Version: {str(v2cversion)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a label for selected files
 | 
					# Create a label for selected files
 | 
				
			||||||
label = tk.Label(root, text="Selected Files:")
 | 
					label = tk.Label(root, text="Selected Files:")
 | 
				
			||||||
label.pack(padx=20, pady=10)
 | 
					label.pack(padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a Listbox to display the selected files
 | 
					# Create a Listbox to display the selected files
 | 
				
			||||||
selected_files_listbox = tk.Listbox(root, selectmode=tk.SINGLE, exportselection=0)
 | 
					selected_files_listbox = tk.Listbox(root, selectmode=tk.SINGLE, exportselection=0)
 | 
				
			||||||
selected_files_listbox.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
 | 
					selected_files_listbox.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a button to select and add files to the Listbox
 | 
					# Create a button to select and add files to the Listbox
 | 
				
			||||||
select_files_button = tk.Button(root, text="Select Files", command=add_files)
 | 
					select_files_button = tk.Button(root, text="Select Files", command=add_files)
 | 
				
			||||||
select_files_button.pack(padx=20, pady=10)
 | 
					select_files_button.pack(padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a button to remove the selected file from the Listbox
 | 
					# Create a button to remove the selected file from the Listbox
 | 
				
			||||||
remove_file_button = tk.Button(root, text="Remove File", command=remove_file)
 | 
					remove_file_button = tk.Button(root, text="Remove File", command=remove_file)
 | 
				
			||||||
remove_file_button.pack(padx=20, pady=10)
 | 
					remove_file_button.pack(padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a button to process the selected files
 | 
					# Create a button to process the selected files
 | 
				
			||||||
process_files_button = tk.Button(root, text="Process Files", command=process_files)
 | 
					processing_button = tk.Button(root, text="Process Files", command=process_cancel_toggle)
 | 
				
			||||||
process_files_button.pack(padx=20, pady=10)
 | 
					processing_button.pack(padx=20, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					debug('DEBUG', 'UI generated, running main loop')
 | 
				
			||||||
 | 
					debug('INFO', '... Video2Crops initialized and ready!')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					check_for_updates()
 | 
				
			||||||
 | 
					STARTING = False
 | 
				
			||||||
root.mainloop()
 | 
					root.mainloop()
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user