Skip to content

Instantly share code, notes, and snippets.

@AGulev
Last active March 27, 2025 04:37
Show Gist options
  • Save AGulev/6562494806be79d96f1579888dc09519 to your computer and use it in GitHub Desktop.
Save AGulev/6562494806be79d96f1579888dc09519 to your computer and use it in GitHub Desktop.
Python script for removing unused PNG files from the Defold project
import os, sys, hashlib, stat
import deftree, configparser
PROJECT_FILE = "game.project"
def all_files(ending):
# Generator to get files
for root, folders, files in os.walk(project_root):
for f in files:
if f.endswith(ending):
yield os.path.join(root, f)
def get_attribute(root, attribute_name):
# To simplify getting all images in an atlas
for x in root.iter():
if deftree.is_element(x):
get_attribute(x, attribute_name)
else:
if x.name == attribute_name:
yield x
def image_in_file(atlas):
# Returns all images in an atlas
tree = deftree.parse(atlas)
root = tree.get_root()
images = []
for x in get_attribute(root, "image"):
images.append(x)
return images
def image_in_cubemap(cubemap):
# Returns all images in a cubemap
tree = deftree.parse(cubemap)
root = tree.get_root()
images = []
for child in root.iter():
if str(child).endswith(".png\""):
images.append(child)
return images
def unused_png_remover(project_root):
output = []
files_with_images = {}
config = configparser.ConfigParser()
config.read(os.path.join(project_root, PROJECT_FILE))
config_images = []
for key in config:
for value in config[key]:
if config[key][value].endswith(".png"):
config_images.append(config[key][value])
files_with_images[PROJECT_FILE] = config_images
for x in all_files(".tilesource"):
name = os.path.basename(x)
files_with_images[name] = image_in_file(x)
for x in all_files(".atlas"):
name = os.path.basename(x)
files_with_images[name] = image_in_file(x)
for x in all_files(".cubemap"):
name = os.path.basename(x)
files_with_images[name] = image_in_cubemap(x)
# Iterate over all images and remove unused
for x in all_files(".png"):
count = 0
img = "/" + x.replace(project_root, "").replace("\\", "/")
for file in files_with_images:
if img in files_with_images[file]:
count += 1
if count == 0:
path = project_root + img
output.append({"file": img, "size": os.stat(path)[stat.ST_SIZE]})
os.remove(path)
return output
def empty_folder_cleaner(project_root):
counter = 0
remove_dir = True
while remove_dir:
remove_dir = False
for dir, subdirs, files in os.walk(project_root, topdown=False):
if not subdirs and not files: #check whether the directory is now empty after deletions, and if so, remove it
print("Folder {} removed".format(dir))
os.rmdir(dir)
counter = counter + 1
remove_dir = True
return counter
def sizeof_fmt(num, suffix):
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
if len(sys.argv) > 1:
project_root = sys.argv[1]
if os.path.exists(project_root):
# Adds all atlases and its images to a dictionary
output = unused_png_remover(project_root)
#empty folders remove
count_of_folders = empty_folder_cleaner(project_root)
#Print log
total_size = 0
for x in output:
print("File {} removed. {}".format(x["file"], sizeof_fmt(x["size"], "B")))
total_size = total_size + x["size"]
print("{} files and {} folders was removed. {} ".format(len(output), count_of_folders, sizeof_fmt(total_size, "B")))
else:
print('Usage: python remove_unused_png.py project_folder')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment