endoreg-db 0.8.8.9__py3-none-any.whl → 0.8.9.10__py3-none-any.whl

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.

Potentially problematic release.


This version of endoreg-db might be problematic. Click here for more details.

Files changed (453) hide show
  1. endoreg_db/admin.py +10 -5
  2. endoreg_db/apps.py +4 -7
  3. endoreg_db/authz/auth.py +1 -0
  4. endoreg_db/authz/backends.py +1 -1
  5. endoreg_db/authz/management/commands/list_routes.py +2 -0
  6. endoreg_db/authz/middleware.py +8 -7
  7. endoreg_db/authz/permissions.py +21 -10
  8. endoreg_db/authz/policy.py +14 -19
  9. endoreg_db/authz/views_auth.py +14 -10
  10. endoreg_db/codemods/rename_datetime_fields.py +8 -1
  11. endoreg_db/exceptions.py +5 -2
  12. endoreg_db/forms/__init__.py +0 -1
  13. endoreg_db/forms/examination_form.py +4 -3
  14. endoreg_db/forms/patient_finding_intervention_form.py +30 -8
  15. endoreg_db/forms/patient_form.py +9 -13
  16. endoreg_db/forms/questionnaires/__init__.py +1 -1
  17. endoreg_db/forms/settings/__init__.py +4 -1
  18. endoreg_db/forms/unit.py +2 -1
  19. endoreg_db/helpers/count_db.py +17 -14
  20. endoreg_db/helpers/default_objects.py +2 -1
  21. endoreg_db/helpers/download_segmentation_model.py +4 -3
  22. endoreg_db/helpers/interact.py +0 -5
  23. endoreg_db/helpers/test_video_helper.py +33 -25
  24. endoreg_db/import_files/__init__.py +1 -1
  25. endoreg_db/import_files/context/__init__.py +1 -1
  26. endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
  27. endoreg_db/import_files/context/ensure_center.py +4 -4
  28. endoreg_db/import_files/context/file_lock.py +3 -3
  29. endoreg_db/import_files/context/import_context.py +11 -12
  30. endoreg_db/import_files/context/validate_directories.py +1 -0
  31. endoreg_db/import_files/file_storage/create_report_file.py +57 -34
  32. endoreg_db/import_files/file_storage/create_video_file.py +64 -35
  33. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
  34. endoreg_db/import_files/file_storage/state_management.py +146 -83
  35. endoreg_db/import_files/file_storage/storage.py +5 -1
  36. endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
  37. endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
  38. endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
  39. endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
  40. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
  41. endoreg_db/import_files/report_import_service.py +36 -30
  42. endoreg_db/import_files/video_import_service.py +27 -23
  43. endoreg_db/logger_conf.py +56 -40
  44. endoreg_db/management/__init__.py +1 -1
  45. endoreg_db/management/commands/__init__.py +1 -1
  46. endoreg_db/management/commands/check_auth.py +45 -38
  47. endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
  48. endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
  49. endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
  50. endoreg_db/management/commands/fix_video_paths.py +75 -54
  51. endoreg_db/management/commands/import_report.py +1 -3
  52. endoreg_db/management/commands/list_routes.py +2 -0
  53. endoreg_db/management/commands/load_ai_model_data.py +8 -2
  54. endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
  55. endoreg_db/management/commands/load_center_data.py +3 -3
  56. endoreg_db/management/commands/load_distribution_data.py +35 -38
  57. endoreg_db/management/commands/load_endoscope_data.py +0 -3
  58. endoreg_db/management/commands/load_examination_data.py +20 -4
  59. endoreg_db/management/commands/load_finding_data.py +18 -3
  60. endoreg_db/management/commands/load_gender_data.py +17 -24
  61. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
  62. endoreg_db/management/commands/load_information_source.py +0 -3
  63. endoreg_db/management/commands/load_lab_value_data.py +14 -3
  64. endoreg_db/management/commands/load_legacy_data.py +303 -0
  65. endoreg_db/management/commands/load_name_data.py +1 -2
  66. endoreg_db/management/commands/load_pdf_type_data.py +4 -8
  67. endoreg_db/management/commands/load_profession_data.py +0 -1
  68. endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
  69. endoreg_db/management/commands/load_requirement_data.py +6 -2
  70. endoreg_db/management/commands/load_unit_data.py +0 -4
  71. endoreg_db/management/commands/load_user_groups.py +5 -7
  72. endoreg_db/management/commands/model_input.py +169 -0
  73. endoreg_db/management/commands/register_ai_model.py +22 -16
  74. endoreg_db/management/commands/setup_endoreg_db.py +110 -32
  75. endoreg_db/management/commands/storage_management.py +14 -8
  76. endoreg_db/management/commands/summarize_db_content.py +154 -63
  77. endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
  78. endoreg_db/management/commands/validate_video_files.py +82 -50
  79. endoreg_db/management/commands/video_validation.py +4 -6
  80. endoreg_db/migrations/0001_initial.py +112 -63
  81. endoreg_db/migrations/__init__.py +0 -0
  82. endoreg_db/models/__init__.py +8 -0
  83. endoreg_db/models/administration/ai/active_model.py +5 -5
  84. endoreg_db/models/administration/ai/ai_model.py +41 -18
  85. endoreg_db/models/administration/ai/model_type.py +1 -0
  86. endoreg_db/models/administration/case/case.py +22 -22
  87. endoreg_db/models/administration/center/__init__.py +5 -5
  88. endoreg_db/models/administration/center/center.py +6 -2
  89. endoreg_db/models/administration/center/center_resource.py +18 -4
  90. endoreg_db/models/administration/center/center_shift.py +3 -1
  91. endoreg_db/models/administration/center/center_waste.py +6 -2
  92. endoreg_db/models/administration/person/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  94. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  95. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  96. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  97. endoreg_db/models/administration/person/names/first_name.py +6 -4
  98. endoreg_db/models/administration/person/names/last_name.py +4 -3
  99. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  100. endoreg_db/models/administration/person/patient/patient.py +0 -1
  101. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  102. endoreg_db/models/administration/person/person.py +1 -1
  103. endoreg_db/models/administration/product/__init__.py +7 -6
  104. endoreg_db/models/administration/product/product.py +6 -2
  105. endoreg_db/models/administration/product/product_group.py +9 -7
  106. endoreg_db/models/administration/product/product_material.py +9 -2
  107. endoreg_db/models/administration/product/reference_product.py +64 -15
  108. endoreg_db/models/administration/qualification/qualification.py +3 -1
  109. endoreg_db/models/administration/shift/shift.py +3 -1
  110. endoreg_db/models/administration/shift/shift_type.py +12 -4
  111. endoreg_db/models/aidataset/__init__.py +5 -0
  112. endoreg_db/models/aidataset/aidataset.py +193 -0
  113. endoreg_db/models/label/__init__.py +1 -1
  114. endoreg_db/models/label/label.py +10 -2
  115. endoreg_db/models/label/label_set.py +3 -1
  116. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  117. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  118. endoreg_db/models/media/__init__.py +12 -5
  119. endoreg_db/models/media/frame/__init__.py +1 -1
  120. endoreg_db/models/media/frame/frame.py +34 -8
  121. endoreg_db/models/media/pdf/__init__.py +2 -1
  122. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  123. endoreg_db/models/media/pdf/report_file.py +6 -2
  124. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  125. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  126. endoreg_db/models/media/video/create_from_file.py +20 -41
  127. endoreg_db/models/media/video/pipe_1.py +75 -30
  128. endoreg_db/models/media/video/pipe_2.py +37 -12
  129. endoreg_db/models/media/video/video_file.py +36 -24
  130. endoreg_db/models/media/video/video_file_ai.py +235 -70
  131. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  132. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  133. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  134. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  135. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  136. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  140. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  141. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  142. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  143. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  144. endoreg_db/models/media/video/video_file_io.py +85 -33
  145. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  146. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  147. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  148. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  149. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  150. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  151. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  152. endoreg_db/models/media/video/video_file_segments.py +118 -27
  153. endoreg_db/models/media/video/video_metadata.py +25 -6
  154. endoreg_db/models/media/video/video_processing.py +54 -15
  155. endoreg_db/models/medical/__init__.py +3 -13
  156. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  157. endoreg_db/models/medical/disease.py +18 -6
  158. endoreg_db/models/medical/event.py +6 -2
  159. endoreg_db/models/medical/examination/__init__.py +5 -1
  160. endoreg_db/models/medical/examination/examination.py +22 -6
  161. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  162. endoreg_db/models/medical/examination/examination_time.py +6 -2
  163. endoreg_db/models/medical/finding/__init__.py +3 -1
  164. endoreg_db/models/medical/finding/finding.py +37 -12
  165. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  166. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  167. endoreg_db/models/medical/finding/finding_type.py +3 -1
  168. endoreg_db/models/medical/hardware/__init__.py +1 -1
  169. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  170. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  171. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  172. endoreg_db/models/medical/medication/__init__.py +7 -3
  173. endoreg_db/models/medical/medication/medication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  175. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  176. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  177. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  178. endoreg_db/models/medical/patient/__init__.py +2 -10
  179. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  180. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  181. endoreg_db/models/medical/patient/patient_event.py +12 -4
  182. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  183. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  184. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  185. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  186. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  187. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  188. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  189. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  190. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  191. endoreg_db/models/metadata/model_meta.py +40 -12
  192. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  193. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  194. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  195. endoreg_db/models/metadata/video_meta.py +146 -39
  196. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  197. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  198. endoreg_db/models/operation_log.py +63 -0
  199. endoreg_db/models/other/__init__.py +10 -10
  200. endoreg_db/models/other/distribution/__init__.py +9 -7
  201. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  202. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  203. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  204. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  205. endoreg_db/models/other/emission/__init__.py +1 -1
  206. endoreg_db/models/other/emission/emission_factor.py +9 -3
  207. endoreg_db/models/other/information_source.py +15 -5
  208. endoreg_db/models/other/material.py +3 -1
  209. endoreg_db/models/other/transport_route.py +3 -1
  210. endoreg_db/models/other/unit.py +6 -2
  211. endoreg_db/models/report/report.py +0 -1
  212. endoreg_db/models/requirement/requirement.py +84 -27
  213. endoreg_db/models/requirement/requirement_error.py +5 -6
  214. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  215. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  216. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  217. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  218. endoreg_db/models/requirement/requirement_operator.py +28 -8
  219. endoreg_db/models/requirement/requirement_set.py +34 -11
  220. endoreg_db/models/state/__init__.py +1 -0
  221. endoreg_db/models/state/audit_ledger.py +9 -2
  222. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  223. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  224. endoreg_db/models/state/raw_pdf.py +0 -1
  225. endoreg_db/models/state/video.py +2 -3
  226. endoreg_db/models/utils.py +4 -2
  227. endoreg_db/queries/__init__.py +2 -6
  228. endoreg_db/queries/annotations/__init__.py +1 -3
  229. endoreg_db/queries/annotations/legacy.py +37 -26
  230. endoreg_db/root_urls.py +3 -4
  231. endoreg_db/schemas/examination_evaluation.py +3 -0
  232. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  233. endoreg_db/serializers/__init__.py +2 -8
  234. endoreg_db/serializers/administration/__init__.py +1 -2
  235. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  236. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  237. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  238. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  239. endoreg_db/serializers/administration/center.py +7 -2
  240. endoreg_db/serializers/administration/gender.py +4 -2
  241. endoreg_db/serializers/anonymization.py +13 -13
  242. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  243. endoreg_db/serializers/examination/__init__.py +1 -1
  244. endoreg_db/serializers/examination/base.py +12 -13
  245. endoreg_db/serializers/examination/dropdown.py +6 -7
  246. endoreg_db/serializers/examination_serializer.py +3 -6
  247. endoreg_db/serializers/finding/__init__.py +1 -1
  248. endoreg_db/serializers/finding/finding.py +14 -7
  249. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  250. endoreg_db/serializers/finding_classification/choice.py +3 -3
  251. endoreg_db/serializers/finding_classification/classification.py +2 -4
  252. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  253. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  254. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  255. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  256. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  257. endoreg_db/serializers/meta/__init__.py +1 -2
  258. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  259. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  260. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  261. endoreg_db/serializers/misc/__init__.py +2 -2
  262. endoreg_db/serializers/misc/file_overview.py +11 -7
  263. endoreg_db/serializers/misc/stats.py +10 -8
  264. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  265. endoreg_db/serializers/misc/upload_job.py +32 -29
  266. endoreg_db/serializers/patient/__init__.py +2 -1
  267. endoreg_db/serializers/patient/patient.py +32 -15
  268. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  269. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  270. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  271. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  272. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  273. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  274. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  275. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  276. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  277. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  278. endoreg_db/serializers/pdf/__init__.py +1 -3
  279. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  280. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  281. endoreg_db/serializers/video/__init__.py +2 -2
  282. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  283. endoreg_db/serializers/video/video_file_brief.py +6 -2
  284. endoreg_db/serializers/video/video_file_detail.py +36 -23
  285. endoreg_db/serializers/video/video_file_list.py +4 -2
  286. endoreg_db/serializers/video/video_processing_history.py +54 -50
  287. endoreg_db/services/__init__.py +1 -1
  288. endoreg_db/services/anonymization.py +2 -2
  289. endoreg_db/services/examination_evaluation.py +40 -17
  290. endoreg_db/services/model_meta_from_hf.py +76 -0
  291. endoreg_db/services/polling_coordinator.py +101 -70
  292. endoreg_db/services/pseudonym_service.py +27 -22
  293. endoreg_db/services/report_import.py +6 -3
  294. endoreg_db/services/segment_sync.py +75 -59
  295. endoreg_db/services/video_import.py +6 -7
  296. endoreg_db/urls/__init__.py +2 -2
  297. endoreg_db/urls/ai.py +7 -25
  298. endoreg_db/urls/anonymization.py +61 -15
  299. endoreg_db/urls/auth.py +4 -4
  300. endoreg_db/urls/classification.py +4 -9
  301. endoreg_db/urls/examination.py +27 -18
  302. endoreg_db/urls/media.py +27 -34
  303. endoreg_db/urls/patient.py +11 -7
  304. endoreg_db/urls/requirements.py +3 -1
  305. endoreg_db/urls/root_urls.py +2 -3
  306. endoreg_db/urls/stats.py +24 -16
  307. endoreg_db/urls/upload.py +3 -11
  308. endoreg_db/utils/__init__.py +14 -15
  309. endoreg_db/utils/ai/__init__.py +1 -1
  310. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  311. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  312. endoreg_db/utils/ai/get.py +2 -1
  313. endoreg_db/utils/ai/inference_dataset.py +14 -15
  314. endoreg_db/utils/ai/model_training/config.py +117 -0
  315. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  316. endoreg_db/utils/ai/model_training/losses.py +68 -0
  317. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  318. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  319. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  320. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  321. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  322. endoreg_db/utils/ai/predict.py +4 -4
  323. endoreg_db/utils/ai/preprocess.py +19 -11
  324. endoreg_db/utils/calc_duration_seconds.py +4 -4
  325. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  326. endoreg_db/utils/check_video_files.py +74 -47
  327. endoreg_db/utils/cropping.py +10 -9
  328. endoreg_db/utils/dataloader.py +11 -3
  329. endoreg_db/utils/dates.py +3 -4
  330. endoreg_db/utils/defaults/set_default_center.py +7 -6
  331. endoreg_db/utils/env.py +6 -2
  332. endoreg_db/utils/extract_specific_frames.py +24 -9
  333. endoreg_db/utils/file_operations.py +30 -18
  334. endoreg_db/utils/fix_video_path_direct.py +57 -41
  335. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  336. endoreg_db/utils/hashs.py +3 -18
  337. endoreg_db/utils/links/requirement_link.py +96 -52
  338. endoreg_db/utils/ocr.py +30 -25
  339. endoreg_db/utils/operation_log.py +61 -0
  340. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  341. endoreg_db/utils/paths.py +6 -6
  342. endoreg_db/utils/permissions.py +40 -24
  343. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  344. endoreg_db/utils/product/sum_emissions.py +5 -3
  345. endoreg_db/utils/product/sum_weights.py +4 -2
  346. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  347. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  348. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  349. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  350. endoreg_db/utils/setup_config.py +21 -5
  351. endoreg_db/utils/storage.py +3 -1
  352. endoreg_db/utils/translation.py +19 -15
  353. endoreg_db/utils/uuid.py +1 -0
  354. endoreg_db/utils/validate_endo_roi.py +12 -4
  355. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  356. endoreg_db/utils/validate_video_detailed.py +207 -149
  357. endoreg_db/utils/video/__init__.py +7 -3
  358. endoreg_db/utils/video/extract_frames.py +30 -18
  359. endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
  360. endoreg_db/utils/video/names.py +11 -6
  361. endoreg_db/utils/video/streaming_processor.py +175 -101
  362. endoreg_db/utils/video/video_splitter.py +30 -19
  363. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  364. endoreg_db/views/__init__.py +0 -20
  365. endoreg_db/views/anonymization/__init__.py +6 -2
  366. endoreg_db/views/anonymization/media_management.py +2 -6
  367. endoreg_db/views/anonymization/overview.py +34 -1
  368. endoreg_db/views/anonymization/validate.py +79 -18
  369. endoreg_db/views/auth/__init__.py +1 -1
  370. endoreg_db/views/auth/keycloak.py +16 -14
  371. endoreg_db/views/examination/__init__.py +12 -15
  372. endoreg_db/views/examination/examination.py +5 -5
  373. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  374. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  375. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  376. endoreg_db/views/examination/get_findings.py +8 -10
  377. endoreg_db/views/examination/get_instruments.py +3 -2
  378. endoreg_db/views/examination/get_interventions.py +1 -1
  379. endoreg_db/views/finding/__init__.py +2 -2
  380. endoreg_db/views/finding/finding.py +58 -54
  381. endoreg_db/views/finding/get_classifications.py +1 -1
  382. endoreg_db/views/finding/get_interventions.py +1 -1
  383. endoreg_db/views/finding_classification/__init__.py +5 -5
  384. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  385. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  386. endoreg_db/views/media/__init__.py +13 -13
  387. endoreg_db/views/media/pdf_media.py +9 -9
  388. endoreg_db/views/media/sensitive_metadata.py +10 -7
  389. endoreg_db/views/media/video_media.py +4 -4
  390. endoreg_db/views/meta/__init__.py +1 -1
  391. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  392. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  393. endoreg_db/views/misc/__init__.py +6 -34
  394. endoreg_db/views/misc/center.py +2 -1
  395. endoreg_db/views/misc/csrf.py +2 -1
  396. endoreg_db/views/misc/gender.py +2 -1
  397. endoreg_db/views/misc/stats.py +141 -106
  398. endoreg_db/views/patient/__init__.py +1 -3
  399. endoreg_db/views/patient/patient.py +141 -99
  400. endoreg_db/views/patient_examination/__init__.py +5 -5
  401. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  402. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  403. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  404. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  405. endoreg_db/views/patient_examination/video.py +114 -80
  406. endoreg_db/views/patient_finding/__init__.py +1 -1
  407. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  408. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  409. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  410. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  411. endoreg_db/views/report/reimport.py +1 -1
  412. endoreg_db/views/report/report_stream.py +5 -8
  413. endoreg_db/views/requirement/__init__.py +2 -1
  414. endoreg_db/views/requirement/evaluate.py +7 -9
  415. endoreg_db/views/requirement/lookup.py +2 -3
  416. endoreg_db/views/requirement/lookup_store.py +0 -1
  417. endoreg_db/views/requirement/requirement_utils.py +2 -4
  418. endoreg_db/views/stats/__init__.py +4 -4
  419. endoreg_db/views/stats/stats_views.py +152 -115
  420. endoreg_db/views/video/__init__.py +18 -27
  421. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  422. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  423. endoreg_db/views/video/correction.py +5 -6
  424. endoreg_db/views/video/reimport.py +134 -99
  425. endoreg_db/views/video/segments_crud.py +134 -44
  426. endoreg_db/views/video/video_apply_mask.py +13 -12
  427. endoreg_db/views/video/video_correction.py +2 -1
  428. endoreg_db/views/video/video_download_processed.py +15 -15
  429. endoreg_db/views/video/video_meta_stats.py +7 -6
  430. endoreg_db/views/video/video_processing_history.py +3 -2
  431. endoreg_db/views/video/video_remove_frames.py +13 -12
  432. endoreg_db/views/video/video_stream.py +110 -82
  433. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  434. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
  435. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
  436. endoreg_db/management/commands/import_fallback_video.py +0 -203
  437. endoreg_db/management/commands/import_video.py +0 -422
  438. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  439. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  440. endoreg_db/serializers/label/__init__.py +0 -7
  441. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  442. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  443. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  444. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  445. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  446. endoreg_db/services/__old/pdf_import.py +0 -1487
  447. endoreg_db/services/__old/video_import.py +0 -1306
  448. endoreg_db/tasks/upload_tasks.py +0 -216
  449. endoreg_db/tasks/video_ingest.py +0 -161
  450. endoreg_db/tasks/video_processing_tasks.py +0 -327
  451. endoreg_db/views/misc/translation.py +0 -182
  452. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  453. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -9,93 +9,132 @@ from ...exceptions import InsufficientStorageError, VideoProcessingError
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
12
+
12
13
  class StreamingVideoProcessor:
13
14
  """
14
15
  Streaming video processor for memory-efficient video anonymization.
15
16
  Processes videos in chunks to reduce memory usage and improve performance.
16
17
  """
17
-
18
+
18
19
  def __init__(self, chunk_duration: int = 30, temp_dir: Optional[Path] = None):
19
20
  """
20
21
  Initialize the streaming processor.
21
-
22
+
22
23
  Args:
23
24
  chunk_duration: Duration of each chunk in seconds
24
25
  temp_dir: Temporary directory for processing chunks
25
26
  """
26
27
  self.chunk_duration = chunk_duration
27
- self.temp_dir = Path(temp_dir) if temp_dir else Path(tempfile.gettempdir()) / 'video_streaming'
28
+ self.temp_dir = (
29
+ Path(temp_dir)
30
+ if temp_dir
31
+ else Path(tempfile.gettempdir()) / "video_streaming"
32
+ )
28
33
  self.temp_dir.mkdir(parents=True, exist_ok=True)
29
-
34
+
30
35
  def check_ffmpeg_available(self) -> bool:
31
36
  """Check if FFmpeg is available in the system."""
32
37
  try:
33
- subprocess.run(['ffmpeg', '-version'],
34
- capture_output=True, check=True, timeout=10)
38
+ subprocess.run(
39
+ ["ffmpeg", "-version"], capture_output=True, check=True, timeout=10
40
+ )
35
41
  return True
36
- except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
42
+ except (
43
+ subprocess.CalledProcessError,
44
+ FileNotFoundError,
45
+ subprocess.TimeoutExpired,
46
+ ):
37
47
  return False
38
-
48
+
39
49
  def get_video_duration(self, video_path: Path) -> float:
40
50
  """Get video duration in seconds using FFprobe."""
41
51
  try:
42
52
  cmd = [
43
- 'ffprobe', '-v', 'quiet', '-show_entries', 'format=duration',
44
- '-of', 'csv=p=0', str(video_path)
53
+ "ffprobe",
54
+ "-v",
55
+ "quiet",
56
+ "-show_entries",
57
+ "format=duration",
58
+ "-of",
59
+ "csv=p=0",
60
+ str(video_path),
45
61
  ]
46
- result = subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=30)
62
+ result = subprocess.run(
63
+ cmd, capture_output=True, text=True, check=True, timeout=30
64
+ )
47
65
  return float(result.stdout.strip())
48
- except (subprocess.CalledProcessError, ValueError, subprocess.TimeoutExpired) as e:
66
+ except (
67
+ subprocess.CalledProcessError,
68
+ ValueError,
69
+ subprocess.TimeoutExpired,
70
+ ) as e:
49
71
  logger.error(f"Failed to get video duration for {video_path}: {e}")
50
72
  raise VideoProcessingError(f"Could not determine video duration: {e}")
51
-
52
- def split_video_chunks(self, video_path: Path) -> Iterator[Tuple[Path, float, float]]:
73
+
74
+ def split_video_chunks(
75
+ self, video_path: Path
76
+ ) -> Iterator[Tuple[Path, float, float]]:
53
77
  """
54
78
  Split video into chunks for streaming processing.
55
-
79
+
56
80
  Args:
57
81
  video_path: Path to the input video
58
-
82
+
59
83
  Yields:
60
84
  Tuple of (chunk_path, start_time, end_time)
61
85
  """
62
86
  if not self.check_ffmpeg_available():
63
87
  raise VideoProcessingError("FFmpeg not available for video processing")
64
-
88
+
65
89
  try:
66
90
  total_duration = self.get_video_duration(video_path)
67
- logger.info(f"Video duration: {total_duration:.2f}s, splitting into {self.chunk_duration}s chunks")
68
-
91
+ logger.info(
92
+ f"Video duration: {total_duration:.2f}s, splitting into {self.chunk_duration}s chunks"
93
+ )
94
+
69
95
  chunk_count = 0
70
96
  for start_time in range(0, int(total_duration), self.chunk_duration):
71
97
  end_time = min(start_time + self.chunk_duration, total_duration)
72
-
98
+
73
99
  # Create chunk filename
74
- chunk_filename = f"chunk_{chunk_count:04d}_{start_time}_{int(end_time)}.mp4"
100
+ chunk_filename = (
101
+ f"chunk_{chunk_count:04d}_{start_time}_{int(end_time)}.mp4"
102
+ )
75
103
  chunk_path = self.temp_dir / chunk_filename
76
-
104
+
77
105
  # Extract chunk using FFmpeg
78
106
  cmd = [
79
- 'ffmpeg', '-y', # Overwrite output files
80
- '-ss', str(start_time), # Start time
81
- '-i', str(video_path), # Input file
82
- '-t', str(end_time - start_time), # Duration
83
- '-c', 'copy', # Copy streams without re-encoding for speed
84
- '-avoid_negative_ts', 'make_zero', # Handle timestamp issues
85
- str(chunk_path)
107
+ "ffmpeg",
108
+ "-y", # Overwrite output files
109
+ "-ss",
110
+ str(start_time), # Start time
111
+ "-i",
112
+ str(video_path), # Input file
113
+ "-t",
114
+ str(end_time - start_time), # Duration
115
+ "-c",
116
+ "copy", # Copy streams without re-encoding for speed
117
+ "-avoid_negative_ts",
118
+ "make_zero", # Handle timestamp issues
119
+ str(chunk_path),
86
120
  ]
87
-
121
+
88
122
  try:
89
- logger.debug(f"Creating chunk {chunk_count}: {start_time}s-{end_time}s")
90
- result = subprocess.run(cmd, capture_output=True, text=True,
91
- check=True, timeout=300) # 5 minute timeout per chunk
92
-
123
+ logger.debug(
124
+ f"Creating chunk {chunk_count}: {start_time}s-{end_time}s"
125
+ )
126
+ result = subprocess.run(
127
+ cmd, capture_output=True, text=True, check=True, timeout=300
128
+ ) # 5 minute timeout per chunk
129
+
93
130
  if chunk_path.exists() and chunk_path.stat().st_size > 0:
94
131
  yield chunk_path, start_time, end_time
95
132
  chunk_count += 1
96
133
  else:
97
- logger.warning(f"Chunk {chunk_count} was not created or is empty")
98
-
134
+ logger.warning(
135
+ f"Chunk {chunk_count} was not created or is empty"
136
+ )
137
+
99
138
  except subprocess.CalledProcessError as e:
100
139
  logger.error(f"FFmpeg failed for chunk {chunk_count}: {e.stderr}")
101
140
  # Skip this chunk but continue with others
@@ -103,102 +142,119 @@ class StreamingVideoProcessor:
103
142
  except subprocess.TimeoutExpired:
104
143
  logger.error(f"FFmpeg timeout for chunk {chunk_count}")
105
144
  continue
106
-
145
+
107
146
  except Exception as e:
108
147
  logger.error(f"Error in video chunking: {e}")
109
148
  raise VideoProcessingError(f"Video chunking failed: {e}")
110
-
111
- def process_chunk_anonymization(self, chunk_path: Path, anonymizer_func, **kwargs) -> Path:
149
+
150
+ def process_chunk_anonymization(
151
+ self, chunk_path: Path, anonymizer_func, **kwargs
152
+ ) -> Path:
112
153
  """
113
154
  Process a single chunk with the anonymization function.
114
-
155
+
115
156
  Args:
116
157
  chunk_path: Path to the video chunk
117
158
  anonymizer_func: Function to anonymize the chunk
118
159
  **kwargs: Additional arguments for the anonymizer
119
-
160
+
120
161
  Returns:
121
162
  Path to the anonymized chunk
122
163
  """
123
164
  try:
124
- output_path = chunk_path.with_suffix('.anonymized.mp4')
125
-
165
+ output_path = chunk_path.with_suffix(".anonymized.mp4")
166
+
126
167
  # Call the anonymization function
127
168
  result = anonymizer_func(chunk_path, output_path, **kwargs)
128
-
169
+
129
170
  if isinstance(result, Path):
130
171
  return result
131
172
  elif result is True and output_path.exists():
132
173
  return output_path
133
174
  else:
134
- raise VideoProcessingError(f"Anonymization failed for chunk {chunk_path}")
135
-
175
+ raise VideoProcessingError(
176
+ f"Anonymization failed for chunk {chunk_path}"
177
+ )
178
+
136
179
  except Exception as e:
137
180
  logger.error(f"Chunk anonymization failed for {chunk_path}: {e}")
138
181
  raise VideoProcessingError(f"Chunk processing failed: {e}")
139
-
182
+
140
183
  def merge_chunks(self, chunk_paths: list[Path], output_path: Path) -> Path:
141
184
  """
142
185
  Merge anonymized chunks back into a single video.
143
-
186
+
144
187
  Args:
145
188
  chunk_paths: List of paths to anonymized chunks
146
189
  output_path: Path for the final merged video
147
-
190
+
148
191
  Returns:
149
192
  Path to the merged video
150
193
  """
151
194
  if not chunk_paths:
152
195
  raise VideoProcessingError("No chunks to merge")
153
-
196
+
154
197
  try:
155
198
  # Check storage space before merging
156
- total_chunk_size = sum(chunk.stat().st_size for chunk in chunk_paths if chunk.exists())
199
+ total_chunk_size = sum(
200
+ chunk.stat().st_size for chunk in chunk_paths if chunk.exists()
201
+ )
157
202
  free_space = shutil.disk_usage(output_path.parent).free
158
-
203
+
159
204
  if free_space < total_chunk_size * 1.2: # 20% safety margin
160
205
  raise InsufficientStorageError(
161
206
  f"Insufficient space for merging. Required: {total_chunk_size * 1.2 / 1e9:.1f} GB, "
162
207
  f"Available: {free_space / 1e9:.1f} GB",
163
208
  required_space=int(total_chunk_size * 1.2),
164
- available_space=free_space
209
+ available_space=free_space,
165
210
  )
166
-
211
+
167
212
  # Create concat file for FFmpeg
168
213
  concat_file = self.temp_dir / f"concat_{output_path.stem}.txt"
169
-
170
- with open(concat_file, 'w') as f:
214
+
215
+ with open(concat_file, "w") as f:
171
216
  for chunk_path in chunk_paths:
172
217
  if chunk_path.exists():
173
218
  # Use relative paths in concat file for better portability
174
219
  f.write(f"file '{chunk_path.name}'\n")
175
-
220
+
176
221
  # Merge using FFmpeg concat demuxer
177
222
  cmd = [
178
- 'ffmpeg', '-y', # Overwrite output
179
- '-f', 'concat', # Use concat demuxer
180
- '-safe', '0', # Allow unsafe file paths
181
- '-i', str(concat_file), # Input concat file
182
- '-c', 'copy', # Copy streams without re-encoding
183
- str(output_path)
223
+ "ffmpeg",
224
+ "-y", # Overwrite output
225
+ "-f",
226
+ "concat", # Use concat demuxer
227
+ "-safe",
228
+ "0", # Allow unsafe file paths
229
+ "-i",
230
+ str(concat_file), # Input concat file
231
+ "-c",
232
+ "copy", # Copy streams without re-encoding
233
+ str(output_path),
184
234
  ]
185
-
235
+
186
236
  logger.info(f"Merging {len(chunk_paths)} chunks into {output_path}")
187
-
237
+
188
238
  # Change working directory to temp_dir for relative paths
189
- result = subprocess.run(cmd, cwd=str(self.temp_dir),
190
- capture_output=True, text=True, check=True, timeout=600)
191
-
239
+ result = subprocess.run(
240
+ cmd,
241
+ cwd=str(self.temp_dir),
242
+ capture_output=True,
243
+ text=True,
244
+ check=True,
245
+ timeout=600,
246
+ )
247
+
192
248
  if not output_path.exists() or output_path.stat().st_size == 0:
193
249
  raise VideoProcessingError("Merged video was not created or is empty")
194
-
250
+
195
251
  logger.info(f"Successfully merged video: {output_path}")
196
-
252
+
197
253
  # Clean up concat file
198
254
  concat_file.unlink(missing_ok=True)
199
-
255
+
200
256
  return output_path
201
-
257
+
202
258
  except subprocess.CalledProcessError as e:
203
259
  logger.error(f"FFmpeg merge failed: {e.stderr}")
204
260
  raise VideoProcessingError(f"Video merging failed: {e.stderr}")
@@ -208,80 +264,98 @@ class StreamingVideoProcessor:
208
264
  except Exception as e:
209
265
  logger.error(f"Unexpected error during merge: {e}")
210
266
  raise VideoProcessingError(f"Video merging failed: {e}")
211
-
212
- def process_video_streaming(self, input_path: Path, output_path: Path,
213
- anonymizer_func, progress_callback=None, **kwargs) -> Path:
267
+
268
+ def process_video_streaming(
269
+ self,
270
+ input_path: Path,
271
+ output_path: Path,
272
+ anonymizer_func,
273
+ progress_callback=None,
274
+ **kwargs,
275
+ ) -> Path:
214
276
  """
215
277
  Process a video using streaming approach for memory efficiency.
216
-
278
+
217
279
  Args:
218
280
  input_path: Path to input video
219
281
  output_path: Path for output video
220
282
  anonymizer_func: Function to anonymize video chunks
221
283
  progress_callback: Optional callback for progress updates
222
284
  **kwargs: Additional arguments for anonymizer
223
-
285
+
224
286
  Returns:
225
287
  Path to the processed video
226
288
  """
227
289
  processed_chunks = []
228
290
  total_chunks = 0
229
-
291
+
230
292
  try:
231
- logger.info(f"Starting streaming video processing: {input_path} -> {output_path}")
232
-
293
+ logger.info(
294
+ f"Starting streaming video processing: {input_path} -> {output_path}"
295
+ )
296
+
233
297
  # First pass: count total chunks for progress tracking
234
298
  chunk_list = list(self.split_video_chunks(input_path))
235
299
  total_chunks = len(chunk_list)
236
-
300
+
237
301
  if total_chunks == 0:
238
- raise VideoProcessingError("No chunks were created from the input video")
239
-
302
+ raise VideoProcessingError(
303
+ "No chunks were created from the input video"
304
+ )
305
+
240
306
  logger.info(f"Processing {total_chunks} chunks")
241
-
307
+
242
308
  # Process each chunk
243
309
  for i, (chunk_path, start_time, end_time) in enumerate(chunk_list):
244
310
  try:
245
- logger.debug(f"Processing chunk {i+1}/{total_chunks}: {chunk_path}")
246
-
311
+ logger.debug(
312
+ f"Processing chunk {i + 1}/{total_chunks}: {chunk_path}"
313
+ )
314
+
247
315
  # Process the chunk
248
316
  processed_chunk = self.process_chunk_anonymization(
249
317
  chunk_path, anonymizer_func, **kwargs
250
318
  )
251
319
  processed_chunks.append(processed_chunk)
252
-
320
+
253
321
  # Update progress
254
322
  if progress_callback:
255
- progress = int((i + 1) / total_chunks * 80) # Reserve 20% for merging
256
- progress_callback(progress, f"Processed chunk {i+1}/{total_chunks}")
257
-
323
+ progress = int(
324
+ (i + 1) / total_chunks * 80
325
+ ) # Reserve 20% for merging
326
+ progress_callback(
327
+ progress, f"Processed chunk {i + 1}/{total_chunks}"
328
+ )
329
+
258
330
  # Clean up original chunk to save space
259
331
  chunk_path.unlink(missing_ok=True)
260
-
332
+
261
333
  except Exception as e:
262
334
  logger.error(f"Failed to process chunk {i}: {e}")
263
335
  # Clean up failed chunk
264
336
  chunk_path.unlink(missing_ok=True)
265
337
  # Continue with other chunks
266
338
  continue
267
-
339
+
268
340
  if not processed_chunks:
269
341
  raise VideoProcessingError("No chunks were successfully processed")
270
-
342
+
271
343
  # Update progress for merging phase
272
344
  if progress_callback:
273
- progress_callback(80, f"Merging {len(processed_chunks)} processed chunks...")
274
-
345
+ progress_callback(
346
+ 80, f"Merging {len(processed_chunks)} processed chunks..."
347
+ )
348
+
275
349
  # Merge processed chunks
276
350
  final_output = self.merge_chunks(processed_chunks, output_path)
277
-
351
+
278
352
  # Final progress update
279
353
  if progress_callback:
280
354
  progress_callback(100, "Video processing completed")
281
-
355
+
282
356
  logger.info(f"Streaming video processing completed: {final_output}")
283
357
  return final_output
284
-
358
+
285
359
  except (InsufficientStorageError, VideoProcessingError):
286
360
  # Re-raise these specific errors as-is
287
361
  raise
@@ -291,7 +365,7 @@ class StreamingVideoProcessor:
291
365
  finally:
292
366
  # Clean up all temporary chunks
293
367
  self.cleanup_chunks(processed_chunks)
294
-
368
+
295
369
  def cleanup_chunks(self, chunk_paths: list[Path]) -> None:
296
370
  """Clean up temporary chunk files."""
297
371
  for chunk_path in chunk_paths:
@@ -301,7 +375,7 @@ class StreamingVideoProcessor:
301
375
  logger.debug(f"Cleaned up chunk: {chunk_path}")
302
376
  except Exception as e:
303
377
  logger.warning(f"Failed to clean up chunk {chunk_path}: {e}")
304
-
378
+
305
379
  def cleanup_temp_dir(self) -> None:
306
380
  """Clean up the entire temporary directory."""
307
381
  try:
@@ -309,4 +383,4 @@ class StreamingVideoProcessor:
309
383
  shutil.rmtree(self.temp_dir)
310
384
  logger.debug(f"Cleaned up temp directory: {self.temp_dir}")
311
385
  except Exception as e:
312
- logger.warning(f"Failed to clean up temp directory {self.temp_dir}: {e}")
386
+ logger.warning(f"Failed to clean up temp directory {self.temp_dir}: {e}")
@@ -1,21 +1,25 @@
1
- import os
2
1
  import subprocess
3
2
  import pathlib
4
3
  import math
5
4
  import logging
6
- import json
7
5
 
8
6
  # Setup basic logging
9
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
7
+ logging.basicConfig(
8
+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
9
+ )
10
+
10
11
 
11
12
  def get_video_duration(video_path: pathlib.Path) -> float:
12
13
  """Gets the duration of a video file using ffprobe."""
13
14
  cmd = [
14
15
  "ffprobe",
15
- "-v", "error",
16
- "-show_entries", "format=duration",
17
- "-of", "default=noprint_wrappers=1:nokey=1",
18
- str(video_path)
16
+ "-v",
17
+ "error",
18
+ "-show_entries",
19
+ "format=duration",
20
+ "-of",
21
+ "default=noprint_wrappers=1:nokey=1",
22
+ str(video_path),
19
23
  ]
20
24
  try:
21
25
  result = subprocess.run(cmd, capture_output=True, text=True, check=True)
@@ -28,6 +32,7 @@ def get_video_duration(video_path: pathlib.Path) -> float:
28
32
  logging.error(f"Could not parse duration from ffprobe output: {result.stdout}")
29
33
  raise
30
34
 
35
+
31
36
  def split_video(input_path: str, interval: int):
32
37
  """
33
38
  Splits a video into segments of a specified interval using ffmpeg.
@@ -54,11 +59,13 @@ def split_video(input_path: str, interval: int):
54
59
  return
55
60
 
56
61
  num_segments = math.ceil(duration / interval)
57
- logging.info(f"Splitting into {num_segments} segments of approximately {interval} seconds each.")
62
+ logging.info(
63
+ f"Splitting into {num_segments} segments of approximately {interval} seconds each."
64
+ )
58
65
 
59
66
  for i in range(num_segments):
60
67
  start_time = i * interval
61
- output_filename = output_dir / f"segment_{i+1:03d}{input_file.suffix}"
68
+ output_filename = output_dir / f"segment_{i + 1:03d}{input_file.suffix}"
62
69
 
63
70
  # Use -t for interval duration. For the last segment, ffmpeg with -c copy
64
71
  # might automatically stop at the end, or we could calculate exact duration.
@@ -69,26 +76,30 @@ def split_video(input_path: str, interval: int):
69
76
 
70
77
  cmd = [
71
78
  "ffmpeg",
72
- "-i", str(input_file),
73
- "-ss", str(start_time),
74
- "-t", str(interval),
75
- "-c", "copy", # Fast, lossless splitting
76
- "-avoid_negative_ts", "make_zero", # Avoids issues with negative timestamps
77
- str(output_filename)
79
+ "-i",
80
+ str(input_file),
81
+ "-ss",
82
+ str(start_time),
83
+ "-t",
84
+ str(interval),
85
+ "-c",
86
+ "copy", # Fast, lossless splitting
87
+ "-avoid_negative_ts",
88
+ "make_zero", # Avoids issues with negative timestamps
89
+ str(output_filename),
78
90
  ]
79
91
 
80
92
  logging.info(f"Running command: {' '.join(cmd)}")
81
93
  try:
82
94
  result = subprocess.run(cmd, capture_output=True, text=True, check=True)
83
95
  logging.info(f"Successfully created segment: {output_filename}")
84
- if result.stderr: # ffmpeg often outputs info to stderr
85
- logging.debug(f"ffmpeg output for segment {i+1}:\n{result.stderr}")
96
+ if result.stderr: # ffmpeg often outputs info to stderr
97
+ logging.debug(f"ffmpeg output for segment {i + 1}:\n{result.stderr}")
86
98
  except subprocess.CalledProcessError as e:
87
- logging.error(f"Error creating segment {i+1}: {output_filename}")
99
+ logging.error(f"Error creating segment {i + 1}: {output_filename}")
88
100
  logging.error(f"Command failed: {' '.join(cmd)}")
89
101
  logging.error(f"ffmpeg stderr:\n{e.stderr}")
90
102
  # Decide if you want to stop on error or continue
91
103
  # return # Uncomment to stop on first error
92
104
 
93
105
  logging.info("Video splitting completed.")
94
-