diff --git a/build_multi_version.py b/build_multi_version.py index 3113077..8a116f6 100644 --- a/build_multi_version.py +++ b/build_multi_version.py @@ -131,7 +131,27 @@ def copy_additional_files(): """Copy non-Python files that don't need obfuscation.""" print("\n=== Copying additional files ===") - # Copy config directory + # Ensure configs directory exists in obfuscated output + # PyArmor should copy it, but we verify and copy if needed + configs_src = Path('src/orisys/configs') + configs_dst = Path('dist_obfuscated/src/orisys/configs') + + if configs_src.exists(): + print(f"Ensuring configs directory is copied...") + if not configs_dst.exists(): + print(f"Copying configs directory from {configs_src} to {configs_dst}") + shutil.copytree(configs_src, configs_dst, dirs_exist_ok=True) + else: + # Verify JSON files are present + json_files_src = list(configs_src.glob('*.json')) + json_files_dst = list(configs_dst.glob('*.json')) + if len(json_files_dst) < len(json_files_src): + print(f"Updating configs directory (found {len(json_files_dst)}/{len(json_files_src)} JSON files)") + shutil.copytree(configs_src, configs_dst, dirs_exist_ok=True) + else: + print(f"Configs directory already present with {len(json_files_dst)} JSON files") + + # Copy config directory (if exists at root level) if os.path.exists('config'): print("Copying config directory...") shutil.copytree('config', 'dist_obfuscated/config', dirs_exist_ok=True) @@ -166,15 +186,32 @@ with open('requirements.txt', 'r') as f: # Find all packages including pyarmor_runtime packages = find_packages(where="src") -# Collect all package data including PyArmor runtime files +# Collect all package data including PyArmor runtime files and configs package_data = {{}} for pkg in packages: pkg_path = Path('src') / pkg.replace('.', '/') if pkg_path.exists(): - # Include all .pyd, .so, .dll files (PyArmor runtime) data_files = [] + + # Include all .pyd, .so, .dll files (PyArmor runtime) for pattern in ['*.pyd', '*.so', '*.dll', '*.dylib']: data_files.extend([str(f.relative_to(pkg_path)) for f in pkg_path.glob(pattern)]) + + # Include JSON files from configs directory (for orisys.configs package) + if pkg == 'orisys.configs': + # Include all JSON files in the configs package directory + for json_file in pkg_path.glob('*.json'): + rel_path = json_file.relative_to(pkg_path) + data_files.append(str(rel_path)) + + # For orisys package, include configs subdirectory JSON files + if pkg == 'orisys': + configs_dir = pkg_path / 'configs' + if configs_dir.exists() and configs_dir.is_dir(): + for json_file in configs_dir.glob('*.json'): + rel_path = json_file.relative_to(pkg_path) + data_files.append(str(rel_path)) + if data_files: package_data[pkg] = data_files @@ -205,6 +242,29 @@ setup( print("setup.py created successfully!") +def create_manifest_in(): + """Create MANIFEST.in to ensure JSON files in configs are included.""" + print("\n=== Creating MANIFEST.in ===") + + manifest_content = """# Include all JSON files in configs directory +include src/orisys/configs/*.json +recursive-include src/orisys/configs *.json + +# Include PyArmor runtime files +recursive-include src *.pyd +recursive-include src *.so +recursive-include src *.dll +recursive-include src *.dylib +""" + + manifest_path = Path('dist_obfuscated/MANIFEST.in') + with open(manifest_path, 'w') as f: + f.write(manifest_content) + + print("MANIFEST.in created successfully!") + print(manifest_content) + + def get_platform_tag(): """Get the platform tag for wheel naming.""" system = platform.system() @@ -347,9 +407,12 @@ def main(): print("\n❌ Build failed during obfuscation!") sys.exit(1) - # Step 3: Copy additional files + # Step 3: Copy additional files (including configs) copy_additional_files() + # Step 3.5: Create MANIFEST.in to ensure JSON files are included + create_manifest_in() + # Step 4 & 5: Build based on mode if build_mode == 'universal': # Build one wheel for all Python versions diff --git a/src/orisys/sensor.py b/src/orisys/sensor.py index 8cf3fa6..f831187 100644 --- a/src/orisys/sensor.py +++ b/src/orisys/sensor.py @@ -514,7 +514,20 @@ class Sensor: def _open_video_source(self, override, box): """Initialize video capture and calibration.""" - self.cap = cv2.VideoCapture(self.vid_src) + # Use DirectShow backend on Windows for cameras (more stable than MSMF) + if isinstance(self.vid_src, int): + # For camera indices, try DirectShow first (avoids MSMF errors) + try: + self.cap = cv2.VideoCapture(self.vid_src, cv2.CAP_DSHOW) + except: + # Fallback to default backend + self.cap = cv2.VideoCapture(self.vid_src) + else: + # For file paths, use default backend + self.cap = cv2.VideoCapture(self.vid_src) + + # Set buffer size to 1 to reduce latency and avoid frame accumulation + self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G')) self.cap.set(cv2.CAP_PROP_FPS, self._config_fps) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self._config_resolution_raw[0])