diff --git a/api/api_specification.json b/api/api_specification.json index 5dd606c02d7c1a9c883648d909661f4ae9ec3abd..5790d4b0d46d09990889a6f4cb3d9b49af25a2f3 100644 --- a/api/api_specification.json +++ b/api/api_specification.json @@ -1078,6 +1078,15 @@ "": { "fields": { "": { + "is_automatic_media_process_scheduler_enabled": { + "config_directly_modifiable": false, + "id": "is_automatic_media_process_scheduler_enabled", + "notes": "", + "object_variant": null, + "only_mod": false, + "optional": false, + "type": "boolean" + }, "job_context": { "config_directly_modifiable": false, "id": "job_context", @@ -1114,6 +1123,15 @@ "optional": false, "type": "media_process" }, + "process_sha256": { + "config_directly_modifiable": false, + "id": "process_sha256", + "notes": "SHA256 of the current process for this lecture", + "object_variant": null, + "only_mod": false, + "optional": false, + "type": "string" + }, "publish_media": { "config_directly_modifiable": false, "id": "publish_media", @@ -1168,10 +1186,19 @@ "": { "fields": { "": { - "scheduled": { + "job_id": { "config_directly_modifiable": false, - "id": "scheduled", - "notes": "If false, another scheduler is probably still queued/running", + "id": "job_id", + "notes": "The id of an already running scheduler job or the newly scheduled job", + "object_variant": null, + "only_mod": false, + "optional": false, + "type": "int" + }, + "scheduled_new": { + "config_directly_modifiable": false, + "id": "scheduled_new", + "notes": "If false, the scheduler job already existed", "object_variant": null, "only_mod": false, "optional": false, @@ -2927,6 +2954,15 @@ "optional": false, "type": "string" }, + "crf": { + "config_directly_modifiable": false, + "id": "crf", + "notes": "", + "object_variant": null, + "only_mod": false, + "optional": true, + "type": "int" + }, "framerate": { "config_directly_modifiable": false, "id": "framerate", @@ -3490,6 +3526,15 @@ } }, "rescale_video": { + "crf": { + "config_directly_modifiable": false, + "id": "crf", + "notes": "", + "object_variant": "rescale_video", + "only_mod": false, + "optional": true, + "type": "int" + }, "input_id": { "config_directly_modifiable": false, "id": "input_id", diff --git a/api/api_specification.md b/api/api_specification.md index f6433b828c84bab383d988df9d4c945d156d2834..28e8aeb5d1d82b95ecbb4530e32f834644fb0877 100644 --- a/api/api_specification.md +++ b/api/api_specification.md @@ -866,6 +866,11 @@ This may only be used by a moderator. <th>Notes</th> </thead> <tbody> + <tr> + <td>is_automatic_media_process_scheduler_enabled</td> + <td>boolean</td> + <td></td> + </tr> <tr> <td>job_context</td> <td>job{}</td> @@ -886,6 +891,11 @@ This may only be used by a moderator. <td>media_process</td> <td>The current process for this lecture</td> </tr> + <tr> + <td>process_sha256</td> + <td>string</td> + <td>SHA256 of the current process for this lecture</td> + </tr> <tr> <td>publish_media</td> <td>publish_medium{}</td> @@ -923,9 +933,14 @@ This may only be used by a moderator and requires the CSRF Token. </thead> <tbody> <tr> - <td>scheduled</td> + <td>job_id</td> + <td>int</td> + <td>The id of an already running scheduler job or the newly scheduled job</td> + </tr> + <tr> + <td>scheduled_new</td> <td>boolean</td> - <td>If false, another scheduler is probably still queued/running</td> + <td>If false, the scheduler job already existed</td> </tr> </tbody> </table> @@ -2172,6 +2187,11 @@ Additionally, the following objects may appear as the type of some field: <td>string</td> <td>Possible values: <code>h264</code>, <code>vp9</code>, <code>av1</code>, <code>opus</code>, <code>flac</code>, <code>aac</code></td> </tr> + <tr> + <td>crf</td> + <td>?int</td> + <td></td> + </tr> <tr> <td>framerate</td> <td>?int</td> @@ -2545,7 +2565,12 @@ Additionally, the following objects may appear as the type of some field: <td></td> </tr> <tr> - <td rowspan="3"><code>rescale_video</code></td> + <td rowspan="4"><code>rescale_video</code></td> + <td>crf</td> + <td>?int</td> + <td></td> + </tr> + <tr> <td>input_id</td> <td>string</td> <td>Must match <code>[a-zA-Z0-9_]{1,100}</code></td> @@ -3188,6 +3213,18 @@ Possible `error_code`: ## Changelog +### v0.83 + +* Updated `GET /lecture/{lecture_id}/media_process_overview` + * Added `is_automatic_media_process_scheduler_enabled` field + * Added `process_sha256` field +* Updated `POST /lecture/{lecture_id}/run_media_process_scheduler` + * Changed response to same format as run_source_file_sorter +* Updated `ffmpeg_filter_graph_output_stream` + * Added `crf` field +* Updated `media_process_target_producer` + * Added `crf` field for type `rescale_video` + ### v0.82 * Changed `publish_medium` diff --git a/api/api_specification_template.md b/api/api_specification_template.md index 03f847cae26fd486c562576fb1daf96f6772a0c4..36b13efed48bba55f952d2c0f709a09929505920 100644 --- a/api/api_specification_template.md +++ b/api/api_specification_template.md @@ -139,6 +139,18 @@ Possible `error_code`: ## Changelog +### v0.83 + +* Updated `GET /lecture/{lecture_id}/media_process_overview` + * Added `is_automatic_media_process_scheduler_enabled` field + * Added `process_sha256` field +* Updated `POST /lecture/{lecture_id}/run_media_process_scheduler` + * Changed response to same format as run_source_file_sorter +* Updated `ffmpeg_filter_graph_output_stream` + * Added `crf` field +* Updated `media_process_target_producer` + * Added `crf` field for type `rescale_video` + ### v0.82 * Changed `publish_medium` diff --git a/api/config/db_test_data.sql b/api/config/db_test_data.sql index 06f4b8a27f1c5b7919edad4dc43ceac6c0a062d9..c3c942cd766565fd786a0546f3b5cb5d4c3f468d 100644 --- a/api/config/db_test_data.sql +++ b/api/config/db_test_data.sql @@ -33,28 +33,28 @@ INSERT INTO "user" (id,handle,display_name,full_name,email,last_login,enable_mai SELECT setval('user_id_seq', 1000); -INSERT INTO course (id,handle,full_name,short_name,semester,organizer,topic,description,show_chapters_on_course,authentication_information,listed,internal_comment,allow_download,allow_embed,media_process,disable_automatic_media_process_scheduler,deleted,visible,view_perm_type,view_perm_rwth_auth,view_perm_fsmpi_auth,view_perm_moodle_course_ids,view_perm_passwords) VALUES - (2,'07ws-buk','Berechenbarkeit und Komplexität','BuK','2007ws','Prof. Vöcking','Informatik','Seite zur Veranstaltung...',false,'',true,'',true,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}',true,false,true,'authentication'::view_permissions_type,true,true,'{}','{}'), - (3,'07ws-diskrete','Diskrete Strukturen','Diskrete','2007ws','Prof. Hiß','Informatik','Von dieser Vorlesungsreihe fehlen die ersten zwei Monate. Wenn wir die Gelegenheit bekommen, filmen wir gerne nochmal.',false,'',true,'',true,false,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}',true,false,true,'private'::view_permissions_type,false,false,'{}','{}'), - (13,'09ss-fosap','Formale Systeme, Automaten, Prozesse','FoSAP','2009ss','Prof. Rossmanith','Informatik','Seite des Lehrstuhls ...',false,'',true,'',true,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}',true,false,false,'inherit'::view_permissions_type,false,false,'{}','{}'), - (62,'11ws-infin','Investition und Finanzierung','InFin','2011ws','Prof. Breuer','BWL','Seite im Campus ...',false,'',false,'',false,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}',true,false,true,'authentication'::view_permissions_type,false,false,'{1}','{}'), - (1,'del','Something deleted','S','2007ws','','','Seite zur Veranstaltung...',false,'',true,'',true,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}',true,true,true,'inherit'::view_permissions_type,false,false,'{}','{}'); +INSERT INTO course (id,handle,full_name,short_name,semester,organizer,topic,description,show_chapters_on_course,authentication_information,listed,internal_comment,allow_download,allow_embed,media_process,automatic_media_process_scheduler_state,deleted,visible,view_perm_type,view_perm_rwth_auth,view_perm_fsmpi_auth,view_perm_moodle_course_ids,view_perm_passwords) VALUES + (2,'07ws-buk','Berechenbarkeit und Komplexität','BuK','2007ws','Prof. Vöcking','Informatik','Seite zur Veranstaltung...',false,'',true,'',true,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}','inherit'::automatic_media_process_scheduler_state,false,true,'authentication'::view_permissions_type,true,true,'{}','{}'), + (3,'07ws-diskrete','Diskrete Strukturen','Diskrete','2007ws','Prof. Hiß','Informatik','Von dieser Vorlesungsreihe fehlen die ersten zwei Monate. Wenn wir die Gelegenheit bekommen, filmen wir gerne nochmal.',false,'',true,'',true,false,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}','inherit'::automatic_media_process_scheduler_state,false,true,'private'::view_permissions_type,false,false,'{}','{}'), + (13,'09ss-fosap','Formale Systeme, Automaten, Prozesse','FoSAP','2009ss','Prof. Rossmanith','Informatik','Seite des Lehrstuhls ...',false,'',true,'',true,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}','inherit'::automatic_media_process_scheduler_state,false,false,'inherit'::view_permissions_type,false,false,'{}','{}'), + (62,'11ws-infin','Investition und Finanzierung','InFin','2011ws','Prof. Breuer','BWL','Seite im Campus ...',false,'',false,'',false,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}','inherit'::automatic_media_process_scheduler_state,false,true,'authentication'::view_permissions_type,false,false,'{1}','{}'), + (1,'del','Something deleted','S','2007ws','','','Seite zur Veranstaltung...',false,'',true,'',true,true,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video"}, {"type" : "sample_thumbnail", "input_id" : "video", "timestamp_percent" : 20, "output_id" : "thumbnail"}], "publish_target_ids" : ["video", "thumbnail"], "publish_wait_for_full_process" : true}','inherit'::automatic_media_process_scheduler_state,true,true,'inherit'::view_permissions_type,false,false,'{}','{}'); SELECT setval('course_id_seq', 1000); -INSERT INTO lecture (id,course_id,title,speaker,"location","time",duration,description,no_recording,livestream_planned,internal_comment,publish_time,media_duration_sec,media_process,disable_automatic_media_process_scheduler,current_media_process_error,deleted,visible,view_perm_type,view_perm_rwth_auth,view_perm_fsmpi_auth,view_perm_moodle_course_ids,view_perm_passwords) VALUES - (1,2,'Einführung zur Berechenbarkeit','','','2007-10-19 12:00:00',0,'',false,false,'',NULL,NULL,NULL,false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (2,2,'Einführung zur Berechenbarkeit','','','2007-10-23 08:30:00',0,'',false,false,'',NULL,NULL,NULL,false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (26,3,'Hamiltonkreis, Eulertour, Eulerweg','','','2007-12-18 13:30:00',0,'',false,false,'',NULL,NULL,NULL,false,NULL,false,false,'inherit'::view_permissions_type,false,false,'{}','{}'), - (29,3,'Modulare Arithmetik: Gruppe, Ring, Körper, abelsche Gruppe, Untergruppe, Einheitengruppe. Restklassenringe, Primzahl.','','','2008-01-17 08:15:00',0,'',false,false,'',NULL,NULL,NULL,false,NULL,false,true,'authentication'::view_permissions_type,false,false,'{}','{"t": "t"}'), - (185,13,'Organisatorisches, Motivation, Künstliche Pflanzen, Alphabete, Wörter, Sprachen','','','2009-04-16 10:00:00',90,'Sorry für den schlechten Ton',false,false,'',NULL,NULL,NULL,false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (187,13,'Reguläre Ausdrücke, Endliche Automaten','','','2009-04-23 10:00:00',90,'',false,false,'',NULL,NULL,NULL,false,NULL,true,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (1187,62,'','','Aula','2011-10-17 18:30:00',90,'noch kein Titel',false,false,'',NULL,NULL,NULL,false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (1188,62,'','','Aula','2050-10-24 18:30:00',90,'noch kein Titel',false,false,'',NULL,NULL,NULL,false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (3,2,'Einführung zur Berechenbarkeit','','','2007-10-26 12:00:00',0,'',false,false,'',NULL,5243,NULL,false,NULL,false,true,'public'::view_permissions_type,false,false,'{}','{}'), - (25,3,'Graphentheorie: Grundbegriffe, Datenstrukturen, Algorithmus für Breitensuche','','','2007-12-11 13:30:00',0,'',false,false,'','2007-12-12 19:12:04',5420,NULL,false,NULL,false,true,'public'::view_permissions_type,false,false,'{}','{}'), - (1186,62,'Einführung, I. Grundlagen','','Aula','2011-10-10 18:30:00',90,'',false,false,'','2011-10-17 14:33:45',5001,NULL,false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), - (186,13,'Alphabete, Wörter, Sprachen, Reguläre Ausdrücke','','','2009-04-21 08:15:00',45,'',false,false,'','2009-05-18 03:13:20',5431,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video_1080"}, {"type" : "sample_thumbnail", "input_id" : "video_1080", "timestamp_percent" : 20, "output_id" : "thumbnail"}, {"type": "rescale_video", "input_id": "video_1080", "target_vertical_resolution": 720, "output_id": "video_720"}, {"type": "downscale_video", "input_id": "video_480", "target_vertical_resolution": 480, "output_id": "video_480"}], "publish_target_ids" : ["video_1080", "video_720", "video_480", "thumbnail"], "publish_wait_for_full_process" : true}',false,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'); +INSERT INTO lecture (id,course_id,title,speaker,"location","time",duration,description,no_recording,livestream_planned,internal_comment,publish_time,media_duration_sec,media_process,automatic_media_process_scheduler_state,current_media_process_error,deleted,visible,view_perm_type,view_perm_rwth_auth,view_perm_fsmpi_auth,view_perm_moodle_course_ids,view_perm_passwords) VALUES + (1,2,'Einführung zur Berechenbarkeit','','','2007-10-19 12:00:00',0,'',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (2,2,'Einführung zur Berechenbarkeit','','','2007-10-23 08:30:00',0,'',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (26,3,'Hamiltonkreis, Eulertour, Eulerweg','','','2007-12-18 13:30:00',0,'',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,false,'inherit'::view_permissions_type,false,false,'{}','{}'), + (29,3,'Modulare Arithmetik: Gruppe, Ring, Körper, abelsche Gruppe, Untergruppe, Einheitengruppe. Restklassenringe, Primzahl.','','','2008-01-17 08:15:00',0,'',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'authentication'::view_permissions_type,false,false,'{}','{"t": "t"}'), + (185,13,'Organisatorisches, Motivation, Künstliche Pflanzen, Alphabete, Wörter, Sprachen','','','2009-04-16 10:00:00',90,'Sorry für den schlechten Ton',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (187,13,'Reguläre Ausdrücke, Endliche Automaten','','','2009-04-23 10:00:00',90,'',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,true,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (1187,62,'','','Aula','2011-10-17 18:30:00',90,'noch kein Titel',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (1188,62,'','','Aula','2050-10-24 18:30:00',90,'noch kein Titel',false,false,'',NULL,NULL,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (3,2,'Einführung zur Berechenbarkeit','','','2007-10-26 12:00:00',0,'',false,false,'',NULL,5243,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'public'::view_permissions_type,false,false,'{}','{}'), + (25,3,'Graphentheorie: Grundbegriffe, Datenstrukturen, Algorithmus für Breitensuche','','','2007-12-11 13:30:00',0,'',false,false,'','2007-12-12 19:12:04',5420,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'public'::view_permissions_type,false,false,'{}','{}'), + (1186,62,'Einführung, I. Grundlagen','','Aula','2011-10-10 18:30:00',90,'',false,false,'','2011-10-17 14:33:45',5001,NULL,'inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'), + (186,13,'Alphabete, Wörter, Sprachen, Reguläre Ausdrücke','','','2009-04-21 08:15:00',45,'',false,false,'','2009-05-18 03:13:20',5431,'{"producers" : [{"type" : "source_file", "tag" : "", "output_id" : "video_1080"}, {"type" : "sample_thumbnail", "input_id" : "video_1080", "timestamp_percent" : 20, "output_id" : "thumbnail"}, {"type": "rescale_video", "input_id": "video_1080", "target_vertical_resolution": 720, "output_id": "video_720"}, {"type": "downscale_video", "input_id": "video_480", "target_vertical_resolution": 480, "output_id": "video_480"}], "publish_target_ids" : ["video_1080", "video_720", "video_480", "thumbnail"], "publish_wait_for_full_process" : true}','inherit'::automatic_media_process_scheduler_state,NULL,false,true,'inherit'::view_permissions_type,false,false,'{}','{}'); SELECT setval('lecture_id_seq', 1000); diff --git a/api/migration.sql b/api/migration.sql index 629a69dd73533aaf7754007c23f318a39e7c2c13..8eacea3e2db626e1824e2f989cf275ace492e089 100644 --- a/api/migration.sql +++ b/api/migration.sql @@ -137,7 +137,7 @@ FROM old_data.announcements INSERT INTO data.course (id, deleted, visible, handle, full_name, short_name, semester, organizer, topic, description, show_chapters_on_course, authentication_information, listed, internal_comment, allow_download, allow_embed, media_process, view_perm_type, view_perm_rwth_auth, view_perm_fsmpi_auth, view_perm_moodle_course_ids, view_perm_passwords, - disable_automatic_media_process_scheduler) + automatic_media_process_scheduler_state) SELECT id, deleted, visible, handle, title, short, CASE @@ -178,7 +178,7 @@ SELECT 'publish_wait_for_full_process', True ), 'inherit', False, False, NULL, NULL, -- view_perm (done below) - True -- disable_automatic_media_process_scheduler + 'disabled'::automatic_media_process_scheduler_state FROM old_data.courses_data ; @@ -218,7 +218,7 @@ INSERT INTO data.lecture (id, deleted, visible, course_id, title, speaker, locat no_recording, livestream_planned, internal_comment, publish_time, view_perm_type, view_perm_rwth_auth, view_perm_fsmpi_auth, view_perm_moodle_course_ids, view_perm_passwords, - disable_automatic_media_process_scheduler) + automatic_media_process_scheduler_state) SELECT id, deleted, visible, course_id, title, speaker, place, "time", duration, comment, norecording, live, internal, ( @@ -233,7 +233,7 @@ SELECT id, deleted, visible, course_id, title, speaker, place, "time", duration, WHERE vid.lecture_id = old_lec.id AND vid.visible AND NOT vid.deleted ), -- publish_time 'inherit', False, False, NULL, NULL, -- view_perm (filled below) - False -- disable_automatic_media_process_scheduler (Already disabled by course) + 'inherit'::automatic_media_process_scheduler_state -- Disabled in course FROM old_data.lectures_data AS old_lec WHERE course_id <> 0 -- These are empty ; @@ -565,9 +565,9 @@ DO $$ IF use_custom_process THEN SELECT custom_process_producers || jsonb_build_object( 'type', 'sample_thumbnail', - 'source_id', thumbnail_producer_input_id, + 'input_id', thumbnail_producer_input_id, 'timestamp_percent', 20, - 'output_id', process_target_id + 'output_id', 'thumbnail' ) INTO custom_process_producers; SELECT custom_process_publish_target_ids || jsonb_build_array('thumbnail') INTO custom_process_publish_target_ids; diff --git a/api/src/api/routes/media_process.py b/api/src/api/routes/media_process.py index 0ad1ba541017ac1f3c93e4ebbc56fa8d39e3fcc0..c51f8fb1ca614c2eb4134d46dde046b99da5df3d 100644 --- a/api/src/api/routes/media_process.py +++ b/api/src/api/routes/media_process.py @@ -92,7 +92,7 @@ def api_route_run_source_file_sorter(): def trans(session: SessionDb): scheduled_new = False sorter_job = session.scalar( - Job.select(api_user_ac(), []) + Job.sudo_select() .where(Job.type == "source_file_sorter") .where(Job.state.in_([JobState.READY, JobState.SPAWNING, JobState.RUNNING])) .order_by(Job.creation_time.desc()) @@ -135,7 +135,9 @@ def api_route_get_media_process_templates(): response_description=f"Note that for the jobs there is a maximum recursion depth of {API_RELATIONSHIP_LOADING_MAXIMUM_RECURSION_DEPTH}", response_objects=[ ("process", "media_process", "The current process for this lecture"), + ("process_sha256", "string", "SHA256 of the current process for this lecture"), ("status", "string", "String with some information about the process (e.g. error messages)"), + ("is_automatic_media_process_scheduler_enabled", "boolean"), ("sorter_files", "sorter_file{}", "Key is id"), ("medium_files", "medium_file{}", "Key is id"), ("medium_metadata", "medium_metadata{}", "Key is id"), @@ -189,7 +191,9 @@ def api_route_get_lecture_media_process_overview(lecture_id: int): ) return { "status": lecture.current_media_process_error, + "is_automatic_media_process_scheduler_enabled": lecture.is_automatic_media_process_scheduler_enabled, "process": lecture.effective_media_process_obj.to_json(), + "process_sha256": lecture.effective_media_process_obj.sha256(), "sorter_files": sorter_file_context, "medium_files": medium_file_context, "medium_metadata": medium_metadata_context, @@ -200,30 +204,36 @@ def api_route_get_lecture_media_process_overview(lecture_id: int): @api_route("/lecture/<int:lecture_id>/run_media_process_scheduler", "POST", response_objects=[ - ("scheduled", "boolean", "If false, another scheduler is probably still queued/running"), + ("scheduled_new", "boolean", "If false, the scheduler job already existed"), + ("job_id", "int", "The id of an already running scheduler job or the newly scheduled job"), ]) @api_moderator_route(require_csrf_token=True) def api_route_run_media_process_scheduler(lecture_id: int): def trans(session: SessionDb): - if session.scalar( + scheduled_new = False + scheduler_job = session.scalar( Job.sudo_select() .where(Job.type == "media_process_scheduler") - .where(Job.state.in_([JobState.READY, JobState.SPAWNING, JobState.RUNNING])) .where(Job.input_data["lecture_id"].astext.cast(sql.Integer) == lecture_id) - .with_only_columns(sql.func.count()) - ) > 0: - return False - session.add(Job( - type="media_process_scheduler", - input_data={ - "lecture_id": lecture_id, - "manually_triggered": True - }, - cause_user_id=get_user_id() - )) - return True + .where(Job.state.in_([JobState.READY, JobState.SPAWNING, JobState.RUNNING])) + .order_by(Job.creation_time.desc()) + .limit(1) + ) + if scheduler_job is None: + scheduler_job = Job( + type="media_process_scheduler", + input_data={ + "lecture_id": lecture_id, + "manually_triggered": True + }, + cause_user_id=get_user_id() + ) + session.add(scheduler_job) + session.flush() # Ensure ID present + scheduled_new = True + return { + "scheduled_new": scheduled_new, + "job_id": scheduler_job.id, + } - return { - "scheduled": database.execute_write_transaction_and_commit(trans) - } - + return database.execute_write_transaction_and_commit(trans) diff --git a/common_py/src/videoag_common/objects/course.py b/common_py/src/videoag_common/objects/course.py index 72a3af04602d8dc1dbb59e8be6c7d6615b3659c3..1f9303a074bab976dbcb267b375177941fa2fb1b 100644 --- a/common_py/src/videoag_common/objects/course.py +++ b/common_py/src/videoag_common/objects/course.py @@ -6,6 +6,7 @@ from videoag_common.api_object import * from videoag_common.media_process import * from videoag_common.miscellaneous import * from .view_permissions import ViewPermissions, ApiViewPermissionsObject, DEFAULT_VIEW_PERMISSIONS +from ..miscellaneous.json import CJsonException if TYPE_CHECKING: # pragma: no cover # Can't actually import due to circular dependency @@ -69,6 +70,15 @@ responsible_table = sql.Table( ) +class AutomaticMediaProcessSchedulerState(JsonSerializableEnum): + ENABLED = "enabled" + DISABLED = "disabled" + INHERIT = "inherit" + + +AUTOMATIC_MEDIA_PROCESS_SCHEDULER_STATE = create_enum_type(AutomaticMediaProcessSchedulerState) + + class Lecture(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject, Base): __api_class__ = ApiObjectClass( parent_relationship_config_ids=["course"] @@ -171,11 +181,10 @@ class Lecture(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject, include_in_config=True ) ) - disable_automatic_media_process_scheduler: Mapped[bool] = api_mapped( - mapped_column(nullable=False, default=False), - ApiBooleanField( + automatic_media_process_scheduler_state: Mapped[AutomaticMediaProcessSchedulerState] = api_mapped( + mapped_column(AUTOMATIC_MEDIA_PROCESS_SCHEDULER_STATE, nullable=False, default=AutomaticMediaProcessSchedulerState.ENABLED), + ApiEnumField( include_in_config=True, - # Course also has this field. Processor only works automatically if both fields are False ) ) current_media_process_error: Mapped[str] = mapped_column(nullable=True) @@ -239,9 +248,25 @@ class Lecture(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject, @property def effective_media_process_obj(self) -> MediaProcess: - if self.media_process is not None: - return MediaProcess.from_json(CJsonValue(self.media_process).as_object()) - return MediaProcess.from_json(CJsonValue(self.course.media_process).as_object()) + try: + if self.media_process is not None: + return MediaProcess.from_json(CJsonValue(self.media_process).as_object()) + print(f"Course {self.course.id}. Py ID Self: {self}, Py ID Course: {self.course}") + print(self.course.media_process) + res = MediaProcess.from_json(CJsonValue(self.course.media_process).as_object()) + print(self.course.media_process) + return res + except CJsonException as e: + raise Exception("Invalid media process in database") from e + + @property + def is_automatic_media_process_scheduler_enabled(self) -> bool: + state = self.automatic_media_process_scheduler_state + if state == AutomaticMediaProcessSchedulerState.INHERIT: + state = self.course.automatic_media_process_scheduler_state + if state == AutomaticMediaProcessSchedulerState.INHERIT: + state = AutomaticMediaProcessSchedulerState.ENABLED + return state == AutomaticMediaProcessSchedulerState.ENABLED @api_include_in_data( data_if=lambda lec, args: args.include_media, @@ -377,11 +402,10 @@ class Course(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject, include_in_config=True ) ) - disable_automatic_media_process_scheduler: Mapped[bool] = api_mapped( - mapped_column(nullable=False, default=False), - ApiBooleanField( + automatic_media_process_scheduler_state: Mapped[AutomaticMediaProcessSchedulerState] = api_mapped( + mapped_column(AUTOMATIC_MEDIA_PROCESS_SCHEDULER_STATE, nullable=False, default=AutomaticMediaProcessSchedulerState.ENABLED), + ApiEnumField( include_in_config=True, - # Lecture also has this field. Processor only works automatically if both fields are False ) ) diff --git a/job_controller/jobs/media_process_scheduler/job.py b/job_controller/jobs/media_process_scheduler/job.py index 2ba51840cedae2fb47d8cb66caba2c7aa13aee40..a7fa1f8c8e992e7ed6e1cfc7a49793d74a7116d7 100644 --- a/job_controller/jobs/media_process_scheduler/job.py +++ b/job_controller/jobs/media_process_scheduler/job.py @@ -56,9 +56,7 @@ class ProcessScheduler: if self._lecture is None: raise Exception(f"Unknown lecture with id {self._lecture_id}") - if not self._manually_triggered \ - and (self._lecture.disable_automatic_media_process_scheduler - or self._lecture.course.disable_automatic_media_process_scheduler): + if not self._manually_triggered and not self._lecture.is_automatic_media_process_scheduler_enabled: logger.info("Skipping because automatic scheduler is disabled") return diff --git a/job_controller/jobs/media_process_scheduler/metadata.json b/job_controller/jobs/media_process_scheduler/metadata.json index 452581f25e4f39864ccb4cb8ab26da5b93a846e7..d2be677be3c6dfef0fb50e59b0a467e3ced57f23 100644 --- a/job_controller/jobs/media_process_scheduler/metadata.json +++ b/job_controller/jobs/media_process_scheduler/metadata.json @@ -8,12 +8,13 @@ "condition": [ "&", ["object_type", "=", "lecture"], - "|", "|", "|", "|", + "|", "|", "|", "|", "|", ["field_id", "=", "title"], ["field_id", "=", "speaker"], ["field_id", "=", "time"], ["field_id", "=", "location"], - ["field_id", "=", "media_process"] + ["field_id", "=", "media_process"], + ["field_id", "=", "automatic_media_process_scheduler_state"] ], "job_data": { "lecture_id": "${object_id}" @@ -25,9 +26,10 @@ "&", "&", ["object_type", "=", "lecture"], ["parent_object_type", "=", "course"], - "|", + "|", "|", ["field_id", "=", "full_name"], - ["field_id", "=", "media_process"] + ["field_id", "=", "media_process"], + ["field_id", "=", "automatic_media_process_scheduler_state"] ], "job_data": { "lecture_id": "${object_id}"