pymcap-cli 0.12.0__tar.gz → 0.14.0__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 (177) hide show
  1. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/PKG-INFO +131 -17
  2. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/README.md +122 -15
  3. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/pyproject.toml +12 -2
  4. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cli.py +4 -1
  5. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/_run_processor.py +21 -0
  6. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/bag2mcap_cmd.py +1 -0
  7. pymcap_cli-0.14.0/src/pymcap_cli/cmd/compress_cmd.py +152 -0
  8. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/convert_cmd.py +1 -0
  9. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/__init__.py +45 -0
  10. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/_helpers.py +456 -0
  11. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/duplicates_cmd.py +152 -0
  12. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/errors_cmd.py +163 -0
  13. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/info_cmd.py +350 -0
  14. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/migrate_cmd.py +73 -0
  15. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/query_cmd.py +287 -0
  16. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/scan_cmd.py +160 -0
  17. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/schemas_cmd.py +190 -0
  18. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/serve_cmd.py +195 -0
  19. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/sessions_cmd.py +169 -0
  20. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/status_cmd.py +267 -0
  21. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/timeline_cmd.py +127 -0
  22. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/topics_cmd.py +137 -0
  23. pymcap_cli-0.14.0/src/pymcap_cli/cmd/index/tree_cmd.py +423 -0
  24. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/info_cmd.py +39 -2
  25. pymcap_cli-0.14.0/src/pymcap_cli/cmd/msg/__init__.py +14 -0
  26. pymcap_cli-0.14.0/src/pymcap_cli/cmd/msg/def_cmd.py +66 -0
  27. pymcap_cli-0.14.0/src/pymcap_cli/cmd/msg/list_cmd.py +65 -0
  28. pymcap_cli-0.14.0/src/pymcap_cli/cmd/msg/serve_cmd.py +507 -0
  29. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/roscompress_cmd.py +3 -3
  30. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/split_cmd.py +5 -2
  31. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/mcap_processor.py +127 -14
  32. pymcap_cli-0.14.0/src/pymcap_cli/core/msg_resolver.py +719 -0
  33. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/always_decode.py +3 -1
  34. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/attachment_filter.py +4 -1
  35. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/boundary_split.py +6 -1
  36. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/channel_merge.py +5 -1
  37. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/chunk_groupers.py +4 -1
  38. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/dedup.py +5 -1
  39. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/duration_split.py +5 -1
  40. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/expression_split.py +6 -1
  41. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/latching.py +7 -1
  42. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/metadata_filter.py +4 -1
  43. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/nth_message.py +5 -1
  44. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/size_split.py +5 -0
  45. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/time_filter.py +6 -1
  46. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/time_offset.py +5 -1
  47. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/timestamp_split.py +3 -0
  48. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/topic_alias.py +7 -1
  49. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/topic_filter.py +4 -1
  50. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/topic_rewrite.py +4 -1
  51. pymcap_cli-0.14.0/src/pymcap_cli/core/qos.py +147 -0
  52. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/display_utils.py +97 -4
  53. pymcap_cli-0.14.0/src/pymcap_cli/display/schema_html.py +206 -0
  54. pymcap_cli-0.14.0/src/pymcap_cli/display/schema_render.py +187 -0
  55. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/doctor.py +76 -1
  56. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/http_utils.py +8 -3
  57. pymcap_cli-0.14.0/src/pymcap_cli/index/__init__.py +13 -0
  58. pymcap_cli-0.14.0/src/pymcap_cli/index/datasette/metadata.yaml +724 -0
  59. pymcap_cli-0.14.0/src/pymcap_cli/index/datasette/plugins/pymcap_render.py +166 -0
  60. pymcap_cli-0.14.0/src/pymcap_cli/index/datasette/templates/index.html +62 -0
  61. pymcap_cli-0.14.0/src/pymcap_cli/index/datasette/templates/query-index-timeline.html +26 -0
  62. pymcap_cli-0.14.0/src/pymcap_cli/index/db.py +130 -0
  63. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0002_normalise_and_drop_chunks.py +247 -0
  64. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0003_intern_channels.py +108 -0
  65. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0004_intern_compress_channel_metadata.py +125 -0
  66. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0005_content_compression.py +43 -0
  67. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0006_channel_statistics.py +35 -0
  68. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0007_consolidate_schema.py +357 -0
  69. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0008_rewrite_current_file_view.py +59 -0
  70. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/0009_read_side_covering_indexes.py +25 -0
  71. pymcap_cli-0.14.0/src/pymcap_cli/index/migrations/__init__.py +96 -0
  72. pymcap_cli-0.14.0/src/pymcap_cli/index/scanner.py +1368 -0
  73. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/index/summary_fingerprint.py +31 -14
  74. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/rihs01.py +8 -1
  75. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/info_data.py +64 -38
  76. pymcap_cli-0.14.0/src/pymcap_cli/types/qos.py +83 -0
  77. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/types_manual.py +24 -0
  78. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/utils.py +11 -2
  79. pymcap_cli-0.12.0/src/pymcap_cli/cmd/compress_cmd.py +0 -97
  80. pymcap_cli-0.12.0/src/pymcap_cli/cmd/index_cmd.py +0 -1066
  81. pymcap_cli-0.12.0/src/pymcap_cli/core/msg_resolver.py +0 -447
  82. pymcap_cli-0.12.0/src/pymcap_cli/index/db.py +0 -78
  83. pymcap_cli-0.12.0/src/pymcap_cli/index/migrations/__init__.py +0 -67
  84. pymcap_cli-0.12.0/src/pymcap_cli/index/scanner.py +0 -687
  85. pymcap_cli-0.12.0/src/pymcap_cli/types/__init__.py +0 -0
  86. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/__init__.py +0 -0
  87. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/__init__.py +0 -0
  88. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/_rechunk_strategy.py +0 -0
  89. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/_run_processor_multi.py +0 -0
  90. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/bridge/__init__.py +0 -0
  91. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/bridge/_shared.py +0 -0
  92. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/bridge/cat.py +0 -0
  93. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/bridge/inspect.py +0 -0
  94. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/bridge/record.py +0 -0
  95. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/cat_cmd.py +0 -0
  96. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/diag_cmd.py +0 -0
  97. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/diff_cmd.py +0 -0
  98. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/doctor_cmd.py +0 -0
  99. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/du_cmd.py +0 -0
  100. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/duplicates_cmd.py +0 -0
  101. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/export_csv_cmd.py +0 -0
  102. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/export_geo_cmd.py +0 -0
  103. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/export_images_cmd.py +0 -0
  104. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/export_json_cmd.py +0 -0
  105. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/export_parquet_cmd.py +0 -0
  106. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/export_pcd_cmd.py +0 -0
  107. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/filter_cmd.py +0 -0
  108. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/get_cmd.py +0 -0
  109. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/info_json_cmd.py +0 -0
  110. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/list_cmd.py +0 -0
  111. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/merge_cmd.py +0 -0
  112. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/plot_cmd.py +0 -0
  113. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/process_cmd.py +0 -0
  114. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/rechunk_cmd.py +0 -0
  115. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/records_cmd.py +0 -0
  116. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/recover_cmd.py +0 -0
  117. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/recover_inplace_cmd.py +0 -0
  118. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/rosdecompress_cmd.py +0 -0
  119. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/tf_export_cmd.py +0 -0
  120. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/tf_get_cmd.py +0 -0
  121. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/tftree_cmd.py +0 -0
  122. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/topic_chunks_cmd.py +0 -0
  123. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/cmd/video_cmd.py +0 -0
  124. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/constants.py +0 -0
  125. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/__init__.py +0 -0
  126. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/input_handler.py +0 -0
  127. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/input_options.py +0 -0
  128. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/input_processor_chain.py +0 -0
  129. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/mcap_compare.py +0 -0
  130. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/mcap_transform.py +0 -0
  131. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/ARCHITECTURE.md +0 -0
  132. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/__init__.py +0 -0
  133. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/base.py +0 -0
  134. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/processors/utils.py +0 -0
  135. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/tf_findings.py +0 -0
  136. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/core/tf_tree.py +0 -0
  137. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/debug_wrapper.py +0 -0
  138. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/__init__.py +0 -0
  139. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/cat_helpers.py +0 -0
  140. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/message_render.py +0 -0
  141. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/osc_utils.py +0 -0
  142. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/sparkline.py +0 -0
  143. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/display/time_ranges.py +0 -0
  144. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/encoding/__init__.py +0 -0
  145. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/encoding/arrow_schema.py +0 -0
  146. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/__init__.py +0 -0
  147. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/_common.py +0 -0
  148. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/base.py +0 -0
  149. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/csv_exporter.py +0 -0
  150. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/driver.py +0 -0
  151. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/geo_common.py +0 -0
  152. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/geojson_exporter.py +0 -0
  153. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/gpx_exporter.py +0 -0
  154. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/image_exporter.py +0 -0
  155. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/json_exporter.py +0 -0
  156. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/kml_exporter.py +0 -0
  157. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/parquet_exporter.py +0 -0
  158. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/pcd_exporter.py +0 -0
  159. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/plot_exporter.py +0 -0
  160. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/sdf_exporter.py +0 -0
  161. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/urdf_exporter.py +0 -0
  162. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/video_exporter.py +0 -0
  163. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/exporters/video_file_writer.py +0 -0
  164. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/index/fingerprint.py +0 -0
  165. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/index/migrations/0001.py +0 -0
  166. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/log_setup.py +0 -0
  167. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/py.typed +0 -0
  168. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/rosbag_reader/__init__.py +0 -0
  169. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/rosbag_reader/_reader.py +0 -0
  170. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/rosbag_reader/_types.py +0 -0
  171. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/rosbag_reader/py.typed +0 -0
  172. {pymcap_cli-0.12.0/src/pymcap_cli/index → pymcap_cli-0.14.0/src/pymcap_cli/types}/__init__.py +0 -0
  173. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/duration.py +0 -0
  174. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/info_link.py +0 -0
  175. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/info_types.py +0 -0
  176. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/size.py +0 -0
  177. {pymcap_cli-0.12.0 → pymcap_cli-0.14.0}/src/pymcap_cli/types/to_plain.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymcap-cli
3
- Version: 0.12.0
3
+ Version: 0.14.0
4
4
  Summary: High-performance Python CLI for MCAP file processing with advanced recovery, filtering, and optimization capabilities
5
5
  Keywords: mcap,cli,robotics,ros,ros2,recovery,filtering,compression
6
6
  Author: Marko Bausch
@@ -29,7 +29,7 @@ Requires-Dist: ros-parser
29
29
  Requires-Dist: platformdirs>=4.0.0
30
30
  Requires-Dist: pyyaml>=6.0
31
31
  Requires-Dist: typing-extensions>=4.15.0
32
- Requires-Dist: pymcap-cli[video,pointcloud,plot,parquet,image,draco,bridge,xxhash] ; extra == 'all'
32
+ Requires-Dist: pymcap-cli[video,pointcloud,plot,parquet,image,draco,bridge,xxhash,serve] ; extra == 'all'
33
33
  Requires-Dist: robo-ws-bridge ; extra == 'bridge'
34
34
  Requires-Dist: mcap-codec-support[draco] ; extra == 'draco'
35
35
  Requires-Dist: pillow>=10.0 ; extra == 'image'
@@ -39,6 +39,12 @@ Requires-Dist: numpy>=1.24.0 ; extra == 'parquet'
39
39
  Requires-Dist: mcap-codec-support[pointcloud] ; extra == 'parquet'
40
40
  Requires-Dist: plotly>=6.0.0 ; extra == 'plot'
41
41
  Requires-Dist: mcap-codec-support[pointcloud] ; extra == 'pointcloud'
42
+ Requires-Dist: datasette>=0.65.2,<1 ; extra == 'serve'
43
+ Requires-Dist: datasette-block-robots>=1.1,<2 ; extra == 'serve'
44
+ Requires-Dist: datasette-copyable>=0.3.2,<1 ; extra == 'serve'
45
+ Requires-Dist: datasette-dashboards>=0.8.0,<1 ; extra == 'serve'
46
+ Requires-Dist: datasette-export-notebook>=1.0.1,<2 ; extra == 'serve'
47
+ Requires-Dist: datasette-vega>=0.6.2,<1 ; extra == 'serve'
42
48
  Requires-Dist: mcap-codec-support[video] ; extra == 'video'
43
49
  Requires-Dist: xxhash>=3.0.0 ; extra == 'xxhash'
44
50
  Requires-Python: >=3.10
@@ -53,6 +59,7 @@ Provides-Extra: lite
53
59
  Provides-Extra: parquet
54
60
  Provides-Extra: plot
55
61
  Provides-Extra: pointcloud
62
+ Provides-Extra: serve
56
63
  Provides-Extra: video
57
64
  Provides-Extra: xxhash
58
65
  Description-Content-Type: text/markdown
@@ -76,8 +83,14 @@ uvx pymcap-cli info data.mcap
76
83
  # Or add to your project
77
84
  uv add pymcap-cli
78
85
 
79
- # With video support (for video and roscompress commands)
80
- uv add pymcap-cli[video]
86
+ # With video support (for video generation and ROS image compression)
87
+ uv add "pymcap-cli[video]"
88
+
89
+ # With ROS image and point-cloud compression support
90
+ uv add "pymcap-cli[video,pointcloud]"
91
+
92
+ # Add Draco point-cloud compression support
93
+ uv add "pymcap-cli[video,pointcloud,draco]"
81
94
  ```
82
95
 
83
96
  ## Why pymcap-cli over the official Go CLI?
@@ -158,6 +171,16 @@ pymcap-cli cat recording.mcap --bytes base64 # base64-encoded
158
171
  pymcap-cli cat recording.mcap --bytes skip # omit binary fields
159
172
  ```
160
173
 
174
+ ### `doctor` — MCAP Container Validation
175
+
176
+ Check an MCAP file structure against the MCAP container specification, with
177
+ summary, index, chunk, message-order, and advisory findings.
178
+
179
+ ```bash
180
+ pymcap-cli doctor data.mcap
181
+ pymcap-cli doctor data.mcap --strict-message-order --show-all
182
+ ```
183
+
161
184
  ### `tftree` — TF Transform Tree
162
185
 
163
186
  Visualize the ROS TF transform tree with colored static/dynamic transforms, translation and rotation values.
@@ -176,6 +199,16 @@ pymcap-cli tftree data.mcap
176
199
  pymcap-cli tftree data.mcap --static-only
177
200
  ```
178
201
 
202
+ ### `tf-get` — TF Transform Lookup
203
+
204
+ Resolve the transform from a source frame into a target frame using `/tf_static`
205
+ and `/tf`. Without `--at`, dynamic edges use their latest sample.
206
+
207
+ ```bash
208
+ pymcap-cli tf-get data.mcap map base_link
209
+ pymcap-cli tf-get data.mcap odom base_link --at 2024-01-01T10:00:00Z
210
+ ```
211
+
179
212
  ### `tf-export` — TF Tree to URDF / SDF / JSON
180
213
 
181
214
  Reconstruct robot description files from `/tf_static` (and optionally `/tf` at a
@@ -376,7 +409,7 @@ Filter messages by topic patterns (simpler version of `process`).
376
409
 
377
410
  ```bash
378
411
  # Include specific topics
379
- pymcap-cli filter data.mcap -o filtered.mcap --include-topics "/camera/image" "/lidar/points"
412
+ pymcap-cli filter data.mcap -o filtered.mcap --topics "/camera/image" "/lidar/points"
380
413
 
381
414
  # Exclude topics
382
415
  pymcap-cli filter data.mcap -o filtered.mcap --exclude-topics "/debug.*" "/test.*"
@@ -389,6 +422,13 @@ Change MCAP file compression.
389
422
  ```bash
390
423
  pymcap-cli compress input.mcap -o output.mcap --compression zstd
391
424
  pymcap-cli compress input.mcap -o output.mcap --compression lz4
425
+
426
+ # Compress in place: write to a temp file, validate it, then replace the source
427
+ pymcap-cli compress input.mcap --in-place --compression zstd
428
+
429
+ # Trade a little ratio for throughput: --fast (zstd fast mode), or pick a level
430
+ pymcap-cli compress input.mcap -o output.mcap --fast
431
+ pymcap-cli compress input.mcap -o output.mcap --compression-level -5
392
432
  ```
393
433
 
394
434
  ### `du` — Disk Usage Analysis
@@ -407,10 +447,32 @@ List various record types in an MCAP file.
407
447
  pymcap-cli list channels data.mcap
408
448
  pymcap-cli list chunks data.mcap
409
449
  pymcap-cli list schemas data.mcap
450
+ pymcap-cli list schema data.mcap --name sensor_msgs/msg/Image
410
451
  pymcap-cli list attachments data.mcap
411
452
  pymcap-cli list metadata data.mcap
412
453
  ```
413
454
 
455
+ ### `msg` — ROS2 Message Definitions
456
+
457
+ Resolve, list, and browse ROS2 `.msg` definitions. `msg def` prints complete
458
+ definitions including dependencies; `msg list` lists package message types; and
459
+ `msg serve` starts a local browser UI.
460
+
461
+ ```bash
462
+ # Resolve a standard ROS2 message
463
+ pymcap-cli msg def sensor_msgs/msg/Image --distro humble
464
+
465
+ # Include custom package roots before AMENT_PREFIX_PATH and the user cache
466
+ pymcap-cli msg def my_robot_msgs/msg/Status -I ./install/share
467
+
468
+ # List messages in a package or browse definitions locally
469
+ pymcap-cli msg list sensor_msgs --distro jazzy
470
+ pymcap-cli msg serve --distro jazzy --no-browser
471
+ ```
472
+
473
+ Missing standard packages are resolved from rosdistro/GitHub and cached under
474
+ the `pymcap_cli_msg_def` user cache.
475
+
414
476
  ### `get` — Extract Attachments and Metadata
415
477
 
416
478
  Extract a single attachment's bytes or a metadata record's key/value map.
@@ -460,6 +522,38 @@ pymcap-cli duplicates /data/recordings --all
460
522
  pymcap-cli duplicates /data/recordings --rebuild-missing
461
523
  ```
462
524
 
525
+ ### `index` — Sidecar Catalog
526
+
527
+ Maintain a sidecar SQLite catalog of MCAP summaries for fast lookup across
528
+ large recording trees. Requires the `xxhash` extra.
529
+
530
+ ```bash
531
+ # Scan a tree and skip unchanged files on later runs
532
+ pymcap-cli index scan /data/recordings
533
+
534
+ # Coverage and directory-level rollups
535
+ pymcap-cli index status /data/recordings
536
+ pymcap-cli index tree /data/recordings --max-depth 3
537
+
538
+ # Query by topic/schema/time and inspect catalog-wide topics
539
+ pymcap-cli index query /data/recordings --topic /camera/front --format json
540
+ pymcap-cli index topics /camera --sort-by messages
541
+
542
+ # Apply pending schema migrations to an existing catalog
543
+ pymcap-cli index migrate
544
+
545
+ # Browse the catalog in a local Datasette web UI (dashboards, charts, cross-links)
546
+ pymcap-cli index serve
547
+ ```
548
+
549
+ `index serve` launches [Datasette](https://datasette.io/) against the sidecar DB
550
+ with bundled dashboards, canned queries, and a render plugin. It needs the
551
+ `serve` extra (`pip install 'pymcap-cli[serve]'`; in this workspace, use
552
+ `uv run --package pymcap-cli --extra serve pymcap-cli index serve`). Datasette
553
+ runs from the same environment so the plugin resolves. Use `--db` to point at a
554
+ non-default catalog, `--port` to change the port (default 8001), and
555
+ `--no-browser` to skip auto-open.
556
+
463
557
  ### `records` — Raw Record Dump
464
558
 
465
559
  Print every MCAP record in file order using its `repr`. Useful for inspecting
@@ -480,22 +574,26 @@ pymcap-cli topic-chunks data.mcap
480
574
 
481
575
  ### `video` — Video Generation
482
576
 
483
- Generate MP4 videos from image topics using hardware-accelerated encoding. Requires the `video` extra.
577
+ Generate one MP4 per image topic using hardware-accelerated encoding. Requires
578
+ the `video` extra.
484
579
 
485
580
  ```bash
486
581
  # Basic video generation
487
- pymcap-cli video data.mcap --topic /camera/front --output front.mp4
582
+ pymcap-cli video data.mcap --topic /camera/front --output ./videos
488
583
 
489
584
  # With quality preset
490
- pymcap-cli video data.mcap --topic /camera/rear --output rear.mp4 --quality high
585
+ pymcap-cli video data.mcap --topic /camera/rear --output ./videos --quality high
491
586
 
492
587
  # Use specific codec and encoder
493
- pymcap-cli video data.mcap --topic /lidar/image --output lidar.mp4 --codec h265 --encoder videotoolbox
588
+ pymcap-cli video data.mcap --topic /lidar/image --output ./videos --codec h265 --encoder videotoolbox
494
589
  ```
495
590
 
496
- ### `roscompress` — ROS Image Compression
591
+ ### `roscompress` — ROS Image and Point-Cloud Compression
497
592
 
498
- Compress ROS MCAP files by converting CompressedImage/Image topics to CompressedVideo format and PointCloud2 topics to Cloudini or Draco compressed point clouds.
593
+ Compress ROS MCAP files by converting CompressedImage/Image topics to
594
+ CompressedVideo format and PointCloud2 topics to Cloudini or Draco compressed
595
+ point clouds. Requires the `video` and `pointcloud` extras; Draco compression
596
+ also requires the `draco` extra.
499
597
 
500
598
  ```bash
501
599
  # Basic compression
@@ -510,7 +608,7 @@ pymcap-cli roscompress data.mcap -o compressed.mcap --pc-format draco --pc-schem
510
608
 
511
609
  ### `rosdecompress` — ROS Decompression
512
610
 
513
- Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats.
611
+ Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats. Requires the `video` and `pointcloud` extras.
514
612
 
515
613
  ```bash
516
614
  # Decompress to CompressedImage (JPEG)
@@ -605,6 +703,22 @@ pymcap-cli export-geo data.mcap -o ./geo --format gpx --mode track --stride 5
605
703
  pymcap-cli export-geo data.mcap -o ./geo --include-no-fix
606
704
  ```
607
705
 
706
+ ### `bridge` — Live Foxglove Bridge
707
+
708
+ Inspect, stream, or record live topics from a Foxglove WebSocket bridge. Requires
709
+ the `bridge` extra.
710
+
711
+ ```bash
712
+ # Inspect advertised channels
713
+ pymcap-cli bridge localhost:8765
714
+
715
+ # Stream decoded messages
716
+ pymcap-cli bridge cat localhost:8765 --topics /tf --limit 10
717
+
718
+ # Record all advertised topics to MCAP
719
+ pymcap-cli bridge record localhost:8765 --all -o live.mcap
720
+ ```
721
+
608
722
  ### Shell Autocompletion
609
723
 
610
724
  ```bash
@@ -631,9 +745,9 @@ pymcap-cli process full_log.mcap -o camera.mcap \
631
745
  # Recover corrupt file and compress in one pass
632
746
  pymcap-cli process corrupt.mcap -o recovered.mcap --recovery-mode --compression lz4
633
747
 
634
- # Fast filtering with chunk copying (up to 10x faster)
748
+ # Fast filtering with automatic chunk copying when possible
635
749
  pymcap-cli process 100gb_file.mcap -o filtered.mcap \
636
- -y "/lidar.*" --chunk-copying --compression zstd
750
+ -y "/lidar.*" --compression zstd
637
751
 
638
752
  # Optimize for topic-specific playback
639
753
  pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
@@ -653,14 +767,14 @@ pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
653
767
 
654
768
  ```bash
655
769
  # Setup development environment
656
- uv sync
770
+ uv sync --all-groups --all-extras --all-packages
657
771
 
658
772
  # Run locally during development
659
773
  uv run pymcap-cli --help
660
774
 
661
775
  # Format and lint code
662
- uv run pre-commit run --all-files
776
+ pre-commit run --all-files
663
777
 
664
778
  # Run tests
665
- uv run pytest pymcap-cli/tests
779
+ uv run pytest pymcap-cli/tests -m "not benchmark" --no-cov -q
666
780
  ```
@@ -17,8 +17,14 @@ uvx pymcap-cli info data.mcap
17
17
  # Or add to your project
18
18
  uv add pymcap-cli
19
19
 
20
- # With video support (for video and roscompress commands)
21
- uv add pymcap-cli[video]
20
+ # With video support (for video generation and ROS image compression)
21
+ uv add "pymcap-cli[video]"
22
+
23
+ # With ROS image and point-cloud compression support
24
+ uv add "pymcap-cli[video,pointcloud]"
25
+
26
+ # Add Draco point-cloud compression support
27
+ uv add "pymcap-cli[video,pointcloud,draco]"
22
28
  ```
23
29
 
24
30
  ## Why pymcap-cli over the official Go CLI?
@@ -99,6 +105,16 @@ pymcap-cli cat recording.mcap --bytes base64 # base64-encoded
99
105
  pymcap-cli cat recording.mcap --bytes skip # omit binary fields
100
106
  ```
101
107
 
108
+ ### `doctor` — MCAP Container Validation
109
+
110
+ Check an MCAP file structure against the MCAP container specification, with
111
+ summary, index, chunk, message-order, and advisory findings.
112
+
113
+ ```bash
114
+ pymcap-cli doctor data.mcap
115
+ pymcap-cli doctor data.mcap --strict-message-order --show-all
116
+ ```
117
+
102
118
  ### `tftree` — TF Transform Tree
103
119
 
104
120
  Visualize the ROS TF transform tree with colored static/dynamic transforms, translation and rotation values.
@@ -117,6 +133,16 @@ pymcap-cli tftree data.mcap
117
133
  pymcap-cli tftree data.mcap --static-only
118
134
  ```
119
135
 
136
+ ### `tf-get` — TF Transform Lookup
137
+
138
+ Resolve the transform from a source frame into a target frame using `/tf_static`
139
+ and `/tf`. Without `--at`, dynamic edges use their latest sample.
140
+
141
+ ```bash
142
+ pymcap-cli tf-get data.mcap map base_link
143
+ pymcap-cli tf-get data.mcap odom base_link --at 2024-01-01T10:00:00Z
144
+ ```
145
+
120
146
  ### `tf-export` — TF Tree to URDF / SDF / JSON
121
147
 
122
148
  Reconstruct robot description files from `/tf_static` (and optionally `/tf` at a
@@ -317,7 +343,7 @@ Filter messages by topic patterns (simpler version of `process`).
317
343
 
318
344
  ```bash
319
345
  # Include specific topics
320
- pymcap-cli filter data.mcap -o filtered.mcap --include-topics "/camera/image" "/lidar/points"
346
+ pymcap-cli filter data.mcap -o filtered.mcap --topics "/camera/image" "/lidar/points"
321
347
 
322
348
  # Exclude topics
323
349
  pymcap-cli filter data.mcap -o filtered.mcap --exclude-topics "/debug.*" "/test.*"
@@ -330,6 +356,13 @@ Change MCAP file compression.
330
356
  ```bash
331
357
  pymcap-cli compress input.mcap -o output.mcap --compression zstd
332
358
  pymcap-cli compress input.mcap -o output.mcap --compression lz4
359
+
360
+ # Compress in place: write to a temp file, validate it, then replace the source
361
+ pymcap-cli compress input.mcap --in-place --compression zstd
362
+
363
+ # Trade a little ratio for throughput: --fast (zstd fast mode), or pick a level
364
+ pymcap-cli compress input.mcap -o output.mcap --fast
365
+ pymcap-cli compress input.mcap -o output.mcap --compression-level -5
333
366
  ```
334
367
 
335
368
  ### `du` — Disk Usage Analysis
@@ -348,10 +381,32 @@ List various record types in an MCAP file.
348
381
  pymcap-cli list channels data.mcap
349
382
  pymcap-cli list chunks data.mcap
350
383
  pymcap-cli list schemas data.mcap
384
+ pymcap-cli list schema data.mcap --name sensor_msgs/msg/Image
351
385
  pymcap-cli list attachments data.mcap
352
386
  pymcap-cli list metadata data.mcap
353
387
  ```
354
388
 
389
+ ### `msg` — ROS2 Message Definitions
390
+
391
+ Resolve, list, and browse ROS2 `.msg` definitions. `msg def` prints complete
392
+ definitions including dependencies; `msg list` lists package message types; and
393
+ `msg serve` starts a local browser UI.
394
+
395
+ ```bash
396
+ # Resolve a standard ROS2 message
397
+ pymcap-cli msg def sensor_msgs/msg/Image --distro humble
398
+
399
+ # Include custom package roots before AMENT_PREFIX_PATH and the user cache
400
+ pymcap-cli msg def my_robot_msgs/msg/Status -I ./install/share
401
+
402
+ # List messages in a package or browse definitions locally
403
+ pymcap-cli msg list sensor_msgs --distro jazzy
404
+ pymcap-cli msg serve --distro jazzy --no-browser
405
+ ```
406
+
407
+ Missing standard packages are resolved from rosdistro/GitHub and cached under
408
+ the `pymcap_cli_msg_def` user cache.
409
+
355
410
  ### `get` — Extract Attachments and Metadata
356
411
 
357
412
  Extract a single attachment's bytes or a metadata record's key/value map.
@@ -401,6 +456,38 @@ pymcap-cli duplicates /data/recordings --all
401
456
  pymcap-cli duplicates /data/recordings --rebuild-missing
402
457
  ```
403
458
 
459
+ ### `index` — Sidecar Catalog
460
+
461
+ Maintain a sidecar SQLite catalog of MCAP summaries for fast lookup across
462
+ large recording trees. Requires the `xxhash` extra.
463
+
464
+ ```bash
465
+ # Scan a tree and skip unchanged files on later runs
466
+ pymcap-cli index scan /data/recordings
467
+
468
+ # Coverage and directory-level rollups
469
+ pymcap-cli index status /data/recordings
470
+ pymcap-cli index tree /data/recordings --max-depth 3
471
+
472
+ # Query by topic/schema/time and inspect catalog-wide topics
473
+ pymcap-cli index query /data/recordings --topic /camera/front --format json
474
+ pymcap-cli index topics /camera --sort-by messages
475
+
476
+ # Apply pending schema migrations to an existing catalog
477
+ pymcap-cli index migrate
478
+
479
+ # Browse the catalog in a local Datasette web UI (dashboards, charts, cross-links)
480
+ pymcap-cli index serve
481
+ ```
482
+
483
+ `index serve` launches [Datasette](https://datasette.io/) against the sidecar DB
484
+ with bundled dashboards, canned queries, and a render plugin. It needs the
485
+ `serve` extra (`pip install 'pymcap-cli[serve]'`; in this workspace, use
486
+ `uv run --package pymcap-cli --extra serve pymcap-cli index serve`). Datasette
487
+ runs from the same environment so the plugin resolves. Use `--db` to point at a
488
+ non-default catalog, `--port` to change the port (default 8001), and
489
+ `--no-browser` to skip auto-open.
490
+
404
491
  ### `records` — Raw Record Dump
405
492
 
406
493
  Print every MCAP record in file order using its `repr`. Useful for inspecting
@@ -421,22 +508,26 @@ pymcap-cli topic-chunks data.mcap
421
508
 
422
509
  ### `video` — Video Generation
423
510
 
424
- Generate MP4 videos from image topics using hardware-accelerated encoding. Requires the `video` extra.
511
+ Generate one MP4 per image topic using hardware-accelerated encoding. Requires
512
+ the `video` extra.
425
513
 
426
514
  ```bash
427
515
  # Basic video generation
428
- pymcap-cli video data.mcap --topic /camera/front --output front.mp4
516
+ pymcap-cli video data.mcap --topic /camera/front --output ./videos
429
517
 
430
518
  # With quality preset
431
- pymcap-cli video data.mcap --topic /camera/rear --output rear.mp4 --quality high
519
+ pymcap-cli video data.mcap --topic /camera/rear --output ./videos --quality high
432
520
 
433
521
  # Use specific codec and encoder
434
- pymcap-cli video data.mcap --topic /lidar/image --output lidar.mp4 --codec h265 --encoder videotoolbox
522
+ pymcap-cli video data.mcap --topic /lidar/image --output ./videos --codec h265 --encoder videotoolbox
435
523
  ```
436
524
 
437
- ### `roscompress` — ROS Image Compression
525
+ ### `roscompress` — ROS Image and Point-Cloud Compression
438
526
 
439
- Compress ROS MCAP files by converting CompressedImage/Image topics to CompressedVideo format and PointCloud2 topics to Cloudini or Draco compressed point clouds.
527
+ Compress ROS MCAP files by converting CompressedImage/Image topics to
528
+ CompressedVideo format and PointCloud2 topics to Cloudini or Draco compressed
529
+ point clouds. Requires the `video` and `pointcloud` extras; Draco compression
530
+ also requires the `draco` extra.
440
531
 
441
532
  ```bash
442
533
  # Basic compression
@@ -451,7 +542,7 @@ pymcap-cli roscompress data.mcap -o compressed.mcap --pc-format draco --pc-schem
451
542
 
452
543
  ### `rosdecompress` — ROS Decompression
453
544
 
454
- Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats.
545
+ Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats. Requires the `video` and `pointcloud` extras.
455
546
 
456
547
  ```bash
457
548
  # Decompress to CompressedImage (JPEG)
@@ -546,6 +637,22 @@ pymcap-cli export-geo data.mcap -o ./geo --format gpx --mode track --stride 5
546
637
  pymcap-cli export-geo data.mcap -o ./geo --include-no-fix
547
638
  ```
548
639
 
640
+ ### `bridge` — Live Foxglove Bridge
641
+
642
+ Inspect, stream, or record live topics from a Foxglove WebSocket bridge. Requires
643
+ the `bridge` extra.
644
+
645
+ ```bash
646
+ # Inspect advertised channels
647
+ pymcap-cli bridge localhost:8765
648
+
649
+ # Stream decoded messages
650
+ pymcap-cli bridge cat localhost:8765 --topics /tf --limit 10
651
+
652
+ # Record all advertised topics to MCAP
653
+ pymcap-cli bridge record localhost:8765 --all -o live.mcap
654
+ ```
655
+
549
656
  ### Shell Autocompletion
550
657
 
551
658
  ```bash
@@ -572,9 +679,9 @@ pymcap-cli process full_log.mcap -o camera.mcap \
572
679
  # Recover corrupt file and compress in one pass
573
680
  pymcap-cli process corrupt.mcap -o recovered.mcap --recovery-mode --compression lz4
574
681
 
575
- # Fast filtering with chunk copying (up to 10x faster)
682
+ # Fast filtering with automatic chunk copying when possible
576
683
  pymcap-cli process 100gb_file.mcap -o filtered.mcap \
577
- -y "/lidar.*" --chunk-copying --compression zstd
684
+ -y "/lidar.*" --compression zstd
578
685
 
579
686
  # Optimize for topic-specific playback
580
687
  pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
@@ -594,14 +701,14 @@ pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
594
701
 
595
702
  ```bash
596
703
  # Setup development environment
597
- uv sync
704
+ uv sync --all-groups --all-extras --all-packages
598
705
 
599
706
  # Run locally during development
600
707
  uv run pymcap-cli --help
601
708
 
602
709
  # Format and lint code
603
- uv run pre-commit run --all-files
710
+ pre-commit run --all-files
604
711
 
605
712
  # Run tests
606
- uv run pytest pymcap-cli/tests
713
+ uv run pytest pymcap-cli/tests -m "not benchmark" --no-cov -q
607
714
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pymcap-cli"
3
- version = "0.12.0"
3
+ version = "0.14.0"
4
4
  description = "High-performance Python CLI for MCAP file processing with advanced recovery, filtering, and optimization capabilities"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -46,7 +46,7 @@ dependencies = [
46
46
  ]
47
47
 
48
48
  [project.optional-dependencies]
49
- all = ["pymcap-cli[video,pointcloud,plot,parquet,image,draco,bridge,xxhash]"]
49
+ all = ["pymcap-cli[video,pointcloud,plot,parquet,image,draco,bridge,xxhash,serve]"]
50
50
  lite = ["pymcap-cli[image,draco,bridge,xxhash]"]
51
51
  bridge = ["robo-ws-bridge"]
52
52
  draco = ["mcap-codec-support[draco]"]
@@ -56,6 +56,16 @@ plot = ["plotly>=6.0.0"]
56
56
  pointcloud = ["mcap-codec-support[pointcloud]"]
57
57
  video = ["mcap-codec-support[video]"]
58
58
  xxhash = ["xxhash>=3.0.0"]
59
+ # `index serve` web UI. datasette<1 pin: datasette-dashboards 500s on the 1.0
60
+ # pre-release. Run from the same env so the bundled pymcap_render plugin imports.
61
+ serve = [
62
+ "datasette>=0.65.2,<1",
63
+ "datasette-block-robots>=1.1,<2",
64
+ "datasette-copyable>=0.3.2,<1",
65
+ "datasette-dashboards>=0.8.0,<1",
66
+ "datasette-export-notebook>=1.0.1,<2",
67
+ "datasette-vega>=0.6.2,<1",
68
+ ]
59
69
 
60
70
 
61
71
  [project.urls]
@@ -37,6 +37,7 @@ from pymcap_cli.cmd import (
37
37
  tftree_cmd,
38
38
  topic_chunks_cmd,
39
39
  )
40
+ from pymcap_cli.cmd import msg as msg_pkg
40
41
  from pymcap_cli.log_setup import ERR, setup_logging
41
42
 
42
43
  CommandFunction: TypeAlias = Callable[..., int]
@@ -194,7 +195,7 @@ rosdecompress = _load_optional_command(
194
195
  install_command="uv add 'pymcap-cli[video,pointcloud]'",
195
196
  )
196
197
  index_app = _load_optional_app(
197
- "pymcap_cli.cmd.index_cmd",
198
+ "pymcap_cli.cmd.index",
198
199
  "index_app",
199
200
  expected_missing_modules=("xxhash",),
200
201
  message="Index command requires the 'xxhash' extra "
@@ -225,6 +226,8 @@ app.command(name="diff", group=inspect_group)(diff_cmd.diff_cmd)
225
226
  app.command(name="duplicates", group=inspect_group)(duplicates_cmd.duplicates)
226
227
  app.command(name="info", group=inspect_group)(info_cmd.info)
227
228
  app.command(name="info-json", group=inspect_group)(info_json_cmd.info_json)
229
+ msg_pkg.msg_app.group = (inspect_group,)
230
+ app.command(msg_pkg.msg_app, name="msg")
228
231
  index_app.group = (inspect_group,)
229
232
  app.command(index_app, name="index")
230
233
  get_cmd.get_app.group = (inspect_group,)
@@ -126,6 +126,27 @@ def delete_source_files(sources: list[str], outputs: list[Path]) -> None:
126
126
  logger.exception(f"Failed to delete '{src}'")
127
127
 
128
128
 
129
+ def in_place_temp_path(source: Path) -> Path:
130
+ """Temp output path next to ``source`` so the final rename stays on one filesystem."""
131
+ return source.with_name(source.name + ".tmp")
132
+
133
+
134
+ def finalize_replace_source(*, source: Path, tmp_output: Path) -> int:
135
+ """Validate ``tmp_output`` and atomically replace ``source`` with it.
136
+
137
+ Returns 0 on success and 1 if validation failed (source is preserved and
138
+ the temp file is removed).
139
+ """
140
+ if not validate_mcap_output(tmp_output):
141
+ logger.error(f"[red]Output failed validation: {tmp_output}[/red]")
142
+ logger.error("Source file preserved — output not safe to replace source.")
143
+ tmp_output.unlink(missing_ok=True)
144
+ return 1
145
+ tmp_output.replace(source)
146
+ logger.info(f"Replaced source: {source}")
147
+ return 0
148
+
149
+
129
150
  def finalize_delete_source(
130
151
  *,
131
152
  sources: list[str],
@@ -37,6 +37,7 @@ class Bag2McapOptions:
37
37
  compression: WriterCompression = DEFAULT_COMPRESSION
38
38
  enable_crcs: bool = True
39
39
  use_chunking: bool = True
40
+ zstd_level: int | None = None
40
41
 
41
42
 
42
43
  @dataclass