opensportslib 0.3.1.dev2__tar.gz → 0.3.1.dev3__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 (194) hide show
  1. {opensportslib-0.3.1.dev2/opensportslib.egg-info → opensportslib-0.3.1.dev3}/PKG-INFO +14 -1
  2. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/README.md +13 -0
  3. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/tools/hf_transfer.py +135 -20
  4. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3/opensportslib.egg-info}/PKG-INFO +14 -1
  5. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/pyproject.toml +1 -1
  6. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_hf_transfer_tools.py +147 -0
  7. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/LICENSE +0 -0
  8. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/LICENSE-COMMERCIAL +0 -0
  9. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/MANIFEST.in +0 -0
  10. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/examples/quickstart/basic_classification.py +0 -0
  11. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/examples/quickstart/basic_localization.py +0 -0
  12. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/examples/quickstart/basic_vqa.py +0 -0
  13. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/__init__.py +0 -0
  14. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/adaptation/__init__.py +0 -0
  15. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/adaptation/spotta.py +0 -0
  16. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/apis/__init__.py +0 -0
  17. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/apis/base_task_model.py +0 -0
  18. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/apis/classification.py +0 -0
  19. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/apis/localization.py +0 -0
  20. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/apis/vqa.py +0 -0
  21. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/cli.py +0 -0
  22. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/classification/default.yaml +0 -0
  23. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
  24. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
  25. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/classification/video.yaml +0 -0
  26. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/default.yaml +0 -0
  27. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
  28. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/default.yaml +0 -0
  29. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
  30. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
  31. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
  32. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
  33. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
  34. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/video_dali.yaml +0 -0
  35. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/localization/video_ocv.yaml +0 -0
  36. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/vqa/default.yaml +0 -0
  37. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/vqa/qwen.yaml +0 -0
  38. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
  39. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
  40. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
  41. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/configs/vqa/xvars.yaml +0 -0
  42. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/__init__.py +0 -0
  43. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/__init__.py +0 -0
  44. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/accessors.py +0 -0
  45. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/conflicts.py +0 -0
  46. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/loader.py +0 -0
  47. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/migrate.py +0 -0
  48. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/migrations/__init__.py +0 -0
  49. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
  50. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/runtime_adapter.py +0 -0
  51. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/schema.py +0 -0
  52. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/schemas/__init__.py +0 -0
  53. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
  54. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
  55. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/config/validate.py +0 -0
  56. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/loss/__init__.py +0 -0
  57. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/loss/builder.py +0 -0
  58. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/loss/calf.py +0 -0
  59. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/loss/ce.py +0 -0
  60. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/loss/combine.py +0 -0
  61. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/loss/nll.py +0 -0
  62. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/optimizer/__init__.py +0 -0
  63. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/optimizer/builder.py +0 -0
  64. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/sampler/weighted_sampler.py +0 -0
  65. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/scheduler/__init__.py +0 -0
  66. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/scheduler/builder.py +0 -0
  67. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/trainer/__init__.py +0 -0
  68. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/trainer/classification_trainer.py +0 -0
  69. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/trainer/localization_trainer.py +0 -0
  70. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/trainer/vqa_trainer.py +0 -0
  71. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/checkpoint.py +0 -0
  72. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/config.py +0 -0
  73. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/config_normalize.py +0 -0
  74. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/data.py +0 -0
  75. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/ddp.py +0 -0
  76. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/default_args.py +0 -0
  77. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/hf_runtime.py +0 -0
  78. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/lightning.py +0 -0
  79. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/load_annotations.py +0 -0
  80. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/seed.py +0 -0
  81. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/video_processing.py +0 -0
  82. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/core/utils/wandb.py +0 -0
  83. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/__init__.py +0 -0
  84. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/builder.py +0 -0
  85. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/classification_dataset.py +0 -0
  86. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/localization_dataset.py +0 -0
  87. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/utils/__init__.py +0 -0
  88. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/utils/h5_tracking.py +0 -0
  89. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/utils/tracking.py +0 -0
  90. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/datasets/vqa_dataset.py +0 -0
  91. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/classification.yaml +0 -0
  92. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
  93. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
  94. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
  95. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/localization.yaml +0 -0
  96. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
  97. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
  98. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/metrics/classification_metric.py +0 -0
  99. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/metrics/localization_metric.py +0 -0
  100. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/metrics/vqa_metric.py +0 -0
  101. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/__init__.py +0 -0
  102. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/backbones/builder.py +0 -0
  103. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/contextaware.py +0 -0
  104. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/e2e.py +0 -0
  105. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/learnablepooling.py +0 -0
  106. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/qwen_vl_native.py +0 -0
  107. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/qwen_xvars.py +0 -0
  108. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/rule_based.py +0 -0
  109. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/tracking.py +0 -0
  110. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/vars.py +0 -0
  111. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/video.py +0 -0
  112. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
  113. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/video_mae.py +0 -0
  114. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
  115. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/builder.py +0 -0
  116. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/heads/builder.py +0 -0
  117. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/neck/builder.py +0 -0
  118. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/common.py +0 -0
  119. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/impl/__init__.py +0 -0
  120. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/impl/asformer.py +0 -0
  121. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/impl/calf.py +0 -0
  122. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/impl/gsm.py +0 -0
  123. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/impl/gtad.py +0 -0
  124. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/impl/tsm.py +0 -0
  125. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/litebase.py +0 -0
  126. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/modules.py +0 -0
  127. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/shift.py +0 -0
  128. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/utils.py +0 -0
  129. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
  130. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/vqa_prompting.py +0 -0
  131. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/models/utils/xvars_clip_index.py +0 -0
  132. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/setup/setup.py +0 -0
  133. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/tools/__init__.py +0 -0
  134. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/tools/_common.py +0 -0
  135. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/tools/osl_json_to_parquet.py +0 -0
  136. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib/tools/parquet_to_osl_json.py +0 -0
  137. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib.egg-info/SOURCES.txt +0 -0
  138. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib.egg-info/dependency_links.txt +0 -0
  139. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib.egg-info/entry_points.txt +0 -0
  140. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib.egg-info/requires.txt +0 -0
  141. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/opensportslib.egg-info/top_level.txt +0 -0
  142. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/scripts/run_h5_header_rule_inference.py +0 -0
  143. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/setup.cfg +0 -0
  144. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/conftest.py +0 -0
  145. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/release/__init__.py +0 -0
  146. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/release/_release_common.py +0 -0
  147. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/release/test_classification_release.py +0 -0
  148. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/release/test_localization_release.py +0 -0
  149. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/release/test_vqa_release.py +0 -0
  150. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_classification_dataset_paths.py +0 -0
  151. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_classification_trainer_dataloader.py +0 -0
  152. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_config_architecture.py +0 -0
  153. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_config_split_override_sync.py +0 -0
  154. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_config_utils_smoke.py +0 -0
  155. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_conversion_tools.py +0 -0
  156. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_extract_xvars_features.py +0 -0
  157. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_h5_header_rule_spotter.py +0 -0
  158. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_h5_header_skeleton_spotter.py +0 -0
  159. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_h5_tracking_dataset.py +0 -0
  160. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_localization_dali_filenames.py +0 -0
  161. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_localization_hf_backend_override.py +0 -0
  162. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_localization_intervals.py +0 -0
  163. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_package_smoke.py +0 -0
  164. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_pretrained_config_merge_policy.py +0 -0
  165. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_public_apis_smoke.py +0 -0
  166. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_setup_cli.py +0 -0
  167. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_spotta_e2e.py +0 -0
  168. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_subset_train_infer_integration.py +0 -0
  169. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_task_model_api_contract.py +0 -0
  170. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_vqa_api.py +0 -0
  171. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_vqa_metrics_semantic.py +0 -0
  172. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_vqa_qwen_xvars.py +0 -0
  173. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_vqa_training_lora.py +0 -0
  174. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tests/test_vqa_xvars_videochatgpt.py +0 -0
  175. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
  176. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/build_sngar_spotting.py +0 -0
  177. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/build_soccernet_gar.py +0 -0
  178. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
  179. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/build_soccernet_gar_vqa.py +0 -0
  180. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/build_xvars_indexes.py +0 -0
  181. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/extract_xvars_clip_features.py +0 -0
  182. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
  183. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
  184. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/sngar_dataset_card.py +0 -0
  185. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/sngar_events.py +0 -0
  186. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/convert/verify_sngar_spotting.py +0 -0
  187. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/download/download_hf_repo.py +0 -0
  188. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/download/download_osl_hf.py +0 -0
  189. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/download/push_sngar_spotting.py +0 -0
  190. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/download/upload_osl_hf.py +0 -0
  191. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/training/classification.py +0 -0
  192. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/training/localization.py +0 -0
  193. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/training/vqa.py +0 -0
  194. {opensportslib-0.3.1.dev2 → opensportslib-0.3.1.dev3}/tools/upload/upload_model_hf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensportslib
3
- Version: 0.3.1.dev2
3
+ Version: 0.3.1.dev3
4
4
  Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
5
5
  Author: Jeet Vora
6
6
  Requires-Python: >=3.12
@@ -392,6 +392,19 @@ The JSON records the resolved Hugging Face commit and can later be passed to
392
392
  Parquet/WebDataset download always completes the local split even when a
393
393
  metadata-only `<split>.json` already exists.
394
394
 
395
+ Download APIs accept `byte_progress_cb(filename, downloaded_bytes,
396
+ total_bytes)`. When the repository file is Xet-backed, OpenSportsLib keeps the
397
+ accelerated Xet transfer and adapts Xet's byte updates to this callback. It
398
+ falls back to classic HTTP progress when Xet is unavailable, disabled, or not
399
+ used by the file.
400
+ When byte progress is enabled, Parquet downloads also emit `[current/total]`
401
+ file messages through `progress_cb` so clients can present file-count progress.
402
+ High-level split downloads also accept `file_plan_cb(filenames)`,
403
+ `file_completed_cb(filename, local_path)`, and
404
+ `json_ready_cb(split, json_path)`. These are transfer lifecycle notifications;
405
+ callers remain responsible for queue policy and presentation. For non-dry-run
406
+ JSON datasets, pinned source metadata is persisted before `json_ready_cb` runs.
407
+
395
408
  JSON uploads support partially downloaded datasets: the JSON and all
396
409
  referenced files available locally are committed, while missing referenced
397
410
  files are skipped and reported. Remote files not included in that commit are
@@ -356,6 +356,19 @@ The JSON records the resolved Hugging Face commit and can later be passed to
356
356
  Parquet/WebDataset download always completes the local split even when a
357
357
  metadata-only `<split>.json` already exists.
358
358
 
359
+ Download APIs accept `byte_progress_cb(filename, downloaded_bytes,
360
+ total_bytes)`. When the repository file is Xet-backed, OpenSportsLib keeps the
361
+ accelerated Xet transfer and adapts Xet's byte updates to this callback. It
362
+ falls back to classic HTTP progress when Xet is unavailable, disabled, or not
363
+ used by the file.
364
+ When byte progress is enabled, Parquet downloads also emit `[current/total]`
365
+ file messages through `progress_cb` so clients can present file-count progress.
366
+ High-level split downloads also accept `file_plan_cb(filenames)`,
367
+ `file_completed_cb(filename, local_path)`, and
368
+ `json_ready_cb(split, json_path)`. These are transfer lifecycle notifications;
369
+ callers remain responsible for queue policy and presentation. For non-dry-run
370
+ JSON datasets, pinned source metadata is persisted before `json_ready_cb` runs.
371
+
359
372
  JSON uploads support partially downloaded datasets: the JSON and all
360
373
  referenced files available locally are committed, while missing referenced
361
374
  files are skipped and reported. Remote files not included in that commit are
@@ -9,6 +9,9 @@ from typing import Any, Callable
9
9
 
10
10
  ProgressCallback = Callable[[str], None]
11
11
  ByteProgressCallback = Callable[[str, int, int], None]
12
+ FilePlanCallback = Callable[[list[str]], None]
13
+ FileCompletedCallback = Callable[[str, str], None]
14
+ JsonReadyCallback = Callable[[str, str], None]
12
15
  CancelCheck = Callable[[], bool]
13
16
 
14
17
  HF_REPO_ID_KEY = "hf_repo_id"
@@ -101,6 +104,29 @@ def _emit_progress(progress_cb: ProgressCallback | None, message: str) -> None:
101
104
  progress_cb(message)
102
105
 
103
106
 
107
+ def _emit_file_plan(
108
+ file_plan_cb: FilePlanCallback | None, filenames: list[str]
109
+ ) -> None:
110
+ if file_plan_cb and filenames:
111
+ file_plan_cb(list(filenames))
112
+
113
+
114
+ def _emit_file_completed(
115
+ file_completed_cb: FileCompletedCallback | None,
116
+ filename: str,
117
+ local_path: str,
118
+ ) -> None:
119
+ if file_completed_cb:
120
+ file_completed_cb(filename, local_path)
121
+
122
+
123
+ def _emit_json_ready(
124
+ json_ready_cb: JsonReadyCallback | None, split: str, json_path: str
125
+ ) -> None:
126
+ if json_ready_cb:
127
+ json_ready_cb(split, json_path)
128
+
129
+
104
130
  def _ensure_not_cancelled(is_cancelled: CancelCheck | None) -> None:
105
131
  if is_cancelled and is_cancelled():
106
132
  raise HfTransferCancelled("Transfer cancelled by user.")
@@ -128,6 +154,23 @@ def _import_hf_file_system():
128
154
  return HfFileSystem, TqdmCallback
129
155
 
130
156
 
157
+ def _import_hf_xet_download():
158
+ try:
159
+ from huggingface_hub import get_hf_file_metadata, hf_hub_url
160
+ from huggingface_hub.file_download import xet_get
161
+ from huggingface_hub.utils import build_hf_headers
162
+ from huggingface_hub.utils._runtime import is_xet_available
163
+ except ImportError:
164
+ return None
165
+ return (
166
+ get_hf_file_metadata,
167
+ hf_hub_url,
168
+ xet_get,
169
+ build_hf_headers,
170
+ is_xet_available,
171
+ )
172
+
173
+
131
174
  def _download_hf_file(
132
175
  hf_hub_download,
133
176
  *,
@@ -169,7 +212,6 @@ def _download_hf_file(
169
212
  token=token or None,
170
213
  )
171
214
 
172
- HfFileSystem, TqdmCallback = _import_hf_file_system()
173
215
  destination.parent.mkdir(parents=True, exist_ok=True)
174
216
  file_descriptor, temporary_path = tempfile.mkstemp(
175
217
  prefix=f".{destination.name}.",
@@ -198,9 +240,43 @@ def _download_hf_file(
198
240
  def close(self):
199
241
  pass
200
242
 
201
- callback = TqdmCallback(tqdm_cls=_CallbackProgress)
202
243
  remote_path = f"datasets/{repo_id}/{normalized_filename}"
203
244
  try:
245
+ xet_download = _import_hf_xet_download()
246
+ if xet_download is not None:
247
+ (
248
+ get_hf_file_metadata,
249
+ hf_hub_url,
250
+ xet_get,
251
+ build_hf_headers,
252
+ is_xet_available,
253
+ ) = xet_download
254
+ if is_xet_available():
255
+ metadata = get_hf_file_metadata(
256
+ hf_hub_url(
257
+ repo_id=repo_id,
258
+ repo_type="dataset",
259
+ filename=normalized_filename,
260
+ revision=revision,
261
+ ),
262
+ token=token or None,
263
+ )
264
+ if metadata.xet_file_data is not None:
265
+ progress = _CallbackProgress(total=metadata.size)
266
+ xet_get(
267
+ incomplete_path=Path(temporary_path),
268
+ xet_file_data=metadata.xet_file_data,
269
+ headers=build_hf_headers(token=token or None),
270
+ expected_size=metadata.size,
271
+ displayed_filename=normalized_filename,
272
+ _tqdm_bar=progress,
273
+ )
274
+ _ensure_not_cancelled(is_cancelled)
275
+ os.replace(temporary_path, destination)
276
+ return str(destination)
277
+
278
+ HfFileSystem, TqdmCallback = _import_hf_file_system()
279
+ callback = TqdmCallback(tqdm_cls=_CallbackProgress)
204
280
  with callback:
205
281
  HfFileSystem(token=token or None).get_file(
206
282
  remote_path,
@@ -391,6 +467,9 @@ def _download_parquet_split_and_convert(
391
467
  token: str | None = None,
392
468
  progress_cb: ProgressCallback | None = None,
393
469
  byte_progress_cb: ByteProgressCallback | None = None,
470
+ file_plan_cb: FilePlanCallback | None = None,
471
+ file_completed_cb: FileCompletedCallback | None = None,
472
+ json_ready_cb: JsonReadyCallback | None = None,
394
473
  is_cancelled: CancelCheck | None = None,
395
474
  ) -> dict[str, Any]:
396
475
  cleaned_repo_id = str(repo_id or "").strip()
@@ -413,16 +492,21 @@ def _download_parquet_split_and_convert(
413
492
  tmp_dir = tempfile.mkdtemp(prefix="hf_parquet_dl_", dir=output_dir)
414
493
  try:
415
494
  if annotations_only:
495
+ metadata_filename = f"{cleaned_split}/metadata.parquet"
496
+ _emit_file_plan(file_plan_cb, [metadata_filename])
416
497
  metadata_path = _download_hf_file(
417
498
  hf_hub_download,
418
499
  repo_id=cleaned_repo_id,
419
- filename=f"{cleaned_split}/metadata.parquet",
500
+ filename=metadata_filename,
420
501
  revision=commit,
421
502
  local_dir=tmp_dir,
422
503
  token=token or None,
423
504
  byte_progress_cb=byte_progress_cb,
424
505
  is_cancelled=is_cancelled,
425
506
  )
507
+ _emit_file_completed(
508
+ file_completed_cb, metadata_filename, metadata_path
509
+ )
426
510
  else:
427
511
  if byte_progress_cb is None:
428
512
  snapshot_download(
@@ -447,9 +531,14 @@ def _download_parquet_split_and_convert(
447
531
  raise FileNotFoundError(
448
532
  f"No files found for Parquet split: {cleaned_split}"
449
533
  )
450
- for remote_path in split_files:
534
+ _emit_file_plan(file_plan_cb, split_files)
535
+ for index, remote_path in enumerate(split_files, start=1):
451
536
  _ensure_not_cancelled(is_cancelled)
452
- _download_hf_file(
537
+ _emit_progress(
538
+ progress_cb,
539
+ f"[{index}/{len(split_files)}] Downloading {remote_path}",
540
+ )
541
+ downloaded_path = _download_hf_file(
453
542
  hf_hub_download,
454
543
  repo_id=cleaned_repo_id,
455
544
  filename=remote_path,
@@ -459,6 +548,9 @@ def _download_parquet_split_and_convert(
459
548
  byte_progress_cb=byte_progress_cb,
460
549
  is_cancelled=is_cancelled,
461
550
  )
551
+ _emit_file_completed(
552
+ file_completed_cb, remote_path, downloaded_path
553
+ )
462
554
  _ensure_not_cancelled(is_cancelled)
463
555
 
464
556
  if annotations_only:
@@ -484,6 +576,9 @@ def _download_parquet_split_and_convert(
484
576
  source_format="parquet",
485
577
  commit=commit,
486
578
  )
579
+ _emit_json_ready(
580
+ json_ready_cb, cleaned_split, str(output_json_path)
581
+ )
487
582
  finally:
488
583
  shutil.rmtree(tmp_dir, ignore_errors=True)
489
584
 
@@ -532,6 +627,9 @@ def _download_json_path_from_hf(
532
627
  token: str | None = None,
533
628
  progress_cb: ProgressCallback | None = None,
534
629
  byte_progress_cb: ByteProgressCallback | None = None,
630
+ file_plan_cb: FilePlanCallback | None = None,
631
+ file_completed_cb: FileCompletedCallback | None = None,
632
+ json_ready_cb: JsonReadyCallback | None = None,
535
633
  is_cancelled: CancelCheck | None = None,
536
634
  ) -> dict[str, Any]:
537
635
  HfApi, hf_hub_download, _ = _import_hf_hub()
@@ -543,6 +641,7 @@ def _download_json_path_from_hf(
543
641
  os.makedirs(output_dir, exist_ok=True)
544
642
  _ensure_not_cancelled(is_cancelled)
545
643
  _emit_progress(progress_cb, f"Downloading JSON from {repo_id}@{commit}: {path_in_repo}")
644
+ _emit_file_plan(file_plan_cb, [path_in_repo])
546
645
 
547
646
  json_path = _download_hf_file(
548
647
  hf_hub_download,
@@ -554,6 +653,7 @@ def _download_json_path_from_hf(
554
653
  byte_progress_cb=byte_progress_cb,
555
654
  is_cancelled=is_cancelled,
556
655
  )
656
+ _emit_file_completed(file_completed_cb, path_in_repo, json_path)
557
657
 
558
658
  _ensure_not_cancelled(is_cancelled)
559
659
  with open(json_path, "r", encoding="utf-8") as handle:
@@ -564,6 +664,8 @@ def _download_json_path_from_hf(
564
664
  except ValueError:
565
665
  repo_paths = []
566
666
  allow_patterns = _build_allow_patterns(repo_paths, repo_json_folder)
667
+ if not annotations_only:
668
+ _emit_file_plan(file_plan_cb, allow_patterns)
567
669
 
568
670
  result: dict[str, Any] = {
569
671
  "repo_id": repo_id,
@@ -580,8 +682,11 @@ def _download_json_path_from_hf(
580
682
  "num_samples": len(osl_json.get("data", [])) if isinstance(osl_json.get("data"), list) else 0,
581
683
  }
582
684
 
583
- if annotations_only:
584
- _emit_progress(progress_cb, "Persisting Hugging Face source metadata into downloaded JSON.")
685
+ if not dry_run:
686
+ _emit_progress(
687
+ progress_cb,
688
+ "Persisting Hugging Face source metadata into downloaded JSON.",
689
+ )
585
690
  hf_source_metadata = write_hf_source_metadata_to_dataset_json(
586
691
  json_path,
587
692
  repo_id=repo_id,
@@ -590,9 +695,12 @@ def _download_json_path_from_hf(
590
695
  source_format="json",
591
696
  commit=commit,
592
697
  )
698
+ result["hf_source_metadata"] = hf_source_metadata
699
+ _emit_json_ready(json_ready_cb, cleaned_split, json_path)
700
+
701
+ if annotations_only:
593
702
  result["download_kind"] = "json"
594
703
  result["downloaded_file_count"] = 0
595
- result["hf_source_metadata"] = hf_source_metadata
596
704
  _emit_progress(progress_cb, "Annotation-only download completed.")
597
705
  return result
598
706
 
@@ -651,7 +759,7 @@ def _download_json_path_from_hf(
651
759
  for idx, full_repo_path in enumerate(allow_patterns, start=1):
652
760
  _ensure_not_cancelled(is_cancelled)
653
761
  _emit_progress(progress_cb, f"[{idx}/{len(allow_patterns)}] Downloading {full_repo_path}")
654
- _download_hf_file(
762
+ downloaded_path = _download_hf_file(
655
763
  hf_hub_download,
656
764
  repo_id=repo_id,
657
765
  filename=full_repo_path,
@@ -661,21 +769,13 @@ def _download_json_path_from_hf(
661
769
  byte_progress_cb=byte_progress_cb,
662
770
  is_cancelled=is_cancelled,
663
771
  )
772
+ _emit_file_completed(
773
+ file_completed_cb, full_repo_path, downloaded_path
774
+ )
664
775
  downloaded_count += 1
665
776
 
666
- _emit_progress(progress_cb, "Persisting Hugging Face source metadata into downloaded JSON.")
667
- hf_source_metadata = write_hf_source_metadata_to_dataset_json(
668
- json_path,
669
- repo_id=repo_id,
670
- branch=revision,
671
- split=cleaned_split,
672
- source_format="json",
673
- commit=commit,
674
- )
675
-
676
777
  result["download_kind"] = "json"
677
778
  result["downloaded_file_count"] = downloaded_count
678
- result["hf_source_metadata"] = hf_source_metadata
679
779
  _emit_progress(progress_cb, "Download completed.")
680
780
  return result
681
781
 
@@ -776,6 +876,9 @@ def download_dataset_splits_from_hf(
776
876
  token: str | None = None,
777
877
  progress_cb: ProgressCallback | None = None,
778
878
  byte_progress_cb: ByteProgressCallback | None = None,
879
+ file_plan_cb: FilePlanCallback | None = None,
880
+ file_completed_cb: FileCompletedCallback | None = None,
881
+ json_ready_cb: JsonReadyCallback | None = None,
779
882
  is_cancelled: CancelCheck | None = None,
780
883
  ) -> list[dict[str, Any]]:
781
884
  cleaned_splits = [str(split or "").strip() for split in (splits or [])]
@@ -802,6 +905,9 @@ def download_dataset_splits_from_hf(
802
905
  token=token,
803
906
  progress_cb=_scoped_progress,
804
907
  byte_progress_cb=byte_progress_cb,
908
+ file_plan_cb=file_plan_cb,
909
+ file_completed_cb=file_completed_cb,
910
+ json_ready_cb=json_ready_cb,
805
911
  is_cancelled=is_cancelled,
806
912
  )
807
913
  results.append(result)
@@ -821,6 +927,9 @@ def download_dataset_split_from_hf(
821
927
  token: str | None = None,
822
928
  progress_cb: ProgressCallback | None = None,
823
929
  byte_progress_cb: ByteProgressCallback | None = None,
930
+ file_plan_cb: FilePlanCallback | None = None,
931
+ file_completed_cb: FileCompletedCallback | None = None,
932
+ json_ready_cb: JsonReadyCallback | None = None,
824
933
  is_cancelled: CancelCheck | None = None,
825
934
  ) -> dict[str, Any]:
826
935
  cleaned_repo_id = str(repo_id or "").strip()
@@ -849,6 +958,9 @@ def download_dataset_split_from_hf(
849
958
  token=token,
850
959
  progress_cb=progress_cb,
851
960
  byte_progress_cb=byte_progress_cb,
961
+ file_plan_cb=file_plan_cb,
962
+ file_completed_cb=file_completed_cb,
963
+ json_ready_cb=json_ready_cb,
852
964
  is_cancelled=is_cancelled,
853
965
  )
854
966
 
@@ -863,6 +975,9 @@ def download_dataset_split_from_hf(
863
975
  token=token,
864
976
  progress_cb=progress_cb,
865
977
  byte_progress_cb=byte_progress_cb,
978
+ file_plan_cb=file_plan_cb,
979
+ file_completed_cb=file_completed_cb,
980
+ json_ready_cb=json_ready_cb,
866
981
  is_cancelled=is_cancelled,
867
982
  )
868
983
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensportslib
3
- Version: 0.3.1.dev2
3
+ Version: 0.3.1.dev3
4
4
  Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
5
5
  Author: Jeet Vora
6
6
  Requires-Python: >=3.12
@@ -392,6 +392,19 @@ The JSON records the resolved Hugging Face commit and can later be passed to
392
392
  Parquet/WebDataset download always completes the local split even when a
393
393
  metadata-only `<split>.json` already exists.
394
394
 
395
+ Download APIs accept `byte_progress_cb(filename, downloaded_bytes,
396
+ total_bytes)`. When the repository file is Xet-backed, OpenSportsLib keeps the
397
+ accelerated Xet transfer and adapts Xet's byte updates to this callback. It
398
+ falls back to classic HTTP progress when Xet is unavailable, disabled, or not
399
+ used by the file.
400
+ When byte progress is enabled, Parquet downloads also emit `[current/total]`
401
+ file messages through `progress_cb` so clients can present file-count progress.
402
+ High-level split downloads also accept `file_plan_cb(filenames)`,
403
+ `file_completed_cb(filename, local_path)`, and
404
+ `json_ready_cb(split, json_path)`. These are transfer lifecycle notifications;
405
+ callers remain responsible for queue policy and presentation. For non-dry-run
406
+ JSON datasets, pinned source metadata is persisted before `json_ready_cb` runs.
407
+
395
408
  JSON uploads support partially downloaded datasets: the JSON and all
396
409
  referenced files available locally are committed, while missing referenced
397
410
  files are skipped and reported. Remote files not included in that commit are
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opensportslib"
7
- version = "0.3.1.dev2"
7
+ version = "0.3.1.dev3"
8
8
  description = "OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -70,6 +70,7 @@ def test_download_hf_file_reports_transferred_and_total_bytes(monkeypatch, tmp_p
70
70
  "_import_hf_file_system",
71
71
  lambda: (_FakeFileSystem, TqdmCallback),
72
72
  )
73
+ monkeypatch.setattr(hf_transfer_module, "_import_hf_xet_download", lambda: None)
73
74
  progress = []
74
75
 
75
76
  result = hf_transfer_module._download_hf_file(
@@ -115,6 +116,7 @@ def test_download_hf_file_cancels_during_transfer_and_removes_partial(
115
116
  "_import_hf_file_system",
116
117
  lambda: (_FakeFileSystem, TqdmCallback),
117
118
  )
119
+ monkeypatch.setattr(hf_transfer_module, "_import_hf_xet_download", lambda: None)
118
120
 
119
121
  with pytest.raises(HfTransferCancelled):
120
122
  hf_transfer_module._download_hf_file(
@@ -132,6 +134,62 @@ def test_download_hf_file_cancels_during_transfer_and_removes_partial(
132
134
  assert list((tmp_path / "clips").glob("*.part")) == []
133
135
 
134
136
 
137
+ def test_download_hf_file_reports_bytes_while_using_xet(monkeypatch, tmp_path):
138
+ xet_file_data = object()
139
+
140
+ class _Metadata:
141
+ size = 6
142
+
143
+ _Metadata.xet_file_data = xet_file_data
144
+
145
+ def _fake_xet_get(**kwargs):
146
+ assert kwargs["xet_file_data"] is xet_file_data
147
+ assert kwargs["headers"] == {"authorization": "Bearer hf_test"}
148
+ assert kwargs["expected_size"] == 6
149
+ assert kwargs["displayed_filename"] == "clips/large.mp4"
150
+ Path(kwargs["incomplete_path"]).write_bytes(b"abcdef")
151
+ kwargs["_tqdm_bar"].update(2)
152
+ kwargs["_tqdm_bar"].update(4)
153
+
154
+ monkeypatch.setattr(
155
+ hf_transfer_module,
156
+ "_import_hf_xet_download",
157
+ lambda: (
158
+ lambda url, token: _Metadata(),
159
+ lambda **kwargs: "https://huggingface.test/file",
160
+ _fake_xet_get,
161
+ lambda token: {"authorization": f"Bearer {token}"},
162
+ lambda: True,
163
+ ),
164
+ )
165
+ monkeypatch.setattr(
166
+ hf_transfer_module,
167
+ "_import_hf_file_system",
168
+ lambda: pytest.fail("classic HTTP fallback should not be used"),
169
+ )
170
+ progress = []
171
+
172
+ result = hf_transfer_module._download_hf_file(
173
+ pytest.fail,
174
+ repo_id="OpenSportsLab/repo",
175
+ filename="clips/large.mp4",
176
+ revision="pinned",
177
+ local_dir=str(tmp_path),
178
+ token="hf_test",
179
+ byte_progress_cb=lambda filename, current, total: progress.append(
180
+ (filename, current, total)
181
+ ),
182
+ )
183
+
184
+ assert result == str(tmp_path / "clips" / "large.mp4")
185
+ assert Path(result).read_bytes() == b"abcdef"
186
+ assert progress == [
187
+ ("clips/large.mp4", 0, 6),
188
+ ("clips/large.mp4", 2, 6),
189
+ ("clips/large.mp4", 6, 6),
190
+ ]
191
+
192
+
135
193
  def test_download_hf_file_rejects_unsafe_destination(tmp_path):
136
194
  with pytest.raises(ValueError, match="Unsafe Hugging Face file path"):
137
195
  hf_transfer_module._download_hf_file(
@@ -1053,6 +1111,9 @@ def test_download_dataset_split_from_hf_json_downloads_split_json_and_all_inputs
1053
1111
  ]
1054
1112
  }
1055
1113
  downloaded = []
1114
+ planned = []
1115
+ completed = []
1116
+ json_ready = []
1056
1117
 
1057
1118
  class _FakeApi:
1058
1119
  def __init__(self, token=None):
@@ -1082,6 +1143,11 @@ def test_download_dataset_split_from_hf_json_downloads_split_json_and_all_inputs
1082
1143
  "test",
1083
1144
  str(tmp_path),
1084
1145
  download_format="json",
1146
+ file_plan_cb=planned.append,
1147
+ file_completed_cb=lambda filename, path: completed.append(
1148
+ (filename, path)
1149
+ ),
1150
+ json_ready_cb=lambda split, path: json_ready.append((split, path)),
1085
1151
  )
1086
1152
 
1087
1153
  assert downloaded == ["test.json", "test/captions.json", "test/clip_0.mp4"]
@@ -1094,6 +1160,12 @@ def test_download_dataset_split_from_hf_json_downloads_split_json_and_all_inputs
1094
1160
  assert result["output_dir"] == str(expected_output_dir)
1095
1161
  assert result["json_path"] == str(expected_output_dir / "test.json")
1096
1162
  assert result["downloaded_file_count"] == 2
1163
+ assert planned == [
1164
+ ["test.json"],
1165
+ ["test/captions.json", "test/clip_0.mp4"],
1166
+ ]
1167
+ assert [filename for filename, _path in completed] == downloaded
1168
+ assert json_ready == [("test", str(expected_output_dir / "test.json"))]
1097
1169
 
1098
1170
 
1099
1171
  def test_download_dataset_split_from_hf_parquet_downloads_split_folder(monkeypatch, tmp_path):
@@ -1140,6 +1212,78 @@ def test_download_dataset_split_from_hf_parquet_downloads_split_folder(monkeypat
1140
1212
  assert result["download_skipped"] is False
1141
1213
 
1142
1214
 
1215
+ def test_parquet_byte_download_reports_file_count_progress(monkeypatch, tmp_path):
1216
+ progress_messages = []
1217
+ downloaded = []
1218
+ planned = []
1219
+ completed = []
1220
+ json_ready = []
1221
+
1222
+ class _FakeApi:
1223
+ def __init__(self, token=None):
1224
+ pass
1225
+
1226
+ def repo_info(self, **kwargs):
1227
+ return type("_Info", (), {"sha": "pinned"})()
1228
+
1229
+ def list_repo_files(self, *args, **kwargs):
1230
+ return [
1231
+ "test/metadata.parquet",
1232
+ "test/shards/shard-000000.tar",
1233
+ ]
1234
+
1235
+ def _fake_download_file(_hf_hub_download, **kwargs):
1236
+ downloaded.append(kwargs["filename"])
1237
+ return str(Path(kwargs["local_dir"]) / kwargs["filename"])
1238
+
1239
+ def _fake_conversion(**kwargs):
1240
+ kwargs["output_json_path"].write_text(
1241
+ json.dumps({"data": []}), encoding="utf-8"
1242
+ )
1243
+ return {"num_samples": 0, "extracted_media_files": 0}
1244
+
1245
+ monkeypatch.setattr(
1246
+ "opensportslib.tools.hf_transfer._import_hf_hub",
1247
+ lambda: (_FakeApi, object(), object()),
1248
+ )
1249
+ monkeypatch.setattr(
1250
+ "opensportslib.tools.hf_transfer._download_hf_file",
1251
+ _fake_download_file,
1252
+ )
1253
+ monkeypatch.setattr(
1254
+ "opensportslib.tools.hf_transfer.convert_parquet_to_json",
1255
+ _fake_conversion,
1256
+ )
1257
+
1258
+ download_dataset_split_from_hf(
1259
+ "OpenSportsLab/repo",
1260
+ "main",
1261
+ "test",
1262
+ str(tmp_path),
1263
+ download_format="parquet",
1264
+ progress_cb=progress_messages.append,
1265
+ byte_progress_cb=lambda *_args: None,
1266
+ file_plan_cb=planned.append,
1267
+ file_completed_cb=lambda filename, path: completed.append(
1268
+ (filename, path)
1269
+ ),
1270
+ json_ready_cb=lambda split, path: json_ready.append((split, path)),
1271
+ )
1272
+
1273
+ assert downloaded == [
1274
+ "test/metadata.parquet",
1275
+ "test/shards/shard-000000.tar",
1276
+ ]
1277
+ assert "[1/2] Downloading test/metadata.parquet" in progress_messages
1278
+ assert "[2/2] Downloading test/shards/shard-000000.tar" in progress_messages
1279
+ assert planned == [[
1280
+ "test/metadata.parquet",
1281
+ "test/shards/shard-000000.tar",
1282
+ ]]
1283
+ assert [filename for filename, _path in completed] == downloaded
1284
+ assert json_ready == [("test", str(tmp_path / "main" / "test" / "test.json"))]
1285
+
1286
+
1143
1287
  def test_download_dataset_split_from_hf_parquet_completes_existing_json(
1144
1288
  monkeypatch, tmp_path
1145
1289
  ):
@@ -1196,6 +1340,7 @@ def test_json_annotations_only_downloads_json_and_persists_pinned_source(monkeyp
1196
1340
  encoding="utf-8",
1197
1341
  )
1198
1342
  downloaded = []
1343
+ planned = []
1199
1344
 
1200
1345
  class _FakeApi:
1201
1346
  def __init__(self, token=None):
@@ -1215,9 +1360,11 @@ def test_json_annotations_only_downloads_json_and_persists_pinned_source(monkeyp
1215
1360
  str(tmp_path / "output"),
1216
1361
  download_format="json",
1217
1362
  annotations_only=True,
1363
+ file_plan_cb=planned.append,
1218
1364
  )
1219
1365
 
1220
1366
  assert downloaded == ["test.json"]
1367
+ assert planned == [["test.json"]]
1221
1368
  payload = json.loads(Path(result["json_path"]).read_text(encoding="utf-8"))
1222
1369
  assert payload[HF_FORMAT_KEY] == "json"
1223
1370
  assert payload[HF_COMMIT_KEY] == "pinned-json"