solareclipseworkbench 2.0.0rc2__tar.gz → 2.0.0rc4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/PKG-INFO +5 -4
  2. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/README.md +4 -3
  3. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/pyproject.toml +1 -1
  4. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/camera.py +74 -45
  5. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/gui.py +1 -1
  6. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/scripts.py +2 -2
  7. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench.egg-info/PKG-INFO +5 -4
  8. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/LICENSE +0 -0
  9. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/setup.cfg +0 -0
  10. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/__init__.py +0 -0
  11. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/__main__.py +0 -0
  12. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/besselian_element_generator.py +0 -0
  13. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/commands.py +0 -0
  14. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/configuration.py +0 -0
  15. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/constants.py +0 -0
  16. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/deltat.csv +0 -0
  17. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/eclipse_besselian.csv +0 -0
  18. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/20240408.txt +0 -0
  19. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/20250329.txt +0 -0
  20. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/__init__.py +0 -0
  21. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/test.txt +0 -0
  22. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/testEOS1000D.txt +0 -0
  23. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/testEOS80D.txt +0 -0
  24. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/testEOSR.txt +0 -0
  25. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/testNikon.txt +0 -0
  26. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/testSony.txt +0 -0
  27. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/testVirtualCamera.txt +0 -0
  28. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/voice_prompts.txt +0 -0
  29. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/example_scripts/voice_prompts_basic.txt +0 -0
  30. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/exposure_calculator.py +0 -0
  31. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/hardware_problems.py +0 -0
  32. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/__init__.py +0 -0
  33. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/astrowimsara.jpg +0 -0
  34. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/calendar.png +0 -0
  35. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/camera.png +0 -0
  36. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/clock.png +0 -0
  37. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/datetime-format-popup.png +0 -0
  38. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/eclipse-date-popup.png +0 -0
  39. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/folder.png +0 -0
  40. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/live.png +0 -0
  41. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/location-popup.png +0 -0
  42. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/location.png +0 -0
  43. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/logo-small.jpg +0 -0
  44. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/logo-small.svg +0 -0
  45. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/logo.jpg +0 -0
  46. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/refresh.png +0 -0
  47. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/save.png +0 -0
  48. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/settings.png +0 -0
  49. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/simulation-popup.png +0 -0
  50. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/simulator.png +0 -0
  51. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/stop.png +0 -0
  52. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/toolbar.png +0 -0
  53. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/img/ui-top.png +0 -0
  54. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/location.py +0 -0
  55. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/location_ui.py +0 -0
  56. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/notifications.py +0 -0
  57. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/nutation.py +0 -0
  58. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/observer.py +0 -0
  59. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/phone_gps.py +0 -0
  60. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/qt_utils.py +0 -0
  61. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/reference_moments.py +0 -0
  62. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sew.py +0 -0
  63. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/solar_eclipse.py +0 -0
  64. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/1.wav +0 -0
  65. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/10.wav +0 -0
  66. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/15.wav +0 -0
  67. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/2.wav +0 -0
  68. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/20_seconds.wav +0 -0
  69. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/3.wav +0 -0
  70. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/30_seconds.wav +0 -0
  71. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/4.wav +0 -0
  72. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/40_seconds.wav +0 -0
  73. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/5.wav +0 -0
  74. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/__init__.py +0 -0
  75. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c1.wav +0 -0
  76. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c1_in_60_seconds.wav +0 -0
  77. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2.wav +0 -0
  78. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_10_minutes.wav +0 -0
  79. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_15_minutes.wav +0 -0
  80. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_20_minutes.wav +0 -0
  81. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_20_seconds.wav +0 -0
  82. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_25_minutes.wav +0 -0
  83. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_2_minutes.wav +0 -0
  84. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_30_minutes.wav +0 -0
  85. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_30_seconds.wav +0 -0
  86. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_40_minutes.wav +0 -0
  87. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_40_seconds.wav +0 -0
  88. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_4_minutes.wav +0 -0
  89. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_50_minutes.wav +0 -0
  90. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_5_minutes.wav +0 -0
  91. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_60_seconds.wav +0 -0
  92. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_6_minutes.wav +0 -0
  93. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_in_90_seconds.wav +0 -0
  94. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c2_plus_30_seconds.wav +0 -0
  95. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_in_20_seconds.wav +0 -0
  96. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_in_45_seconds.wav +0 -0
  97. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_in_8_seconds.wav +0 -0
  98. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_10_seconds.wav +0 -0
  99. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_15_seconds.wav +0 -0
  100. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_1_minute.wav +0 -0
  101. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_25_seconds.wav +0 -0
  102. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_2_minutes.wav +0 -0
  103. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_2_seconds.wav +0 -0
  104. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c3_plus_45_seconds.wav +0 -0
  105. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c4.wav +0 -0
  106. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/c4_in_60_seconds.wav +0 -0
  107. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/filters_on.wav +0 -0
  108. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/max.wav +0 -0
  109. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/sound/max_in_10_seconds.wav +0 -0
  110. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/usb_gps.py +0 -0
  111. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/utils.py +0 -0
  112. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/vec.py +0 -0
  113. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench/wizard.py +0 -0
  114. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench.egg-info/SOURCES.txt +0 -0
  115. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench.egg-info/dependency_links.txt +0 -0
  116. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench.egg-info/entry_points.txt +0 -0
  117. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench.egg-info/requires.txt +0 -0
  118. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/src/solareclipseworkbench.egg-info/top_level.txt +0 -0
  119. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/tests/test_hardware_problems.py +0 -0
  120. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/tests/test_live_view_preview_types.py +0 -0
  121. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/tests/test_solar_eclipse.py +0 -0
  122. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/tests/test_sony_burst.py +0 -0
  123. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/tests/test_sony_storageinfo.py +0 -0
  124. {solareclipseworkbench-2.0.0rc2 → solareclipseworkbench-2.0.0rc4}/tests/test_voice_prompt_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solareclipseworkbench
3
- Version: 2.0.0rc2
3
+ Version: 2.0.0rc4
4
4
  Summary: Tools to photograph solar eclipses
5
5
  Author-email: Sara Regibo <sararegibo@gmail.com>, Wim De Meester <deepskywim@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -133,7 +133,7 @@ pip install solareclipseworkbench
133
133
 
134
134
  ```bash
135
135
  sudo apt update
136
- sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.14-venv
136
+ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.14-venv gstreamer1.0-plugins-good
137
137
  ```
138
138
 
139
139
  - Install gphoto2 to be able to access the cameras by executing the following line in the terminal
@@ -185,7 +185,7 @@ pip install solareclipseworkbench
185
185
 
186
186
  ```bash
187
187
  sudo apt update
188
- sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.12-venv
188
+ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.12-venv gstreamer1.0-plugins-good
189
189
  ```
190
190
 
191
191
  - Install gphoto2 to be able to access the cameras by executing the following line in the terminal
@@ -818,6 +818,7 @@ endfor
818
818
  - The computer you are using will probably fall asleep during the solar eclipse. You can prevent this on macOS and Linux using [caffeine](https://www.caffeine-app.net/). On Windows, you can use the Windows [powertoys](https://awake.den.dev/).
819
819
  - Sony cameras after cold boot might return wrong configuration values. Please press `Camera(s)` button to retrieve them one more time.
820
820
  - Nikon cameras after fresh connection might take a long time to finish their first shot. A potential mitigation is ensuring that the SD card has no other shots on it already.
821
+ - Sony might set wrong shutter speed for some of the exposures when using `take_hdr` command. USB3 connection is prone to cause this more often than USB2 one. No solution has been devised yet.
821
822
 
822
823
  ## Converting scripts from Solar Eclipse Maestro
823
824
 
@@ -872,7 +873,7 @@ uv pip install PyObjC
872
873
  - Install uv by executing the following line in the terminal:
873
874
 
874
875
  ```bash
875
- sudo apt install curl git gstreamer1.0-plugins-base-apps
876
+ sudo apt install curl git gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-good
876
877
  curl -LsSf https://astral.sh/uv/install.sh | sh
877
878
  ```
878
879
 
@@ -102,7 +102,7 @@ pip install solareclipseworkbench
102
102
 
103
103
  ```bash
104
104
  sudo apt update
105
- sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.14-venv
105
+ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.14-venv gstreamer1.0-plugins-good
106
106
  ```
107
107
 
108
108
  - Install gphoto2 to be able to access the cameras by executing the following line in the terminal
@@ -154,7 +154,7 @@ pip install solareclipseworkbench
154
154
 
155
155
  ```bash
156
156
  sudo apt update
157
- sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.12-venv
157
+ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.12-venv gstreamer1.0-plugins-good
158
158
  ```
159
159
 
160
160
  - Install gphoto2 to be able to access the cameras by executing the following line in the terminal
@@ -787,6 +787,7 @@ endfor
787
787
  - The computer you are using will probably fall asleep during the solar eclipse. You can prevent this on macOS and Linux using [caffeine](https://www.caffeine-app.net/). On Windows, you can use the Windows [powertoys](https://awake.den.dev/).
788
788
  - Sony cameras after cold boot might return wrong configuration values. Please press `Camera(s)` button to retrieve them one more time.
789
789
  - Nikon cameras after fresh connection might take a long time to finish their first shot. A potential mitigation is ensuring that the SD card has no other shots on it already.
790
+ - Sony might set wrong shutter speed for some of the exposures when using `take_hdr` command. USB3 connection is prone to cause this more often than USB2 one. No solution has been devised yet.
790
791
 
791
792
  ## Converting scripts from Solar Eclipse Maestro
792
793
 
@@ -841,7 +842,7 @@ uv pip install PyObjC
841
842
  - Install uv by executing the following line in the terminal:
842
843
 
843
844
  ```bash
844
- sudo apt install curl git gstreamer1.0-plugins-base-apps
845
+ sudo apt install curl git gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-good
845
846
  curl -LsSf https://astral.sh/uv/install.sh | sh
846
847
  ```
847
848
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "solareclipseworkbench"
3
- version = "2.0.0rc2"
3
+ version = "2.0.0rc4"
4
4
 
5
5
  description = "Tools to photograph solar eclipses"
6
6
  readme = "README.md"
@@ -1080,9 +1080,9 @@ def _reset_canon_eos_ptp_session(camera) -> None:
1080
1080
 
1081
1081
  @_serialised_on_camera
1082
1082
  def take_picture(camera: Camera, camera_settings: CameraSettings) -> None:
1083
- """ Take a picture with the selected camera
1084
-
1085
- Args:
1083
+ """ Take a picture with the selected camera
1084
+
1085
+ Args:
1086
1086
  - camera_name: Camera object
1087
1087
  - camera_settings: Settings of the camera (exposure, f, iso)
1088
1088
  """
@@ -1113,6 +1113,8 @@ def take_picture(camera: Camera, camera_settings: CameraSettings) -> None:
1113
1113
  logging.exception('Virtual camera capture failed: %s', e)
1114
1114
  raise
1115
1115
 
1116
+ target = camera._camera if hasattr(camera, '_camera') else camera
1117
+
1116
1118
  # For Nikon cameras, defensively ensure single-frame mode is active before
1117
1119
  # taking a single picture, in case a previous take_burst left the camera in
1118
1120
  # continuous/burst mode.
@@ -1120,7 +1122,6 @@ def take_picture(camera: Camera, camera_settings: CameraSettings) -> None:
1120
1122
  # a single picture (separate set_config call so a bad capturemode value can
1121
1123
  # never poison the ISO/shutter batch).
1122
1124
  if vendor in ('Nikon', 'Sony'):
1123
- target = camera._camera if hasattr(camera, '_camera') else camera
1124
1125
  try:
1125
1126
  cfg2 = gp.check_result(gp.gp_camera_get_config(target, context))
1126
1127
  capture_mode_widget = gp.check_result(gp.gp_widget_get_child_by_name(cfg2, 'capturemode'))
@@ -1146,8 +1147,13 @@ def take_picture(camera: Camera, camera_settings: CameraSettings) -> None:
1146
1147
  else:
1147
1148
  logging.warning(
1148
1149
  '%s: could not ensure single-frame mode before take_picture: %s', camera_name, exc)
1149
- else:
1150
- target = camera._camera if hasattr(camera, '_camera') else camera
1150
+
1151
+ # Ensure that no lingering events are left before proceeding.
1152
+ # Sony in PC mode must be skipped, to avoid disrupting Background Downloader.
1153
+ if not (hasattr(camera, '_bg_downloader')
1154
+ and camera._bg_downloader is not None
1155
+ and camera._bg_downloader.is_alive()):
1156
+ _drain_camera_events(target, context)
1151
1157
 
1152
1158
  # Fire the shutter via trigger_capture (PTP InitiateCapture). This is the
1153
1159
  # only path that guarantees the camera uses the USB-programmed ISO, shutter
@@ -1270,7 +1276,7 @@ def __adapt_camera_settings(camera, camera_settings):
1270
1276
  # Always save to the camera's memory card, never to the computer.
1271
1277
  # This must be re-asserted on every shot because some cameras reset
1272
1278
  # capturetarget to 'Internal RAM' when the USB session is re-used.
1273
- # Skip Sony cameras, as this option is set in stone once USB is connected
1279
+ # Skip Sony cameras, as this option is set in stone once USB is connected.
1274
1280
  try:
1275
1281
  if not _is_sony_camera_instance(camera):
1276
1282
  capture_target_w = gp.check_result(gp.gp_widget_get_child_by_name(config, 'capturetarget'))
@@ -1385,6 +1391,15 @@ def take_burst(camera: Camera, camera_settings: CameraSettings, duration: float)
1385
1391
  logging.exception('Virtual burst capture failed')
1386
1392
  raise
1387
1393
 
1394
+ target = camera._camera if hasattr(camera, '_camera') else camera
1395
+
1396
+ # Ensure that no lingering events are left before proceeding.
1397
+ # Sony in PC mode must be skipped, to avoid disrupting Background Downloader.
1398
+ if not (hasattr(camera, '_bg_downloader')
1399
+ and camera._bg_downloader is not None
1400
+ and camera._bg_downloader.is_alive()):
1401
+ _drain_camera_events(target, context)
1402
+
1388
1403
  # Take picture for real cameras
1389
1404
  if vendor == 'Canon':
1390
1405
  # For bursts, ensure the camera is in a continuous drive mode so
@@ -1410,7 +1425,6 @@ def take_burst(camera: Camera, camera_settings: CameraSettings, duration: float)
1410
1425
  _set_gp_config(camera, config, context)
1411
1426
 
1412
1427
  # Ensure that the camera truly finished, so it won't affect next commands
1413
- target = camera._camera if hasattr(camera, '_camera') else camera
1414
1428
  _wait_for_capture_complete(target, context)
1415
1429
  _drain_camera_events(target, context)
1416
1430
  except gphoto2.GPhoto2Error:
@@ -1454,7 +1468,6 @@ def take_burst(camera: Camera, camera_settings: CameraSettings, duration: float)
1454
1468
  except Exception:
1455
1469
  logging.exception('%s: high-level capture failed, trying low-level gp capture', camera_name)
1456
1470
  try:
1457
- target = camera._camera if hasattr(camera, '_camera') else camera
1458
1471
  ctx = gp.gp_context_new()
1459
1472
  gp.check_result(gp.gp_camera_capture(target, gp.GP_CAPTURE_IMAGE, ctx))
1460
1473
  except Exception:
@@ -1471,8 +1484,6 @@ def take_burst(camera: Camera, camera_settings: CameraSettings, duration: float)
1471
1484
  # Sony burst: enable continuous capture mode, turn on the bulb,
1472
1485
  # sleep for the chosen amount of time, turn off the bulb and
1473
1486
  # lastly drain camera's events
1474
- target = camera._camera if hasattr(camera, '_camera') else camera
1475
-
1476
1487
  # Switch to continuous capture mode — look up the actual choice string so
1477
1488
  # we never send an invalid value that would cause set_config to fail.
1478
1489
  try:
@@ -1488,7 +1499,6 @@ def take_burst(camera: Camera, camera_settings: CameraSettings, duration: float)
1488
1499
  logging.warning('Could not set Sony capturemode to Continuous: %s', e)
1489
1500
 
1490
1501
  try:
1491
- _drain_camera_events(target, context)
1492
1502
  bulb_mode = gp.check_result(gp.gp_widget_get_child_by_name(config, 'bulb'))
1493
1503
  gp.gp_widget_set_value(bulb_mode, 1)
1494
1504
  _set_gp_config(camera, config, context)
@@ -1647,8 +1657,6 @@ def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) ->
1647
1657
  '%s: take_bracket 5-shot sequence: %s', camera_name, [choices[i] for i in indices],
1648
1658
  )
1649
1659
 
1650
- target = camera._camera if hasattr(camera, '_camera') else camera
1651
-
1652
1660
  for idx in indices:
1653
1661
  speed = choices[idx]
1654
1662
  try:
@@ -1690,6 +1698,15 @@ def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) ->
1690
1698
  if base_speed not in choices:
1691
1699
  base_speed = _find_closest_shutter_choice(ss_widget, base_speed)
1692
1700
 
1701
+ target = camera._camera if hasattr(camera, '_camera') else camera
1702
+
1703
+ # Ensure that no lingering events are left before proceeding.
1704
+ # Sony in PC mode must be skipped, to avoid disrupting Background Downloader.
1705
+ if not (hasattr(camera, '_bg_downloader')
1706
+ and camera._bg_downloader is not None
1707
+ and camera._bg_downloader.is_alive()):
1708
+ _drain_camera_events(target, context)
1709
+
1693
1710
  if vendor == 'Canon':
1694
1711
  try:
1695
1712
  # Turn on aeb
@@ -1708,7 +1725,6 @@ def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) ->
1708
1725
  except Exception:
1709
1726
  logging.exception('Bracket capture high-level failed, trying low-level gp capture')
1710
1727
  try:
1711
- target = camera._camera if hasattr(camera, '_camera') else camera
1712
1728
  ctx = gp.gp_context_new()
1713
1729
  gp.check_result(gp.gp_camera_capture(target, gp.GP_CAPTURE_IMAGE, ctx))
1714
1730
  except Exception:
@@ -1791,7 +1807,6 @@ def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) ->
1791
1807
  except Exception:
1792
1808
  logging.exception('%s: high-level capture failed, trying low-level gp capture', camera_name)
1793
1809
  try:
1794
- target = camera._camera if hasattr(camera, '_camera') else camera
1795
1810
  ctx = gp.gp_context_new()
1796
1811
  gp.check_result(gp.gp_camera_capture(target, gp.GP_CAPTURE_IMAGE, ctx))
1797
1812
  except Exception:
@@ -1816,7 +1831,6 @@ def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) ->
1816
1831
  # Sony bracketing: enable continuous bracketing mode, turn on the bulb,
1817
1832
  # sleep for the chosen amount of time, turn off the bulb and
1818
1833
  # lastly drain camera's events in case 'PC+Camera' mode was chosen.
1819
- target = camera._camera if hasattr(camera, '_camera') else camera
1820
1834
  ev_step, num_pictures = parse_bracketing_string(steps)
1821
1835
  bracket_duration = calculate_bracketing_duration(ev_step, num_pictures, base_speed)
1822
1836
 
@@ -1834,7 +1848,6 @@ def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) ->
1834
1848
  logging.warning('Could not set Sony capturemode to "%s": %s', steps, e)
1835
1849
 
1836
1850
  try:
1837
- _drain_camera_events(target, context)
1838
1851
  bulb_mode = gp.check_result(gp.gp_widget_get_child_by_name(config, 'bulb'))
1839
1852
  gp.gp_widget_set_value(bulb_mode, 1)
1840
1853
  _set_gp_config(camera, config, context)
@@ -1965,6 +1978,8 @@ def take_hdr(camera: Camera, camera_settings: CameraSettings, stops: int) -> Non
1965
1978
  logging.exception('Virtual camera HDR capture failed')
1966
1979
  raise
1967
1980
 
1981
+ target = camera._camera if hasattr(camera, '_camera') else camera
1982
+
1968
1983
  # For Nikon cameras, defensively ensure single-frame mode is active before
1969
1984
  # taking a single picture, in case a previous take_burst left the camera in
1970
1985
  # continuous/burst mode.
@@ -1972,7 +1987,6 @@ def take_hdr(camera: Camera, camera_settings: CameraSettings, stops: int) -> Non
1972
1987
  # a single picture (separate set_config call so a bad capturemode value can
1973
1988
  # never poison the ISO/shutter batch).
1974
1989
  if vendor in ('Nikon', 'Sony'):
1975
- target = camera._camera if hasattr(camera, '_camera') else camera
1976
1990
  try:
1977
1991
  cfg2 = gp.check_result(gp.gp_camera_get_config(target, context))
1978
1992
  capture_mode_widget = gp.check_result(gp.gp_widget_get_child_by_name(cfg2, 'capturemode'))
@@ -1998,11 +2012,6 @@ def take_hdr(camera: Camera, camera_settings: CameraSettings, stops: int) -> Non
1998
2012
  else:
1999
2013
  logging.warning(
2000
2014
  '%s: could not ensure single-frame mode before take_picture: %s', camera_name, exc)
2001
- else:
2002
- target = camera._camera if hasattr(camera, '_camera') else camera
2003
-
2004
- # Ensure that no lingering events are left before proceeding with the HDR sequence
2005
- _drain_camera_events(target, context)
2006
2015
 
2007
2016
  # Build the ordered shutter-speed list from this camera's actual capabilities
2008
2017
  choices = _get_shutter_speed_choices(config)
@@ -2043,15 +2052,35 @@ def take_hdr(camera: Camera, camera_settings: CameraSettings, stops: int) -> Non
2043
2052
  [choices[i] for i in indices]
2044
2053
  )
2045
2054
 
2055
+ # Ensure that no lingering events are left before proceeding.
2056
+ # Sony in PC mode must be skipped, to avoid disrupting Background Downloader.
2057
+ if not (hasattr(camera, '_bg_downloader')
2058
+ and camera._bg_downloader is not None
2059
+ and camera._bg_downloader.is_alive()):
2060
+ _drain_camera_events(target, context)
2061
+
2046
2062
  for idx in indices:
2047
2063
  speed = choices[idx]
2048
2064
  try:
2049
2065
  gp.gp_widget_set_value(ss_widget, speed)
2050
2066
  gp.gp_camera_set_config(target, config, context)
2051
2067
  gp.check_result(gp.gp_camera_trigger_capture(target, context))
2068
+ # Sony requires this hack. It allows avoiding
2069
+ # wrong shutter speed values being set or
2070
+ # outright skipped frames in the sequence.
2071
+ # Sadly, it's not fully solving this issue.
2072
+ if vendor == 'Sony':
2073
+ curr_speed = camera.get_config().get_child_by_name('shutterspeed').get_value()
2074
+ if curr_speed != speed:
2075
+ logging.warning('%s: take_hdr has failed to set the shutter speed to %s',
2076
+ camera_name,
2077
+ speed)
2078
+ speed = curr_speed
2079
+ speed_s = _parse_shutter_speed_seconds(speed)
2080
+ time.sleep(speed_s)
2052
2081
  logging.info('%s: take_hdr has triggered capture at %s', camera_name, speed)
2053
2082
  # First successful trigger_capture establishes eos_remotemode=15 on EOS R.
2054
- if getattr(camera, 'vendor', None) == 'Canon':
2083
+ if vendor == 'Canon':
2055
2084
  camera._first_capture_done = True
2056
2085
  except gphoto2.GPhoto2Error as e:
2057
2086
  logging.warning('%s: take_hdr has failed capture at speed %s: %s', camera_name, speed, e)
@@ -2129,11 +2158,11 @@ def get_cameras() -> list:
2129
2158
 
2130
2159
 
2131
2160
  def __get_address(camera_name: str) -> str:
2132
- """ Gets the address of the camera if the name is given
2133
-
2161
+ """ Gets the address of the camera if the name is given
2162
+
2134
2163
  Args:
2135
2164
  - camera_name: Name of the camera
2136
-
2165
+
2137
2166
  Returns: Address of the camera
2138
2167
  """
2139
2168
 
@@ -2150,9 +2179,9 @@ def __get_address(camera_name: str) -> str:
2150
2179
  def get_camera(camera_name: str):
2151
2180
  """ Returns the initialized camera object of the selected camera
2152
2181
 
2153
- Args:
2182
+ Args:
2154
2183
  - camera_name: Name of the camera
2155
-
2184
+
2156
2185
  Returns: Initialized camera object of the selected camera.
2157
2186
  """
2158
2187
 
@@ -2328,9 +2357,9 @@ def get_serial_number(camera: 'BaseCamera') -> Optional[str]:
2328
2357
 
2329
2358
 
2330
2359
  def get_free_space(camera: Camera) -> float:
2331
- """ Return the free space on the card of the selected camera
2332
-
2333
- Args:
2360
+ """ Return the free space on the card of the selected camera
2361
+
2362
+ Args:
2334
2363
  - camera: Camera object
2335
2364
 
2336
2365
  Returns: Free space on the card of the camera [GB]
@@ -2378,9 +2407,9 @@ def get_free_space(camera: Camera) -> float:
2378
2407
 
2379
2408
 
2380
2409
  def get_space(camera: Camera) -> float:
2381
- """ Return the size of the memory card of the selected camera
2382
-
2383
- Args:
2410
+ """ Return the size of the memory card of the selected camera
2411
+
2412
+ Args:
2384
2413
  - camera: Camera object
2385
2414
 
2386
2415
  Returns: Size of memory card of the camera [GB]
@@ -2426,8 +2455,8 @@ def get_space(camera: Camera) -> float:
2426
2455
 
2427
2456
  def get_shooting_mode(camera_name: str, camera: Camera) -> str:
2428
2457
  """ Return the shooting mode of the selected camera. Should be "Manual".
2429
-
2430
- Args:
2458
+
2459
+ Args:
2431
2460
  - camera: Camera object
2432
2461
 
2433
2462
  Returns: Shooting mode of the camera
@@ -2467,8 +2496,8 @@ def get_shooting_mode(camera_name: str, camera: Camera) -> str:
2467
2496
 
2468
2497
  def get_focus_mode(camera: Camera) -> str:
2469
2498
  """ Return the focus mode of the selected camera. Should be "Manual"
2470
-
2471
- Args:
2499
+
2500
+ Args:
2472
2501
  - camera: Camera object
2473
2502
 
2474
2503
  Returns: Focus mode of the camera
@@ -2492,9 +2521,9 @@ def get_focus_mode(camera: Camera) -> str:
2492
2521
 
2493
2522
 
2494
2523
  def get_battery_level(camera: Camera) -> str:
2495
- """ Return the battery level of the selected camera
2496
-
2497
- Args:
2524
+ """ Return the battery level of the selected camera
2525
+
2526
+ Args:
2498
2527
  - camera: Name of the camera
2499
2528
 
2500
2529
  Returns: Current battery level of the camera [%]
@@ -2532,7 +2561,7 @@ def get_battery_level(camera: Camera) -> str:
2532
2561
  def get_time(camera: Camera) -> str:
2533
2562
  """ Returns the current time of the selected camera
2534
2563
 
2535
- Args:
2564
+ Args:
2536
2565
  - camera: Camera object
2537
2566
 
2538
2567
  Returns: Current time of the camera
@@ -2943,7 +2972,7 @@ def main():
2943
2972
  # mirror_lock(camera_object, camera_settings)
2944
2973
 
2945
2974
  # take_picture(camera_object, camera_settings)
2946
-
2975
+
2947
2976
  time.sleep(1)
2948
2977
  camera_settings = CameraSettings(camera[0], "1/400", "6.3", 400)
2949
2978
  # take_bracket(camera_object, camera_settings, "+/- 1 2/3")
@@ -3372,7 +3372,7 @@ class QJobsTableView(QTableView):
3372
3372
  - row: Row index of the first job that will be executed next.
3373
3373
  """
3374
3374
 
3375
- index: QModelIndex = self.model().index(min(row + 5, self.model().rowCount(None) - 1), 0)
3375
+ index: QModelIndex = self.model().index(min(row + 3, self.model().rowCount(None) - 1), 0)
3376
3376
  self.setCurrentIndex(index)
3377
3377
 
3378
3378
  def do(self, actions):
@@ -229,7 +229,7 @@ def convert_script(filename, reference_moments) -> io.StringIO:
229
229
  for_parts = next(csv.reader([line], skipinitialspace=True))
230
230
  start = for_parts[1] if len(for_parts) > 1 else ""
231
231
  stop = for_parts[2] if len(for_parts) > 2 else ""
232
- interval = int(for_parts[3]) if len(for_parts) > 3 else 10
232
+ interval = for_parts[3] if len(for_parts) > 3 else 10
233
233
  start_delta = for_parts[4] if len(for_parts) > 4 else "0"
234
234
  stop_delta = for_parts[5] if len(for_parts) > 5 else "0"
235
235
 
@@ -268,7 +268,7 @@ def convert_script(filename, reference_moments) -> io.StringIO:
268
268
 
269
269
  output_file = convert_command(line, start, sign, time_delta, extra_comment, output_file)
270
270
 
271
- current_time = current_time + timedelta(seconds=interval)
271
+ current_time = current_time + timedelta(seconds=float(interval))
272
272
  iteration = iteration + 1
273
273
 
274
274
  line = input_file.readline()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solareclipseworkbench
3
- Version: 2.0.0rc2
3
+ Version: 2.0.0rc4
4
4
  Summary: Tools to photograph solar eclipses
5
5
  Author-email: Sara Regibo <sararegibo@gmail.com>, Wim De Meester <deepskywim@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -133,7 +133,7 @@ pip install solareclipseworkbench
133
133
 
134
134
  ```bash
135
135
  sudo apt update
136
- sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.14-venv
136
+ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.14-venv gstreamer1.0-plugins-good
137
137
  ```
138
138
 
139
139
  - Install gphoto2 to be able to access the cameras by executing the following line in the terminal
@@ -185,7 +185,7 @@ pip install solareclipseworkbench
185
185
 
186
186
  ```bash
187
187
  sudo apt update
188
- sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.12-venv
188
+ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-x11-0 libxcb-cursor0 libcairo2-dev python3.12-venv gstreamer1.0-plugins-good
189
189
  ```
190
190
 
191
191
  - Install gphoto2 to be able to access the cameras by executing the following line in the terminal
@@ -818,6 +818,7 @@ endfor
818
818
  - The computer you are using will probably fall asleep during the solar eclipse. You can prevent this on macOS and Linux using [caffeine](https://www.caffeine-app.net/). On Windows, you can use the Windows [powertoys](https://awake.den.dev/).
819
819
  - Sony cameras after cold boot might return wrong configuration values. Please press `Camera(s)` button to retrieve them one more time.
820
820
  - Nikon cameras after fresh connection might take a long time to finish their first shot. A potential mitigation is ensuring that the SD card has no other shots on it already.
821
+ - Sony might set wrong shutter speed for some of the exposures when using `take_hdr` command. USB3 connection is prone to cause this more often than USB2 one. No solution has been devised yet.
821
822
 
822
823
  ## Converting scripts from Solar Eclipse Maestro
823
824
 
@@ -872,7 +873,7 @@ uv pip install PyObjC
872
873
  - Install uv by executing the following line in the terminal:
873
874
 
874
875
  ```bash
875
- sudo apt install curl git gstreamer1.0-plugins-base-apps
876
+ sudo apt install curl git gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-good
876
877
  curl -LsSf https://astral.sh/uv/install.sh | sh
877
878
  ```
878
879