import argparse import os import sys from loguru import logger from collections import Counter class MarkLogError(Exception): """Custom exception for mark.log validation failures.""" pass def parse_args(): parser = argparse.ArgumentParser( description="Check the mark.log file that is generated by the CI to make sure it contains the expected values" ) group = parser.add_mutually_exclusive_group(required=True) group.add_argument( "--guids", action="store_true", help="Check the mark.log file for guids" ) group.add_argument( "--locations", action="store_true", help="Check the mark.log file for locations" ) group.add_argument( "--write", action="store_true", help="Check the mark.log file for write-run" ) group.add_argument( "--plex", action="store_true", help="Check the mark.log file for Plex" ) group.add_argument( "--jellyfin", action="store_true", help="Check the mark.log file for Jellyfin" ) group.add_argument( "--emby", action="store_true", help="Check the mark.log file for Emby" ) return parser.parse_args() def read_marklog(): marklog = os.path.join(os.getcwd(), "mark.log") try: with open(marklog, "r") as f: lines = [line.strip() for line in f if line.strip()] return lines except Exception as e: raise MarkLogError(f"Error reading {marklog}: {e}") def check_marklog(lines, expected_values): found_counter = Counter(lines) expected_counter = Counter(expected_values) # Determine missing and extra items by comparing counts missing = expected_counter - found_counter extra = found_counter - expected_counter if missing or extra: if missing: logger.error("Missing expected entries (with counts):") for entry, count in missing.items(): logger.error(f" {entry}: missing {count} time(s)") if extra: logger.error("Unexpected extra entries found (with counts):") for entry, count in extra.items(): logger.error(f" {entry}: found {count} extra time(s)") logger.error( f"Entry count mismatch: found {len(lines)} entries, expected {len(expected_values)} entries." ) logger.error("Full mark.log content:") for line in sorted(lines): logger.error(f" {line}") raise MarkLogError("mark.log validation failed.") return True def main(): args = parse_args() # Expected values defined for each check expected_jellyfin = [ "Plex/JellyPlex-CI/jellyplex_watched/Custom Movies/Movie Two (2021)", "Plex/JellyPlex-CI/jellyplex_watched/Custom TV Shows/Greatest Show Ever 3000/Episode 2", "Plex/JellyPlex-CI/jellyplex_watched/Movies/Five Nights at Freddy's", "Plex/JellyPlex-CI/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/301215", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/300670", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/300741", "Emby/Emby-Server/jellyplex_watched/Custom Movies/Movie Two", "Emby/Emby-Server/jellyplex_watched/Custom TV Shows/Greatest Show Ever (3000)/S01E02", "Emby/Emby-Server/jellyplex_watched/Movies/The Family Plan", "Emby/Emby-Server/jellyplex_watched/Movies/Five Nights at Freddy's", "Emby/Emby-Server/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/5", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/5", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/5", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out", ] expected_emby = [ "Plex/JellyPlex-CI/jellyplex_watched/Custom Movies/Movie Three (2022)", "Plex/JellyPlex-CI/jellyplex_watched/Custom TV Shows/Greatest Show Ever 3000/Episode 3", "Plex/JellyPlex-CI/jellyplex_watched/Movies/Tears of Steel", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Parallels and Interiors/240429", "Jellyfin/Jellyfin-Server/JellyUser/Custom Movies/Movie Three (2022)", "Jellyfin/Jellyfin-Server/JellyUser/Custom TV Shows/Greatest Show Ever (3000)/S01E03", "Jellyfin/Jellyfin-Server/JellyUser/Movies/Tears of Steel", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4", ] expected_plex = [ "Jellyfin/Jellyfin-Server/JellyUser/Movies/Big Buck Bunny", "Jellyfin/Jellyfin-Server/JellyUser/Movies/Killers of the Flower Moon/4", "Jellyfin/Jellyfin-Server/JellyUser/Custom TV Shows/Greatest Show Ever (3000)/S01E01", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/The Unquiet Dead", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/Aliens of London (1)/4", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Secrets and Lies", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4", "Jellyfin/Jellyfin-Server/JellyUser/Custom Movies/Movie One (2020)", "Emby/Emby-Server/jellyplex_watched/Movies/Big Buck Bunny", "Emby/Emby-Server/jellyplex_watched/Movies/The Family Plan", "Emby/Emby-Server/jellyplex_watched/Movies/Killers of the Flower Moon/4", "Emby/Emby-Server/jellyplex_watched/Custom TV Shows/Greatest Show Ever (3000)/S01E01", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The Unquiet Dead", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Aliens of London (1)/4", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Secrets and Lies", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out", "Emby/Emby-Server/jellyplex_watched/Custom Movies/Movie One", ] expected_locations = expected_emby + expected_plex + expected_jellyfin # Remove Custom Movies/TV Shows as they should not have guids expected_guids = [item for item in expected_locations if "Custom" not in item] expected_write = [ "Plex/JellyPlex-CI/jellyplex_watched/Custom Movies/Movie Two (2021)", "Plex/JellyPlex-CI/jellyplex_watched/Custom TV Shows/Greatest Show Ever 3000/Episode 2", "Plex/JellyPlex-CI/jellyplex_watched/Movies/Five Nights at Freddy's", "Plex/JellyPlex-CI/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/301215", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/300670", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/300741", "Jellyfin/Jellyfin-Server/JellyUser/Movies/Big Buck Bunny", "Jellyfin/Jellyfin-Server/JellyUser/Movies/Killers of the Flower Moon/4", "Jellyfin/Jellyfin-Server/JellyUser/Custom TV Shows/Greatest Show Ever (3000)/S01E01", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/The Unquiet Dead", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/Aliens of London (1)/4", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Secrets and Lies", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4", "Jellyfin/Jellyfin-Server/JellyUser/Custom Movies/Movie One (2020)", "Plex/JellyPlex-CI/jellyplex_watched/Custom Movies/Movie Three (2022)", "Plex/JellyPlex-CI/jellyplex_watched/Custom TV Shows/Greatest Show Ever 3000/Episode 3", "Plex/JellyPlex-CI/jellyplex_watched/Movies/Tears of Steel", "Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Parallels and Interiors/240429", "Emby/Emby-Server/jellyplex_watched/Movies/Big Buck Bunny", "Emby/Emby-Server/jellyplex_watched/Movies/The Family Plan", "Emby/Emby-Server/jellyplex_watched/Movies/Five Nights at Freddy's", "Emby/Emby-Server/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/5", "Emby/Emby-Server/jellyplex_watched/Movies/Killers of the Flower Moon/4", "Emby/Emby-Server/jellyplex_watched/Custom TV Shows/Greatest Show Ever (3000)/S01E01", "Emby/Emby-Server/jellyplex_watched/Custom TV Shows/Greatest Show Ever (3000)/S01E02", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/5", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The Unquiet Dead", "Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Aliens of London (1)/4", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/5", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Secrets and Lies", "Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out", "Emby/Emby-Server/jellyplex_watched/Custom Movies/Movie One", "Emby/Emby-Server/jellyplex_watched/Custom Movies/Movie Two", "Jellyfin/Jellyfin-Server/JellyUser/Custom Movies/Movie Three (2022)", "Jellyfin/Jellyfin-Server/JellyUser/Custom TV Shows/Greatest Show Ever (3000)/S01E03", "Jellyfin/Jellyfin-Server/JellyUser/Movies/Tears of Steel", "Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4", ] # Determine which expected values to use based on the command-line flag if args.guids: expected_values = expected_guids check_type = "GUIDs" elif args.locations: expected_values = expected_locations check_type = "locations" elif args.write: expected_values = expected_write check_type = "write-run" elif args.plex: expected_values = expected_plex check_type = "Plex" elif args.jellyfin: expected_values = expected_jellyfin check_type = "Jellyfin" elif args.emby: expected_values = expected_emby check_type = "Emby" else: raise MarkLogError("No server specified") logger.info(f"Validating mark.log for {check_type}...") try: lines = read_marklog() check_marklog(lines, expected_values) except MarkLogError as e: logger.error(e) sys.exit(1) logger.success("Successfully validated mark.log") sys.exit(0) if __name__ == "__main__": main()