endoreg-db 0.8.1__py3-none-any.whl → 0.8.2.1__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 (48) hide show
  1. endoreg_db/helpers/download_segmentation_model.py +31 -0
  2. endoreg_db/migrations/0003_add_center_display_name.py +30 -0
  3. endoreg_db/models/administration/center/center.py +7 -1
  4. endoreg_db/models/media/pdf/raw_pdf.py +31 -26
  5. endoreg_db/models/media/video/create_from_file.py +26 -4
  6. endoreg_db/models/media/video/pipe_1.py +13 -1
  7. endoreg_db/models/media/video/video_file.py +36 -13
  8. endoreg_db/models/media/video/video_file_anonymize.py +2 -1
  9. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +12 -0
  10. endoreg_db/models/media/video/video_file_io.py +4 -2
  11. endoreg_db/models/metadata/video_meta.py +2 -2
  12. endoreg_db/serializers/anonymization.py +3 -0
  13. endoreg_db/services/pdf_import.py +131 -45
  14. endoreg_db/services/video_import.py +427 -128
  15. endoreg_db/urls/__init__.py +0 -2
  16. endoreg_db/urls/media.py +201 -4
  17. endoreg_db/urls/report.py +0 -30
  18. endoreg_db/urls/sensitive_meta.py +0 -36
  19. endoreg_db/urls/video.py +30 -88
  20. endoreg_db/utils/paths.py +2 -10
  21. endoreg_db/utils/video/ffmpeg_wrapper.py +67 -4
  22. endoreg_db/views/anonymization/validate.py +76 -32
  23. endoreg_db/views/media/__init__.py +38 -2
  24. endoreg_db/views/media/pdf_media.py +1 -1
  25. endoreg_db/views/media/segments.py +71 -0
  26. endoreg_db/views/media/sensitive_metadata.py +314 -0
  27. endoreg_db/views/media/video_segments.py +596 -0
  28. endoreg_db/views/pdf/reimport.py +18 -8
  29. endoreg_db/views/video/__init__.py +0 -8
  30. endoreg_db/views/video/correction.py +34 -32
  31. endoreg_db/views/video/reimport.py +15 -12
  32. endoreg_db/views/video/video_stream.py +168 -50
  33. {endoreg_db-0.8.1.dist-info → endoreg_db-0.8.2.1.dist-info}/METADATA +2 -2
  34. {endoreg_db-0.8.1.dist-info → endoreg_db-0.8.2.1.dist-info}/RECORD +47 -43
  35. endoreg_db/views/video/media/__init__.py +0 -23
  36. /endoreg_db/{urls/pdf.py → config/__init__.py} +0 -0
  37. /endoreg_db/views/video/{media/task_status.py → task_status.py} +0 -0
  38. /endoreg_db/views/video/{media/video_analyze.py → video_analyze.py} +0 -0
  39. /endoreg_db/views/video/{media/video_apply_mask.py → video_apply_mask.py} +0 -0
  40. /endoreg_db/views/video/{media/video_correction.py → video_correction.py} +0 -0
  41. /endoreg_db/views/video/{media/video_download_processed.py → video_download_processed.py} +0 -0
  42. /endoreg_db/views/video/{media/video_media.py → video_media.py} +0 -0
  43. /endoreg_db/views/video/{media/video_meta.py → video_meta.py} +0 -0
  44. /endoreg_db/views/video/{media/video_processing_history.py → video_processing_history.py} +0 -0
  45. /endoreg_db/views/video/{media/video_remove_frames.py → video_remove_frames.py} +0 -0
  46. /endoreg_db/views/video/{media/video_reprocess.py → video_reprocess.py} +0 -0
  47. {endoreg_db-0.8.1.dist-info → endoreg_db-0.8.2.1.dist-info}/WHEEL +0 -0
  48. {endoreg_db-0.8.1.dist-info → endoreg_db-0.8.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -25,7 +25,6 @@ from .label_video_segments import url_patterns as label_video_segments_url_patte
25
25
  from .label_video_segment_validate import url_patterns as label_video_segment_validate_url_patterns
26
26
  # TODO Phase 1.2: Implement VideoMediaView and PDFMediaView before enabling
27
27
  # from .media import urlpatterns as media_url_patterns
28
- from .sensitive_meta import urlpatterns as pdf_url_patterns
29
28
  from .report import url_patterns as report_url_patterns
30
29
  from .upload import urlpatterns as upload_url_patterns
31
30
  from .video import url_patterns as video_url_patterns
@@ -43,7 +42,6 @@ api_urls += label_video_segments_url_patterns
43
42
  api_urls += label_video_segment_validate_url_patterns # Neue Validierungs-Endpunkte
44
43
  # Phase 1.2: Enable media_url_patterns ✅ IMPLEMENTED
45
44
  api_urls += media_url_patterns
46
- api_urls += pdf_url_patterns
47
45
  api_urls += report_url_patterns
48
46
  api_urls += upload_url_patterns
49
47
  api_urls += video_url_patterns
endoreg_db/urls/media.py CHANGED
@@ -2,11 +2,35 @@ from django.urls import path
2
2
 
3
3
  from endoreg_db.views.media import (
4
4
  VideoMediaView,
5
- PDFMediaManagementView as PDFMediaView, # Alias to avoid conflict with legacy pdf.PDFMediaView
5
+ PdfMediaView, # Alias to avoid conflict with legacy pdf.PDFMediaView
6
+ video_segments_by_pk,
7
+ video_segments_collection,
8
+ video_segments_by_video,
9
+ video_segment_detail,
10
+ video_segments_stats,
11
+ video_segment_validate,
12
+ video_segments_validate_bulk,
13
+ video_segments_validation_status,
14
+ video_sensitive_metadata,
15
+ video_sensitive_metadata_verify,
16
+ pdf_sensitive_metadata,
17
+ pdf_sensitive_metadata_verify,
18
+ sensitive_metadata_list,
19
+ pdf_sensitive_metadata_list,
6
20
  )
7
21
  from endoreg_db.views import (
8
22
  VideoStreamView,
9
23
  )
24
+ from endoreg_db.views.pdf.reimport import PdfReimportView
25
+ from endoreg_db.views.video.reimport import VideoReimportView
26
+ from endoreg_db.views.video.correction import (
27
+ VideoReprocessView,
28
+ VideoMetadataView,
29
+ VideoProcessingHistoryView,
30
+ VideoAnalyzeView,
31
+ VideoApplyMaskView,
32
+ VideoRemoveFramesView,
33
+ )
10
34
  # ---------------------------------------------------------------------------------------
11
35
  # ANNOTATION API ENDPOINTS
12
36
  #
@@ -23,10 +47,183 @@ urlpatterns = [
23
47
  path("media/videos/<int:pk>/", VideoStreamView.as_view(), name="video-detail-stream"), # Support ?type= params
24
48
  path("media/videos/<int:pk>/details/", VideoMediaView.as_view(), name="video-detail"), # JSON metadata
25
49
  path("media/videos/<int:pk>/stream/", VideoStreamView.as_view(), name="video-stream"), # Legacy support
50
+
51
+ # Video Re-import API endpoint (modern media framework)
52
+ # POST /api/media/videos/<int:pk>/reimport/
53
+ # Re-imports a video file to regenerate metadata when OCR failed or data is incomplete
54
+ path("media/videos/<int:pk>/reimport/", VideoReimportView.as_view(), name="video-reimport"),
55
+
56
+ # ---------------------------------------------------------------------------------------
57
+ # VIDEO CORRECTION API ENDPOINTS (Modern Media Framework - October 14, 2025)
58
+ #
59
+ # All video correction endpoints migrated to unified /api/media/videos/<pk>/ pattern
60
+ # These endpoints enable video correction workflows (Phase 1.1):
61
+ # - Analysis: Detect sensitive frames using MiniCPM-o 2.6 or OCR+LLM
62
+ # - Masking: Apply device-specific masks or custom ROI masks
63
+ # - Frame Removal: Remove sensitive frames from videos
64
+ # - Reprocessing: Re-run entire anonymization pipeline
65
+ # - Metadata: View analysis results
66
+ # - History: Track all correction operations
67
+ # ---------------------------------------------------------------------------------------
68
+
69
+ # Video Metadata API
70
+ # GET /api/media/videos/<int:pk>/metadata/
71
+ # Returns analysis results (sensitive frame count, ratio, frame IDs)
72
+ path("media/videos/<int:pk>/metadata/", VideoMetadataView.as_view(), name="video-metadata"),
73
+
74
+ # Video Processing History API
75
+ # GET /api/media/videos/<int:pk>/processing-history/
76
+ # Returns history of all processing operations (masking, frame removal, analysis)
77
+ path("media/videos/<int:pk>/processing-history/", VideoProcessingHistoryView.as_view(), name="video-processing-history"),
78
+
79
+ # Video Analysis API
80
+ # POST /api/media/videos/<int:pk>/analyze/
81
+ # Analyzes video for sensitive frames using MiniCPM-o 2.6 or OCR+LLM
82
+ # Body: { detection_method: 'minicpm'|'ocr_llm'|'hybrid', sample_interval: 30 }
83
+ path("media/videos/<int:pk>/analyze/", VideoAnalyzeView.as_view(), name="video-analyze"),
84
+
85
+ # Video Masking API
86
+ # POST /api/media/videos/<int:pk>/apply-mask/
87
+ # Applies device mask or custom ROI mask to video
88
+ # Body: { mask_type: 'device'|'custom', device_name: 'olympus', roi: {...} }
89
+ path("media/videos/<int:pk>/apply-mask/", VideoApplyMaskView.as_view(), name="video-apply-mask"),
90
+
91
+ # Video Frame Removal API
92
+ # POST /api/media/videos/<int:pk>/remove-frames/
93
+ # Removes specified frames from video
94
+ # Body: { frame_list: [10,20,30] OR frame_ranges: '10-20,30' OR detection_method: 'automatic' }
95
+ path("media/videos/<int:pk>/remove-frames/", VideoRemoveFramesView.as_view(), name="video-remove-frames"),
96
+
97
+ # Video Reprocess API endpoint (modern media framework)
98
+ # POST /api/media/videos/<int:pk>/reprocess/
99
+ # Re-runs entire anonymization pipeline for a video (correction workflow)
100
+ path("media/videos/<int:pk>/reprocess/", VideoReprocessView.as_view(), name="video-reprocess"),
101
+
102
+ # ---------------------------------------------------------------------------------------
103
+ # VIDEO SEGMENT API ENDPOINTS (Modern Media Framework - October 14, 2025)
104
+ #
105
+ # Unified segment management endpoints replacing legacy /api/video-segments/
106
+ # Collection: GET/POST all segments across videos
107
+ # Video-scoped: GET/POST segments for specific video
108
+ # Detail: GET/PATCH/DELETE individual segment
109
+ # ---------------------------------------------------------------------------------------
110
+
111
+ # Video Segments Collection API
112
+ # GET/POST /api/media/videos/segments/
113
+ # List all video segments across videos or create new segment
114
+ path("media/videos/segments/", video_segments_collection, name="video-segments-collection"),
115
+
116
+ # Video Segments Stats API
117
+ # GET /api/media/videos/segments/stats/
118
+ # Get statistics about video segments
119
+ path("media/videos/segments/stats/", video_segments_stats, name="video-segments-stats"),
120
+
121
+ # Video-Specific Segments API
122
+ # GET/POST /api/media/videos/<int:pk>/segments/
123
+ # List segments for specific video or create segment for video
124
+ path("media/videos/<int:pk>/segments/", video_segments_by_video, name="video-segments-by-video"),
125
+
126
+ # Segment Detail API
127
+ # GET /api/media/videos/<int:pk>/segments/<int:segment_id>/
128
+ # PATCH /api/media/videos/<int:pk>/segments/<int:segment_id>/
129
+ # DELETE /api/media/videos/<int:pk>/segments/<int:segment_id>/
130
+ # Manages individual segment operations
131
+ path("media/videos/<int:pk>/segments/<int:segment_id>/", video_segment_detail, name="video-segment-detail"),
132
+
133
+ # ---------------------------------------------------------------------------------------
134
+ # VIDEO SEGMENT VALIDATION API ENDPOINTS (Modern Media Framework - October 14, 2025)
135
+ #
136
+ # Unified validation endpoints replacing legacy /api/label-video-segment/*/validate/
137
+ # Single: POST validate individual segment
138
+ # Bulk: POST validate multiple segments
139
+ # Status: GET/POST validation status for all segments
140
+ # ---------------------------------------------------------------------------------------
141
+
142
+ # Single Segment Validation API
143
+ # POST /api/media/videos/<int:pk>/segments/<int:segment_id>/validate/
144
+ # Validates a single video segment
145
+ # Body: { "is_validated": true, "notes": "..." }
146
+ path("media/videos/<int:pk>/segments/<int:segment_id>/validate/", video_segment_validate, name="video-segment-validate"),
147
+
148
+ # Bulk Segment Validation API
149
+ # POST /api/media/videos/<int:pk>/segments/validate-bulk/
150
+ # Validates multiple segments at once
151
+ # Body: { "segment_ids": [1,2,3], "is_validated": true, "notes": "..." }
152
+ path("media/videos/<int:pk>/segments/validate-bulk/", video_segments_validate_bulk, name="video-segments-validate-bulk"),
153
+
154
+ # Segment Validation Status API
155
+ # GET /api/media/videos/<int:pk>/segments/validation-status/
156
+ # Returns validation statistics for all segments
157
+ # POST /api/media/videos/<int:pk>/segments/validation-status/
158
+ # Marks all segments (or filtered by label) as validated
159
+ # Body: { "label_name": "polyp", "notes": "..." }
160
+ path("media/videos/<int:pk>/segments/validation-status/", video_segments_validation_status, name="video-segments-validation-status"),
161
+
162
+ # ---------------------------------------------------------------------------------------
163
+ # SENSITIVE METADATA ENDPOINTS (Modern Media Framework)
164
+ # ---------------------------------------------------------------------------------------
165
+
166
+ # Video Sensitive Metadata (Resource-Scoped)
167
+ # GET/PATCH /api/media/videos/<pk>/sensitive-metadata/
168
+ # Get or update sensitive patient data for a video
169
+ path(
170
+ "media/videos/<int:pk>/sensitive-metadata/",
171
+ video_sensitive_metadata,
172
+ name="video-sensitive-metadata"
173
+ ),
174
+
175
+ # POST /api/media/videos/<pk>/sensitive-metadata/verify/
176
+ # Update verification state (dob_verified, names_verified)
177
+ path(
178
+ "media/videos/<int:pk>/sensitive-metadata/verify/",
179
+ video_sensitive_metadata_verify,
180
+ name="video-sensitive-metadata-verify"
181
+ ),
182
+
183
+ # PDF Sensitive Metadata (Resource-Scoped)
184
+ # GET/PATCH /api/media/pdfs/<pk>/sensitive-metadata/
185
+ # Get or update sensitive patient data for a PDF
186
+ path(
187
+ "media/pdfs/<int:pk>/sensitive-metadata/",
188
+ pdf_sensitive_metadata,
189
+ name="pdf-sensitive-metadata"
190
+ ),
191
+
192
+ # POST /api/media/pdfs/<pk>/sensitive-metadata/verify/
193
+ # Update verification state (dob_verified, names_verified)
194
+ path(
195
+ "media/pdfs/<int:pk>/sensitive-metadata/verify/",
196
+ pdf_sensitive_metadata_verify,
197
+ name="pdf-sensitive-metadata-verify"
198
+ ),
199
+
200
+ # List Endpoints (Collection-Level)
201
+ # GET /api/media/sensitive-metadata/
202
+ # List all sensitive metadata (combined PDFs and Videos)
203
+ # Supports filtering: ?content_type=pdf|video&verified=true&search=name
204
+ path(
205
+ "media/sensitive-metadata/",
206
+ sensitive_metadata_list,
207
+ name="sensitive-metadata-list"
208
+ ),
209
+
210
+ # GET /api/media/pdfs/sensitive-metadata/
211
+ # List sensitive metadata for PDFs only
212
+ # Replaces legacy /api/pdf/sensitivemeta/list/
213
+ path(
214
+ "media/pdfs/sensitive-metadata/",
215
+ pdf_sensitive_metadata_list,
216
+ name="pdf-sensitive-metadata-list"
217
+ ),
26
218
 
27
219
  # PDF media endpoints
28
- path("media/pdfs/", PDFMediaView.as_view(), name="pdf-list"),
29
- path("media/pdfs/<int:pk>/", PDFMediaView.as_view(), name="pdf-detail"),
30
- path("media/pdfs/<int:pk>/stream/", PDFMediaView.as_view(), name="pdf-stream"),
220
+ path("media/pdfs/", PdfMediaView.as_view(), name="pdf-list"),
221
+ path("media/pdfs/<int:pk>/", PdfMediaView.as_view(), name="pdf-detail"),
222
+ path("media/pdfs/<int:pk>/stream/", PdfMediaView.as_view(), name="pdf-stream"),
223
+
224
+ # PDF Re-import API endpoint (modern media framework)
225
+ # POST /api/media/pdfs/<int:pk>/reimport/
226
+ # Re-imports a PDF file to regenerate metadata when OCR failed or data is incomplete
227
+ path("media/pdfs/<int:pk>/reimport/", PdfReimportView.as_view(), name="pdf-reimport"),
31
228
  ]
32
229
  # ---------------------------------------------------------------------------------------
endoreg_db/urls/report.py CHANGED
@@ -2,10 +2,7 @@ from django.urls import path
2
2
  from endoreg_db.views import (
3
3
  ReportListView,
4
4
  ReportWithSecureUrlView,
5
- SecureFileUrlView,
6
5
  ReportFileMetadataView,
7
- SecureFileServingView,
8
- validate_secure_url,
9
6
  )
10
7
 
11
8
  url_patterns = [ # ---------------------------------------------------------------------------------------
@@ -40,16 +37,6 @@ url_patterns = [ # ------------------------------------------------------
40
37
  name='report_with_secure_url'
41
38
  ),
42
39
 
43
- # API-Endpunkt für manuelle sichere URL-Generierung
44
- # POST /api/secure-file-urls/
45
- # Body: {"report_id": 123, "file_type": "pdf"}
46
- # Generiert eine neue sichere URL für einen bestehenden Report
47
- path(
48
- 'secure-file-urls/',
49
- SecureFileUrlView.as_view(),
50
- name='generate_secure_file_url'
51
- ),
52
-
53
40
  # API-Endpunkt für Report-Datei-Metadaten
54
41
  # GET /api/reports/{report_id}/file-metadata/
55
42
  # Gibt Datei-Metadaten zurück (Größe, Typ, Datum, etc.)
@@ -58,21 +45,4 @@ url_patterns = [ # ------------------------------------------------------
58
45
  ReportFileMetadataView.as_view(),
59
46
  name='report_file_metadata'
60
47
  ),
61
-
62
- # Sichere Datei-Serving-Endpunkt mit Token-Validierung
63
- # GET /api/reports/{report_id}/secure-file/?token={token}
64
- # Serviert die tatsächliche Datei über eine sichere, tokenbasierte URL
65
- path(
66
- 'reports/<int:report_id>/secure-file/',
67
- SecureFileServingView.as_view(),
68
- name='secure_file_serving'
69
- ),
70
- # URL-Validierungs-Endpunkt
71
- # GET /api/validate-secure-url/?url={url}
72
- # Validiert, ob eine sichere URL noch gültig ist
73
- path(
74
- 'validate-secure-url/',
75
- validate_secure_url,
76
- name='validate_secure_url'
77
- ),
78
48
  ]
@@ -1,36 +0,0 @@
1
- from django.urls import path
2
-
3
- # Prefer explicit imports to avoid relying on parent __init__ exports
4
- from endoreg_db.views import (
5
- SensitiveMetaDetailView,
6
- SensitiveMetaVerificationView,
7
- SensitiveMetaListView,
8
- )
9
-
10
- urlpatterns = [
11
-
12
- # Sensitive Meta Detail API
13
- path(
14
- 'pdf/sensitivemeta/<int:sensitive_meta_id>/',
15
- SensitiveMetaDetailView.as_view(),
16
- name='sensitive_meta_detail'
17
- ),
18
- # Alternative endpoint for query parameter access (backward compatibility)
19
- path(
20
- 'pdf/sensitivemeta/',
21
- SensitiveMetaDetailView.as_view(),
22
- name='sensitive_meta_query'
23
- ),
24
-
25
- # Sensitive Meta Verification API
26
- path(
27
- 'pdf/sensitivemeta/verify/',
28
- SensitiveMetaVerificationView.as_view(),
29
- name='sensitive_meta_verify'),
30
-
31
- # Sensitive Meta List API
32
- path(
33
- 'pdf/sensitivemeta/list/',
34
- SensitiveMetaListView.as_view(),
35
- name='sensitive_meta_list'),
36
- ]
endoreg_db/urls/video.py CHANGED
@@ -1,17 +1,10 @@
1
1
  from django.urls import path
2
2
 
3
3
  from endoreg_db.views import (
4
- VideoReimportView,
5
4
  SensitiveMetaDetailView,
6
5
  VideoLabelView,
7
- VideoStreamView,
8
- # Video Correction Views (Phase 1.1)
9
- VideoMetadataView,
10
- VideoProcessingHistoryView,
11
- VideoAnalyzeView,
12
- VideoApplyMaskView,
13
- VideoRemoveFramesView,
14
- VideoReprocessView,
6
+ # Note: VideoStreamView moved to modern media framework. See: endoreg_db/urls/media.py
7
+ # Note: All Video Correction Views moved to modern media framework. See: endoreg_db/urls/media.py
15
8
  )
16
9
 
17
10
  url_patterns = [
@@ -24,23 +17,22 @@ url_patterns = [
24
17
  name='video_label_segments'
25
18
  ),
26
19
 
27
- # Video streaming API endpoint
28
- # GET /api/videostream/<int:pk>/
29
- # Streams the raw video file with HTTP range and CORS support
30
- path(
31
- 'videostream/<int:pk>/',
32
- VideoStreamView.as_view(),
33
- name='video_stream'
34
- ),
20
+ # ---------------------------------------------------------------------------------------
21
+ # VIDEO STREAMING - MOVED TO MODERN MEDIA FRAMEWORK
22
+ #
23
+ # Video streaming endpoint has been migrated to the media framework
24
+ # as of October 14, 2025. Please use the new endpoints:
25
+ #
26
+ # OLD → NEW:
27
+ # GET /api/videostream/<pk>/ → GET /api/media/videos/<pk>/
28
+ # GET /api/videostream/<pk>/ → GET /api/media/videos/<pk>/stream/
29
+ #
30
+ # See: endoreg_db/urls/media.py for new URL registrations
31
+ # ---------------------------------------------------------------------------------------
32
+
33
+ # Note: Video Re-import moved to modern media framework
34
+ # See: endoreg_db/urls/media.py - POST /api/media/videos/<int:pk>/reimport/
35
35
 
36
- # Video Re-import API endpoint
37
- # POST /api/video/<int:video_id>/reimport/
38
- # Re-imports a video file to regenerate metadata when OCR failed or data is incomplete
39
- path(
40
- 'video/<int:video_id>/reimport/',
41
- VideoReimportView.as_view(),
42
- name='video_reimport'
43
- ),
44
36
  # Video Sensitive Meta endpoints (for video anonymization)
45
37
  # GET /api/video/sensitivemeta/<int:sensitive_meta_id>/
46
38
  # PATCH /api/video/sensitivemeta/<int:sensitive_meta_id>/
@@ -51,69 +43,19 @@ url_patterns = [
51
43
  ),
52
44
 
53
45
  # ---------------------------------------------------------------------------------------
54
- # VIDEO CORRECTION API ENDPOINTS (Phase 1.1)
46
+ # VIDEO CORRECTION API ENDPOINTS - MOVED TO MODERN MEDIA FRAMEWORK
47
+ #
48
+ # All video correction endpoints have been migrated to the modern media framework
49
+ # as of October 14, 2025. Please use the new endpoints:
50
+ #
51
+ # OLD → NEW:
52
+ # GET /api/video-metadata/<id>/ → GET /api/media/videos/<pk>/metadata/
53
+ # GET /api/video-processing-history/<id>/ → GET /api/media/videos/<pk>/processing-history/
54
+ # POST /api/video-analyze/<id>/ → POST /api/media/videos/<pk>/analyze/
55
+ # POST /api/video-apply-mask/<id>/ → POST /api/media/videos/<pk>/apply-mask/
56
+ # POST /api/video-remove-frames/<id>/ → POST /api/media/videos/<pk>/remove-frames/
57
+ # POST /api/video-reprocess/<id>/ → POST /api/media/videos/<pk>/reprocess/
55
58
  #
56
- # These endpoints enable video correction workflows:
57
- # - Analysis: Detect sensitive frames using MiniCPM-o 2.6 or OCR+LLM
58
- # - Masking: Apply device-specific masks or custom ROI masks
59
- # - Frame Removal: Remove sensitive frames from videos
60
- # - Processing History: Track all correction operations
59
+ # See: endoreg_db/urls/media.py for new URL registrations
61
60
  # ---------------------------------------------------------------------------------------
62
-
63
- # Video Metadata API
64
- # GET /api/video-metadata/<int:id>/
65
- # Returns analysis results (sensitive frame count, ratio, frame IDs)
66
- path(
67
- 'video-metadata/<int:id>/',
68
- VideoMetadataView.as_view(),
69
- name='video_metadata'
70
- ),
71
-
72
- # Video Processing History API
73
- # GET /api/video-processing-history/<int:id>/
74
- # Returns history of all processing operations (masking, frame removal, analysis)
75
- path(
76
- 'video-processing-history/<int:id>/',
77
- VideoProcessingHistoryView.as_view(),
78
- name='video_processing_history'
79
- ),
80
-
81
- # Video Analysis API
82
- # POST /api/video-analyze/<int:id>/
83
- # Analyzes video for sensitive frames using MiniCPM-o 2.6 or OCR+LLM
84
- # Body: { detection_method: 'minicpm'|'ocr_llm'|'hybrid', sample_interval: 30 }
85
- path(
86
- 'video-analyze/<int:id>/',
87
- VideoAnalyzeView.as_view(),
88
- name='video_analyze'
89
- ),
90
-
91
- # Video Masking API
92
- # POST /api/video-apply-mask/<int:id>/
93
- # Applies device mask or custom ROI mask to video
94
- # Body: { mask_type: 'device'|'custom', device_name: 'olympus', roi: {...} }
95
- path(
96
- 'video-apply-mask/<int:id>/',
97
- VideoApplyMaskView.as_view(),
98
- name='video_apply_mask'
99
- ),
100
-
101
- # Frame Removal API
102
- # POST /api/video-remove-frames/<int:id>/
103
- # Removes specified frames from video
104
- # Body: { frame_list: [10,20,30] OR frame_ranges: '10-20,30' OR detection_method: 'automatic' }
105
- path(
106
- 'video-remove-frames/<int:id>/',
107
- VideoRemoveFramesView.as_view(),
108
- name='video_remove_frames'
109
- ),
110
-
111
- # Video Reprocessing API
112
- # POST /api/video-reprocess/<int:id>/
113
- # Re-runs entire anonymization pipeline for a video
114
- path(
115
- 'video-reprocess/<int:id>/',
116
- VideoReprocessView.as_view(),
117
- name='video_reprocess'
118
- ),
119
61
  ]
endoreg_db/utils/paths.py CHANGED
@@ -8,19 +8,12 @@ It provides a unified dictionary 'data_paths' for accessing all path objects.
8
8
  from logging import getLogger
9
9
  logger = getLogger(__name__)
10
10
 
11
- import os
12
11
  from pathlib import Path
13
12
  from typing import Dict
14
- import dotenv
15
13
 
16
- # Only load .env in non-pytest contexts to avoid leaking dev settings into tests
17
- if not os.environ.get("PYTEST_CURRENT_TEST"):
18
- dotenv.load_dotenv()
19
- else:
20
- logger.debug("Skipping .env load under pytest")
14
+ from endoreg_db.config.env import env_path
21
15
 
22
- # Define BASE_DIR as the project root (endoreg_db/utils -> endoreg_db -> repo root)
23
- STORAGE_DIR = Path(os.getenv("STORAGE_DIR"))
16
+ STORAGE_DIR = env_path("STORAGE_DIR", "storage")
24
17
 
25
18
  # Resolve STORAGE_DIR from env or default under BASE_DIR
26
19
  #def _resolve_storage_dir() -> Path:
@@ -36,7 +29,6 @@ STORAGE_DIR = Path(os.getenv("STORAGE_DIR"))
36
29
  STORAGE_DIR.mkdir(parents=True, exist_ok=True)
37
30
 
38
31
  PREFIX_RAW = "raw_"
39
- STORAGE_DIR_NAME = "data"
40
32
  IMPORT_DIR_NAME = "import"
41
33
  EXPORT_DIR_NAME = "export"
42
34
 
@@ -1,6 +1,8 @@
1
+ import os
1
2
  import subprocess
2
3
  import json
3
4
  import logging
5
+ from functools import lru_cache
4
6
  from pathlib import Path
5
7
  from typing import List, Dict, Optional, Tuple
6
8
  import cv2
@@ -13,6 +15,67 @@ logger = logging.getLogger("ffmpeg_wrapper")
13
15
  _nvenc_available = None
14
16
  _preferred_encoder = None
15
17
 
18
+
19
+ @lru_cache(maxsize=1)
20
+ def _resolve_ffmpeg_executable() -> Optional[str]:
21
+ """Locate the ffmpeg executable using multiple discovery strategies."""
22
+ # 1) Explicit overrides via env vars
23
+ env_candidates = [
24
+ os.environ.get("FFMPEG_EXECUTABLE"),
25
+ os.environ.get("FFMPEG_BINARY"),
26
+ os.environ.get("FFMPEG_PATH"),
27
+ ]
28
+
29
+ # 2) Django settings overrides (if Django is configured)
30
+ try:
31
+ from django.conf import settings # type: ignore
32
+
33
+ env_candidates.extend(
34
+ getattr(settings, attr)
35
+ for attr in ("FFMPEG_EXECUTABLE", "FFMPEG_BINARY", "FFMPEG_PATH")
36
+ if hasattr(settings, attr)
37
+ )
38
+ except Exception:
39
+ # Django might not be configured for every consumer
40
+ pass
41
+
42
+ # Normalize and verify explicit candidates
43
+ for candidate in env_candidates:
44
+ if not candidate:
45
+ continue
46
+ candidate_path = Path(candidate)
47
+ if candidate_path.is_dir():
48
+ candidate_path = candidate_path / "ffmpeg"
49
+ if candidate_path.exists() and os.access(candidate_path, os.X_OK):
50
+ logger.debug("Using ffmpeg executable override at %s", candidate_path)
51
+ return str(candidate_path)
52
+
53
+ # 3) PATH lookup (shutil.which)
54
+ via_path = shutil.which("ffmpeg")
55
+ if via_path:
56
+ return via_path
57
+
58
+ # 4) Common fallback locations (useful for Nix-based environments)
59
+ nix_store = Path("/nix/store")
60
+ if nix_store.exists():
61
+ patterns = (
62
+ "*-ffmpeg-*/bin/ffmpeg",
63
+ "*-ffmpeg-headless-*/bin/ffmpeg",
64
+ "*-ffmpeg-headless*/bin/ffmpeg",
65
+ )
66
+ for pattern in patterns:
67
+ matches = sorted(nix_store.glob(pattern))
68
+ if matches:
69
+ logger.debug("Discovered ffmpeg in nix store at %s", matches[-1])
70
+ return str(matches[-1])
71
+
72
+ # 5) Final fallback to standard Unix locations
73
+ for fallback in (Path("/usr/bin/ffmpeg"), Path("/usr/local/bin/ffmpeg")):
74
+ if fallback.exists() and os.access(fallback, os.X_OK):
75
+ return str(fallback)
76
+
77
+ return None
78
+
16
79
  def _detect_nvenc_support() -> bool:
17
80
  """
18
81
  Detect if NVIDIA NVENC hardware acceleration is available.
@@ -163,7 +226,7 @@ def is_ffmpeg_available() -> bool:
163
226
  Returns:
164
227
  True if FFmpeg is found in the PATH; otherwise, False.
165
228
  """
166
- return shutil.which("ffmpeg") is not None
229
+ return _resolve_ffmpeg_executable() is not None
167
230
 
168
231
  def check_ffmpeg_availability():
169
232
  """
@@ -607,8 +670,8 @@ def extract_frames(
607
670
  Returns:
608
671
  A list of Path objects for the extracted frames.
609
672
  """
610
- # Check if ffmpeg command exists
611
- ffmpeg_executable = shutil.which("ffmpeg")
673
+ # Resolve ffmpeg executable with multiple fallbacks
674
+ ffmpeg_executable = _resolve_ffmpeg_executable()
612
675
  if not ffmpeg_executable:
613
676
  error_msg = "ffmpeg command not found. Ensure FFmpeg is installed and in the system's PATH."
614
677
  logger.error(error_msg)
@@ -691,7 +754,7 @@ def extract_frame_range(
691
754
  logger.warning("extract_frame_range called with start_frame (%d) >= end_frame (%d). No frames to extract.", start_frame, end_frame)
692
755
  return []
693
756
 
694
- ffmpeg_executable = shutil.which("ffmpeg")
757
+ ffmpeg_executable = _resolve_ffmpeg_executable()
695
758
  if not ffmpeg_executable:
696
759
  error_msg = "ffmpeg command not found. Ensure FFmpeg is installed and in the system's PATH."
697
760
  logger.error(error_msg)