• Hi Guest!

    We are extremely excited to announce the release of our first Beta1.1 and the first release of our Public AddonKit!
    To participate in the Beta, a subscription to the Entertainer or Creator Tier is required. For access to the Public AddonKit you must be a Creator tier member. Once subscribed, download instructions can be found here.

    Click here for information and guides regarding the VaM2 beta. Join our Discord server for more announcements and community discussion about VaM2.
  • Hi Guest!

    VaM2 Resource Categories have now been added to the Hub! For information on posting VaM2 resources and details about VaM2 related changes to our Community Forums, please see our official announcement here.

Script for listing all dependencies

I've found what looks like a bug in the script. If a custom preset exists, the script seems to skip, or not report actual var dependencies. The first pass below is with the preset removed. The second pass is after I put it back in Custom/Atom/Empty. This tripped me up a couple of times. I use this thing almost constantly!

PS C:\games\vam> python List_Deps.py -p . -n vamstory.23 The following var is not used as a dependency in other vars: hazmhox.vamstory.23.var The following 1 iteration of 'vamstory.23' has other vars that depend on it: hazmhox.vamstory.23 -> Ashen Ryder.Room_BlowjobERv1.3.var Ashen Ryder.Room_BlowjobFRv1.2.var ispinox.Kamasutra_Part_1_Male_or_Futa_FREE.4.var ispinox.SexBot_1_The_Prototype_FREE.3.var ispinox.The_Last_Dick_with_Ellie_3_Animations_FREE.5.var kodek.shoplifter.2.var sevenseason.Exaggerated_Blowjob_V4_Final_Version.1.var sevenseason.Free_Exaggerated_Blowjob.1.var sevenseason.Free_Exaggerated_Blowjob.2.var Shapes.Ashley.1.var TdotONY.Leaving_You_Satisfied.2.var VAMFantasy.Banya.4.var VamTastic.RE_Graveyard.3.var xxxa.Free_Soft_Ride_by_xxxa.3.var xxxa.The_Black_VamHub_by_xxxa.1.var PS C:\games\vam> python List_Deps.py -p . -n vamstory.23 The following var is not used as a dependency in other vars: hazmhox.vamstory.23.var The following 1 iteration of 'vamstory.23' has other vars that depend on it: hazmhox.vamstory.23 -> Atom/Empty/Preset_shoplifter start menu.vap PS C:\games\vam>
 
The problem above is caused by merging two Python dicts. When merged dicts have any of the same keys, the second one overwrites the first. I only learned enough Python to be dangerous, but here's a fix:

#!/usr/bin/python3 from zipfile import ZipFile from glob import glob from os.path import join, split, splitext, exists, basename, isdir, dirname import sys import argparse from pathlib import Path from json import load import re def dpPrint(text: str, toFile: str = ''): print(text, flush=True) if toFile: try: with open(toFile, 'a', encoding='utf-8') as f: f.write(text + '\n') except Exception as error: print(f"ERROR, failed to write to {toFile}: {error}", file=sys.stderr, flush=True) return def check_name_variation(varName, dependenciesList): parts = varName.split('.') if parts[-1].isdigit() or parts[-1] == 'latest': baseName = '.'.join(parts[:-1]) else: baseName = varName return varName in dependenciesList or f'{baseName}.latest' in dependenciesList def getDependencies(varFile: str) -> tuple[Exception|None, set]: try: with ZipFile(varFile) as myzip: with myzip.open('meta.json') as metaJson: data = load(metaJson) myzip.close() if 'dependencies' not in data: return None, set() return None, set(data['dependencies'].keys()) except Exception as error: return error, set() # Get all dependencies listed in presets def getPresetDependencies(customPath: str) -> dict: allDependencies = {} presets = sorted(glob(join(customPath, 'Custom', '**/*.vap'), recursive=True)) for vap in presets: try: with open(vap, 'r', encoding='utf-8') as f: pattern = r'"([^"]+):/[^"]*"' occurences = set(re.findall(pattern, f.read())) except Exception as error: dpPrint(f"ERROR, failed to read: {vap}: {error}", args.output) continue parts = Path(vap).parts presetName = '/'.join(parts[parts.index("Custom")+1:]) dependenciesDict = {key: {presetName} for key in occurences} for key, val in dependenciesDict.items(): if key in allDependencies: allDependencies[key].update(val) else: allDependencies[key] = val return allDependencies # Collect all vars, a complete list of all dependencies, and which vars uses which dependency def getAllVars(directoryPath: str) -> tuple[set, dict]: allDependencies = {} vars = glob(join(directoryPath, 'AddonPackages', '**/*.var'), recursive=True) allVarList = {basename(var) for var in vars} for varFilename in vars: if isdir(varFilename): error, dependencies = getMetaDependencies(varFilename) else: error, dependencies = getDependencies(varFilename) if (isinstance(error, Exception)): dpPrint(f"ERROR: {varFilename}: {error}", args.output) continue if (len(dependencies) == 0): continue dependenciesDict = {key: {split(varFilename)[1]} for key in dependencies} for key, val in dependenciesDict.items(): if key in allDependencies: allDependencies[key].update(val) else: allDependencies[key] = val return allVarList, allDependencies # Get all dependencies listed in meta.json stored directly in folders def getMetaDependencies(folder: str) -> tuple[Exception|None, set]: filename = join(folder, 'meta.json') try: with open(filename, 'r', encoding='utf-8') as metaJson: data = load(metaJson) if 'dependencies' not in data: return None, set() return None, set(data['dependencies'].keys()) except Exception as error: return error, set() argParser = argparse.ArgumentParser(prog='DependencyScanner.py', description='Searches for dependencies inside all vars in a folder and if a name is given, lists whether there a vars that depend on it. If no name is given, it will instead list all vars in the folder which isn\'t a dependency of any other var.') argParser.add_argument("-p", "--path", type=Path, default='.', help="Path your VaM folder") argParser.add_argument("-n", "--name", type=str, default='', help="Name of a var file. Can be a partial name") argParser.add_argument("-o", "--output", type=Path, nargs='?', default=None, const='.\\SearchVarOutput.txt', help="File to save the results in (default: '.\\SearchVarOutput.txt')") args = argParser.parse_args() # Check the given arguments are valid if not exists(args.path): print(f"The given path doesn't exist: {args.path}") exit(1) if args.output: if isdir(args.output): print(f"The given output is a folder, not a file: {args.output}") exit(1) if not isdir(dirname(args.output)) and not basename(args.output) != '': print(f"The given output file folder path doesn't exist: {args.output}") exit(1) open(args.output, 'w', encoding='utf-8').close() parts = args.name.split('.') if parts[-1] == 'var': args.name = '.'.join(parts[:-1]) # Get a list of all vars and a dict of all dependencies from vars allVarList, allVarDependencies = getAllVars(args.path) allDepVars = allVarDependencies.keys() # Get a dict of all dependencies from presets allPresetDependencies = getPresetDependencies(args.path) allDepVarsPresets = allPresetDependencies.keys() # Combine all dependencies # Incorrect! Must keep separate dicts till later # allDependencies = allVarDependencies | allPresetDependencies # allDependencies = dict(list(allVarDependencies.items()) + list(allPresetDependencies.items())) # Get a list of which vars in the folder that are not used as a dependency noDependencyVar = [var for var in allVarList if not check_name_variation(var, allDepVars)] noDependencyPreset = [var for var in allVarList if not check_name_variation(var, allDepVarsPresets)] noDependency = sorted(list(set(noDependencyVar) & set(noDependencyPreset)), key=str.lower) # If no name is given, list all vars that aren't used as a dependency if args.name == '': dpPrint(f"{len(noDependency)} vars are not used as a dependency:", args.output) for var in noDependency: dpPrint("\t" + var, args.output) exit(0) # List all vars with the given name that aren't used as a dependency foundVars = [var for var in noDependency if args.name.lower() in var.lower()] if len(foundVars) > 0: dpPrint(f"The following {str(len(foundVars)) + ' vars were' if len(foundVars) > 1 else 'var was'} checked for dependency in other vars:", args.output) for var in foundVars: dpPrint("\t" + var, args.output) print() # List all vars with the given name that depend on other vars # Keep separate lists for Var dependencies and Preset dependencies foundDepAVars = sorted([var for var in allVarDependencies.keys() if args.name.lower() in var.lower()], key=str.lower) foundDepPVars = sorted([var for var in allPresetDependencies.keys() if args.name.lower() in var.lower()], key=str.lower) #foundDepVars = foundDepAVars + foundDepPVars # Loop over both lists to print results if len(foundDepAVars) > 0: dpPrint(f"\nThe following {len(foundDepAVars)} iteration{'s' if len(foundDepAVars) > 1 else ''} of '" + args.name + "' has other vars that depend on it:", args.output) # print all dependencies where the name is a key, or the name is part of the key for key in foundDepAVars: if args.name.lower() in key.lower(): if args.name != key: dpPrint(f"{key} ->", args.output) for var in sorted(allVarDependencies[key], key=str.lower): dpPrint("\t" + var, args.output) if len(foundDepPVars) > 0: dpPrint(f"\nThe following {len(foundDepPVars)} iteration{'s' if len(foundDepPVars) > 1 else ''} of '" + args.name + "' has presets that depend on it:", args.output) # print all dependencies where the name is a key, or the name is part of the key for key in foundDepPVars: if args.name.lower() in key.lower(): if args.name != key: dpPrint(f"{key} ->", args.output) for var in sorted(allPresetDependencies[key], key=str.lower): dpPrint("\t" + var, args.output) if args.output: print(f"\nResults saved to: {args.output}")
 
I changed the wording of the result messages. Results:

PS C:\games\vam> python List_Deps.py -p . -n ellie_hair
The following var was checked for dependency in other vars:
ENIGMA.Ellie_hair_(santa_barbara_style).1.var


The following 1 iteration of 'ellie_hair' has other vars that depend on it:
ENIGMA.Ellie_hair_(santa_barbara_style).latest ->
SaiVaM.Drika.1.var

The following 1 iteration of 'ellie_hair' has presets that depend on it:
ENIGMA.Ellie_hair_(santa_barbara_style).1 ->
Atom/Person/Hair/Preset_Ellie-SB-Sim.vap
 
I'll try to attach the script. You know what to do with the .txt part. If you still let Windows hide extensions for known file types, my condolences...
 

Attachments

  • List_Deps.py.txt
    8.6 KB · Views: 0
Back
Top Bottom