From 4238b0a5fa306199992825fd934cd15d053dcac4 Mon Sep 17 00:00:00 2001 From: Jackson Date: Fri, 27 Aug 2021 17:19:39 -0500 Subject: [PATCH 1/4] repair project files with invalid json for paths --- src/classes/project_data.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/classes/project_data.py b/src/classes/project_data.py index 244ea7b8ac..793c86f8af 100644 --- a/src/classes/project_data.py +++ b/src/classes/project_data.py @@ -346,6 +346,7 @@ def load(self, file_path, clear_thumbnails=True): default_project = self._data try: + self.quote_json_keys(file_path) # Attempt to load v2.X project file project_data = self.read_from_file(file_path, path_mode="absolute") @@ -647,7 +648,7 @@ def read_legacy_project_file(self, file_path): # Increment track counter track_counter += 1 - except Exception: + except Exception as ex: # Error parsing legacy contents msg = "Failed to load legacy project file %(path)s" % {"path": file_path} log.error(msg, exc_info=1) @@ -720,6 +721,21 @@ def upgrade_project_data_structures(self): if self._data.get("id") == "T0": self._data["id"] = self.generate_id() + # Ensure projects can open + # after PR #4211 introduced a bug + # which wrote certain projects with invalid JSON. + def quote_json_keys(self, file_path): + import re + + f = open(file_path) + txt = f.read() + f.close() + newText = re.sub('(\n\s*)(\w*):', r'\1"\2":', txt) + f = open(file_path, 'w') + f.write(newText) + f.close() + return + def save(self, file_path, move_temp_files=True, make_paths_relative=True): """ Save project file to disk """ import openshot From 09951ec1cbe5198a7fcfe6c12c5e3c37335b0ede Mon Sep 17 00:00:00 2001 From: Jackson Date: Fri, 27 Aug 2021 19:11:38 -0500 Subject: [PATCH 2/4] Moved regex to read file --- src/classes/json_data.py | 5 +++++ src/classes/project_data.py | 16 ---------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/classes/json_data.py b/src/classes/json_data.py index f71655613b..caf75c0248 100644 --- a/src/classes/json_data.py +++ b/src/classes/json_data.py @@ -153,12 +153,17 @@ def read_from_file(self, file_path, path_mode="ignore"): contents = f.read() if not contents: raise RuntimeError("Couldn't load {} file, no data.".format(self.data_type)) + + if os.path.splitext(file_path) and os.path.splitext(file_path)[1] == '.osp': + # Repair lost quotes on json keys + contents = re.sub('(\n\s*)(\w*):', r'\1"\2":', contents) # Scan for and correct possible OpenShot 2.5.0 corruption if self.damage_re.search(contents) and self.version_re.search(contents): # File contains corruptions, backup and repair self.make_repair_backup(file_path, contents) + # Repair lost slashes, then fix all corrupted escapes contents = self.slash_repair_re.sub(r'\1/\2', contents) contents, subs_count = self.damage_re.subn(r'\\u\1', contents) diff --git a/src/classes/project_data.py b/src/classes/project_data.py index 793c86f8af..0b7157199b 100644 --- a/src/classes/project_data.py +++ b/src/classes/project_data.py @@ -346,7 +346,6 @@ def load(self, file_path, clear_thumbnails=True): default_project = self._data try: - self.quote_json_keys(file_path) # Attempt to load v2.X project file project_data = self.read_from_file(file_path, path_mode="absolute") @@ -721,21 +720,6 @@ def upgrade_project_data_structures(self): if self._data.get("id") == "T0": self._data["id"] = self.generate_id() - # Ensure projects can open - # after PR #4211 introduced a bug - # which wrote certain projects with invalid JSON. - def quote_json_keys(self, file_path): - import re - - f = open(file_path) - txt = f.read() - f.close() - newText = re.sub('(\n\s*)(\w*):', r'\1"\2":', txt) - f = open(file_path, 'w') - f.write(newText) - f.close() - return - def save(self, file_path, move_temp_files=True, make_paths_relative=True): """ Save project file to disk """ import openshot From 861a2b8574344a1e47d22ecf3b7c226c95df49e9 Mon Sep 17 00:00:00 2001 From: Jackson Date: Fri, 27 Aug 2021 20:14:01 -0500 Subject: [PATCH 3/4] Remove blank line --- src/classes/json_data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/classes/json_data.py b/src/classes/json_data.py index caf75c0248..a4ee168dcc 100644 --- a/src/classes/json_data.py +++ b/src/classes/json_data.py @@ -163,7 +163,6 @@ def read_from_file(self, file_path, path_mode="ignore"): # File contains corruptions, backup and repair self.make_repair_backup(file_path, contents) - # Repair lost slashes, then fix all corrupted escapes contents = self.slash_repair_re.sub(r'\1/\2', contents) contents, subs_count = self.damage_re.subn(r'\\u\1', contents) From 357244986c4725cfa3b7c175244fea8c61d4c340 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 28 Aug 2021 12:21:42 -0500 Subject: [PATCH 4/4] Creating a backup of possible 2.6.0 windows drive letter corruption, compiling regex, and updating original file during the project open. --- src/classes/json_data.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/classes/json_data.py b/src/classes/json_data.py index a4ee168dcc..7d1b6bf838 100644 --- a/src/classes/json_data.py +++ b/src/classes/json_data.py @@ -54,12 +54,16 @@ def __init__(self): self._data = {} # Private data store, accessible through the get and set methods self.data_type = "json data" - # Regular expression for project files with possible corruption - self.version_re = re.compile(r'"openshot-qt".*"2.5.0') + # Regular expressions for file version detection of possible corruption + self.version_re_250 = re.compile(r'"openshot-qt".*"2.5.0') + self.version_re_260 = re.compile(r'"openshot-qt".*"2.6.0') # Regular expression matching likely corruption in project files self.damage_re = re.compile(r'/u([0-9a-fA-F]{4})') + # Regular expression matching likely windows drive letter corruption in project files + self.damage_re_windows_drives = re.compile(r'(\n\s*)(\w*):') + # Regular expression used to detect lost slashes, when repairing data self.slash_repair_re = re.compile(r'(["/][.]+)(/u[0-9a-fA-F]{4})') @@ -153,13 +157,28 @@ def read_from_file(self, file_path, path_mode="ignore"): contents = f.read() if not contents: raise RuntimeError("Couldn't load {} file, no data.".format(self.data_type)) - - if os.path.splitext(file_path) and os.path.splitext(file_path)[1] == '.osp': - # Repair lost quotes on json keys - contents = re.sub('(\n\s*)(\w*):', r'\1"\2":', contents) + + # Scan for and correct possible OpenShot 2.6.0 corruption + if self.damage_re_windows_drives.search(contents) and self.version_re_260.search(contents): + # File contains corruptions, backup and repair + self.make_repair_backup(file_path, contents) + + # Repair lost quotes + contents = self.damage_re_windows_drives.sub(r'\1"\2":', contents) + + # We have to de- and re-serialize the data, to complete repairs + temp_data = json.loads(contents) + contents = json.dumps(temp_data, ensure_ascii=False, indent=1) + + # Save the repaired data back to the original file + with open(file_path, "w", encoding="utf-8") as fout: + fout.write(contents) + + msg_log = "Repaired windows drive corruptions in file {}" + log.info(msg_log.format(file_path)) # Scan for and correct possible OpenShot 2.5.0 corruption - if self.damage_re.search(contents) and self.version_re.search(contents): + if self.damage_re.search(contents) and self.version_re_250.search(contents): # File contains corruptions, backup and repair self.make_repair_backup(file_path, contents) @@ -174,19 +193,13 @@ def read_from_file(self, file_path, path_mode="ignore"): # We have to de- and re-serialize the data, to complete repairs temp_data = json.loads(contents) contents = json.dumps(temp_data, ensure_ascii=False, indent=1) - temp_data = {} # Save the repaired data back to the original file with open(file_path, "w", encoding="utf-8") as fout: fout.write(contents) msg_log = "Repaired {} corruptions in file {}" - msg_local = self._("Repaired {num} corruptions in file {path}") log.info(msg_log.format(subs_count, file_path)) - if hasattr(self.app, "window") and hasattr(self.app.window, "statusBar"): - self.app.window.statusBar.showMessage( - msg_local.format(num=subs_count, path=file_path), 5000 - ) # Process JSON data if path_mode == "absolute":