Spaces:
Runtime error
Runtime error
File size: 15,649 Bytes
a522962 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
import json
def update_scratch_project_data(project_data):
"""
Updates variable and broadcast definitions in a Scratch project JSON,
populating the 'variables' and 'broadcasts' sections of the Stage target
and extracting initial values for variables.
Args:
project_data (dict): The loaded JSON data of the Scratch project.
Returns:
dict: The updated project JSON data.
"""
stage_target = None
for target in project_data['targets']:
if target.get('isStage'):
stage_target = target
break
if stage_target is None:
print("Error: Stage target not found in the project data.")
return project_data
# Ensure 'variables' and 'broadcasts' exist in the Stage target
if "variables" not in stage_target:
stage_target["variables"] = {}
if "broadcasts" not in stage_target:
stage_target["broadcasts"] = {}
# Helper function to recursively find and update variable/broadcast fields
def process_dict(obj):
if isinstance(obj, dict):
# Check for "data_setvariableto" opcode to extract initial values
if obj.get("opcode") == "data_setvariableto":
variable_field = obj.get("fields", {}).get("VARIABLE")
value_input = obj.get("inputs", {}).get("VALUE")
if variable_field and isinstance(variable_field, list) and len(variable_field) == 2:
var_name = variable_field[0]
var_id = variable_field[1]
initial_value = ""
if value_input and isinstance(value_input, list) and len(value_input) > 1 and \
isinstance(value_input[1], list) and len(value_input[1]) > 1:
# Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]]
if value_input[1][0] == 10: # Direct value like [10, "0"]
initial_value = str(value_input[1][1])
elif value_input[1][0] == 12 and len(value_input) > 2 and isinstance(value_input[2], list) and value_input[2][0] == 10: # Variable reference with initial value block
initial_value = str(value_input[2][1])
elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs
initial_value = str(value_input[1])
# Add/update the variable in the Stage's 'variables' with its initial value
stage_target["variables"][var_id] = [var_name, initial_value]
for key, value in obj.items():
# Process variable definitions in 'fields' (for blocks that define variables like 'show variable')
if key == "VARIABLE" and isinstance(value, list) and len(value) == 2:
var_name = value[0]
var_id = value[1]
# Only add if not already defined with an initial value from set_variableto
if var_id not in stage_target["variables"]:
stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
stage_target["variables"][var_id][0] = var_name
# Process broadcast definitions in 'inputs' (BROADCAST_INPUT)
elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \
isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11:
broadcast_name = value[1][1]
broadcast_id = value[1][2]
# Add/update the broadcast in the Stage's 'broadcasts'
stage_target["broadcasts"][broadcast_id] = broadcast_name
# Process broadcast definitions in 'fields' (BROADCAST_OPTION)
elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2:
broadcast_name = value[0]
broadcast_id = value[1]
# Add/update the broadcast in the Stage's 'broadcasts'
stage_target["broadcasts"][broadcast_id] = broadcast_name
# Recursively call for nested dictionaries or lists
process_dict(value)
elif isinstance(obj, list):
for i, item in enumerate(obj):
# Process variable references in 'inputs' (like [12, "score", "id"])
if isinstance(item, list) and len(item) == 3 and item[0] == 12:
var_name = item[1]
var_id = item[2]
# Only add if not already defined with an initial value from set_variableto
if var_id not in stage_target["variables"]:
stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
stage_target["variables"][var_id][0] = var_name
process_dict(item)
# Iterate through all targets to process their blocks
for target in project_data['targets']:
if "blocks" in target:
for block_id, block_data in target["blocks"].items():
process_dict(block_data)
return project_data
def deduplicate_variables(project_data):
"""
Removes duplicate variable entries in the 'variables' dictionary of the Stage target,
prioritizing entries with non-empty values.
Args:
project_data (dict): The loaded JSON data of the Scratch project.
Returns:
dict: The updated project JSON data with deduplicated variables.
"""
stage_target = None
for target in project_data['targets']:
if target.get('isStage'):
stage_target = target
break
if stage_target is None:
print("Error: Stage target not found in the project data.")
return project_data
if "variables" not in stage_target:
return project_data # No variables to deduplicate
# Use a temporary dictionary to store the preferred variable entry by name
# Format: {variable_name: [variable_id, variable_name, variable_value]}
resolved_variables = {}
for var_id, var_info in stage_target["variables"].items():
var_name = var_info[0]
var_value = var_info[1]
if var_name not in resolved_variables:
# If the variable name is not yet seen, add it
resolved_variables[var_name] = [var_id, var_name, var_value]
else:
# If the variable name is already seen, decide which one to keep
existing_id, existing_name, existing_value = resolved_variables[var_name]
# Prioritize the entry with a non-empty value
if var_value != "" and existing_value == "":
resolved_variables[var_name] = [var_id, var_name, var_value]
# If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent)
# The current logic will effectively keep the last one encountered that has a value,
# or the very last one if all are empty.
elif var_value != "" and existing_value != "":
# If there are multiple non-empty values for the same variable name
# this keeps the one from the most recent iteration.
# For the given example, this will correctly keep "5".
resolved_variables[var_name] = [var_id, var_name, var_value]
elif var_value == "" and existing_value == "":
# If both are empty, just keep the current one (arbitrary)
resolved_variables[var_name] = [var_id, var_name, var_value]
# Reconstruct the 'variables' dictionary using the resolved entries
new_variables_dict = {}
for var_name, var_data in resolved_variables.items():
var_id_to_keep = var_data[0]
var_name_to_keep = var_data[1]
var_value_to_keep = var_data[2]
new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep]
stage_target["variables"] = new_variables_dict
return project_data
# Example usage with your provided JSON:
if __name__ == "__main__":
json_data = {
"targets": [
{"isStage": True, "name": "Stage", "variables": {},"lists": {}, "broadcasts": {}, "blocks": {
"event_whenflagclicked_1": {
"opcode": "event_whenflagclicked",
"inputs": {},
"fields": {},
"shadow": False,
"topLevel": True,
"parent": None,
"next": "data_setvariableto_1"
},
"data_setvariableto_1": {
"opcode": "data_setvariableto",
"inputs": {
"VALUE": [
1,
[
4,
"0"
]
]
},
"fields": {
"VARIABLE": [
"score",
"$ELgBAKpb[&l+DX$EMK4"
]
},
"shadow": False,
"topLevel": False,
"parent": "event_whenflagclicked_1",
"next": "data_setvariableto_2"
},
"data_setvariableto_2": {
"opcode": "data_setvariableto",
"inputs": {
"VALUE": [
1,
[
4,
"3"
]
]
},
"fields": {
"VARIABLE": [
"lives",
"Gb]1jA+H{h_6z1^Fn!-a"
]
},
"shadow": False,
"topLevel": False,
"parent": "data_setvariableto_1",
"next": "data_showvariable_1"
},
"data_showvariable_1": {
"opcode": "data_showvariable",
"inputs": {},
"fields": {
"VARIABLE": [
"score",
"$ELgBAKpb[&l+DX$EMK4"
]
},
"shadow": False,
"topLevel": False,
"parent": "data_setvariableto_2",
"next": "data_showvariable_2"
},
"data_showvariable_2": {
"opcode": "data_showvariable",
"inputs": {},
"fields": {
"VARIABLE": [
"lives",
"Gb]1jA+H{h_6z1^Fn!-a"
]
},
"shadow": False,
"topLevel": False,
"parent": "data_showvariable_1",
"next": "event_broadcast_1"
},
"event_broadcast_1": {
"opcode": "event_broadcast",
"inputs": {
"BROADCAST_INPUT": [
1,
[
11,
"Game Start",
"hN1d@^S}e)H~8(qp)rGN"
]
]
},
"fields": {},
"shadow": False,
"topLevel": False,
"parent": "data_showvariable_2",
"next": None
}
}, "comments": {}, "currentCostume": 1, "costumes": [{"name": "backdrop1", "dataFormat": "svg", "assetId": "cd21514d0531fdffb22204e0ec5ed84a", "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", "rotationCenterX": 240, "rotationCenterY": 180}, {"name": "Blue Sky", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "e7c147730f19d284bcd7b3f00af19bb6", "rotationCenterX": 240, "rotationCenterY": 180}], "sounds": [{"name": "pop", "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", "dataFormat": "wav", "format": "", "rate": 48000, "sampleCount": 1123, "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav"}], "volume": 100, "layerOrder": 0, "tempo": 60, "videoTransparency": 50, "videoState": "on", "textToSpeechLanguage": None},
{"isStage": False, "name": "Sprite1", "variables": {}, "lists": {}, "broadcasts": {}, "blocks": {}, "comments": {}, "currentCostume": 0, "costumes": [{"name": "costume1", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "bcf454acf82e4504149f7ffe07081dbc", "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", "rotationCenterX": 48, "rotationCenterY": 50}, {"name": "costume2", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "0fb9be3e8397c983338cb71dc84d0b25", "md5ext": "0fb9be3e8397c983338cb71dc84d0b25.svg", "rotationCenterX": 46, "rotationCenterY": 53}], "sounds": [{"name": "Meow", "assetId": "83c36d806dc92327b9e7049a565c6bff", "dataFormat": "wav", "format": "", "rate": 48000, "sampleCount": 40681, "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav"}], "volume": 100, "layerOrder": 1, "visible": True, "x": 0, "y": -120, "size": 100, "direction": 90, "draggable": False, "rotationStyle": "all around"},
{"isStage": False, "name": "Soccer Ball", "variables": {}, "lists": {}, "broadcasts": {}, "blocks": {}, "comments": {}, "currentCostume": 0, "costumes": [{"name": "soccer ball", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", "rotationCenterX": 23, "rotationCenterY": 22}], "sounds": [{"name": "basketball bounce", "assetId": "1727f65b5f22d151685b8e5917456a60", "dataFormat": "wav", "format": "adpcm", "rate": 22050, "sampleCount": 8129, "md5ext": "1727f65b5f22d151685b8e5917456a60.wav"}], "volume": 100, "layerOrder": 2, "visible": True, "x": -130, "y": -60, "size": 100, "direction": 90, "draggable": False, "rotationStyle": "all around"}], "monitors": [{"id": "76*2udMupx@8!=)9Uqvj", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "score"}, "spriteName": None, "value": "0", "width": 0, "height": 0, "x": 5, "y": 5, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "YiV;2%+lgjnJ$|*Jy.{H", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "cloud Var"}, "spriteName": None, "value": 0, "width": 0, "height": 0, "x": 5, "y": 32, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "@D/}fdt{0XaJ`kRE0t~F", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "lives"}, "spriteName": None, "value": "3", "width": 0, "height": 0, "x": 5, "y": 59, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "^R,uz7yRjtK`=uP~6SN4", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "speed"}, "spriteName": None, "value": "5", "width": 0, "height": 0, "x": 5, "y": 86, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}], "extensions": [], "meta": {"semver": "3.0.0", "vm": "11.3.0", "agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"}}
updated_json_data = update_scratch_project_data(json_data)
updated_json_data = deduplicate_variables(json_data)
print(json.dumps(updated_json_data, indent=1)) |