From 248cb07127d6967096515746c78815afdd2f88e5 Mon Sep 17 00:00:00 2001 From: Elvin Bounphengsy Date: Mon, 4 Mar 2024 14:29:34 -0500 Subject: [PATCH 1/3] Differentiate between different recording types - Store raw recording type - Multi-language support for friendly recording types - When possible, fix previously stored recording types - Corrected syntax to satisfy code checker - Apply suggestions from code review Co-authored-by: Jonathan Champ --- classes/task/get_meeting_recordings.php | 31 ++++++++++--------- classes/webservice.php | 20 ++++++++---- db/upgrade.php | 12 ++++++++ lang/en/zoom.php | 21 +++++++++++-- recordings.php | 41 ++++++++++++++++++++++++- 5 files changed, 102 insertions(+), 23 deletions(-) diff --git a/classes/task/get_meeting_recordings.php b/classes/task/get_meeting_recordings.php index 97457569..6d995770 100644 --- a/classes/task/get_meeting_recordings.php +++ b/classes/task/get_meeting_recordings.php @@ -76,11 +76,6 @@ public function execute() { mtrace('Finding meeting recordings for this account...'); - $recordingtypestrings = [ - 'audio' => get_string('recordingtypeaudio', 'mod_zoom'), - 'video' => get_string('recordingtypevideo', 'mod_zoom'), - ]; - $localmeetings = zoom_get_all_meeting_records(); $now = time(); @@ -109,17 +104,28 @@ public function execute() { $zoomrecordings = $service->get_user_recordings($hostid, $from, $to); foreach ($zoomrecordings as $recordingid => $recording) { - if (isset($localrecordings[$recording->meetinguuid][$recordingid])) { - mtrace('Recording id: ' . $recordingid . ' exists...skipping'); - continue; - } - if (empty($meetings[$recording->meetingid])) { // Skip meetings that are not in Moodle. mtrace('Meeting id: ' . $recording->meetingid . ' does not exist...skipping'); continue; } + $zoom = $meetings[$recording->meetingid]; + + if (isset($localrecordings[$recording->meetinguuid][$recordingid])) { + mtrace('Recording id: ' . $recordingid . ' exists...skipping'); + $localrecording = $localrecordings[$recording->meetinguuid][$recordingid]; + + if ($localrecording->recordingtype !== $zoom->recordingtype) { + $updatemeeting = (object) [ + 'id' => $localrecording->id, + 'recordingtype' => $zoom->recordingtype, + ]; + $DB->update_record('zoom_meeting_recordings', $updatemeeting); + } + continue; + } + // As of 2023-09-24, 'password' is not present in the user recordings API response. if (empty($meetingpasscodes[$recording->meetinguuid])) { try { @@ -130,16 +136,13 @@ public function execute() { } } - $zoom = $meetings[$recording->meetingid]; - $recordingtype = $recording->recordingtype; - $recordingtypestring = $recordingtypestrings[$recordingtype]; $record = new stdClass(); $record->zoomid = $zoom->id; $record->meetinguuid = $recording->meetinguuid; $record->zoomrecordingid = $recordingid; - $record->name = trim($zoom->name) . ' (' . $recordingtypestring . ')'; + $record->name = $zoom->name; $record->externalurl = $recording->url; $record->passcode = $meetingpasscodes[$recording->meetinguuid]; $record->recordingtype = $recordingtype; diff --git a/classes/webservice.php b/classes/webservice.php index f32c6299..1f22008c 100644 --- a/classes/webservice.php +++ b/classes/webservice.php @@ -994,6 +994,9 @@ public function get_recording_url_list($meetingid) { $allowedrecordingtypes = [ 'MP4' => 'video', 'M4A' => 'audio', + 'TRANSCRIPT' => 'transcript', + 'CHAT' => 'chat', + 'CC' => 'captions', ]; try { @@ -1002,13 +1005,14 @@ public function get_recording_url_list($meetingid) { if (!empty($response->recording_files)) { foreach ($response->recording_files as $recording) { - if (!empty($recording->play_url) && isset($allowedrecordingtypes[$recording->file_type])) { + $url = $recording->play_url ?? $recording->download_url ?? null; + if (!empty($url) && isset($allowedrecordingtypes[$recording->file_type])) { $recordinginfo = new stdClass(); $recordinginfo->recordingid = $recording->id; $recordinginfo->meetinguuid = $response->uuid; - $recordinginfo->url = $recording->play_url; + $recordinginfo->url = $url; $recordinginfo->filetype = $recording->file_type; - $recordinginfo->recordingtype = $allowedrecordingtypes[$recording->file_type]; + $recordinginfo->recordingtype = $recording->recording_type; $recordinginfo->passcode = $response->password; $recordinginfo->recordingstart = strtotime($recording->recording_start); @@ -1041,6 +1045,9 @@ public function get_user_recordings($userid, $from, $to) { $allowedrecordingtypes = [ 'MP4' => 'video', 'M4A' => 'audio', + 'TRANSCRIPT' => 'transcript', + 'CHAT' => 'chat', + 'CC' => 'captions', ]; try { @@ -1050,14 +1057,15 @@ public function get_user_recordings($userid, $from, $to) { foreach ($response as $meeting) { foreach ($meeting->recording_files as $recording) { - if (!empty($recording->play_url) && isset($allowedrecordingtypes[$recording->file_type])) { + $url = $recording->play_url ?? $recording->download_url ?? null; + if (!empty($url) && isset($allowedrecordingtypes[$recording->file_type])) { $recordinginfo = new stdClass(); $recordinginfo->recordingid = $recording->id; $recordinginfo->meetingid = $meeting->id; $recordinginfo->meetinguuid = $meeting->uuid; - $recordinginfo->url = $recording->play_url; + $recordinginfo->url = $url; $recordinginfo->filetype = $recording->file_type; - $recordinginfo->recordingtype = $allowedrecordingtypes[$recording->file_type]; + $recordinginfo->recordingtype = $recording->recording_type; $recordinginfo->recordingstart = strtotime($recording->recording_start); $recordings[$recording->id] = $recordinginfo; diff --git a/db/upgrade.php b/db/upgrade.php index 49b08626..f2d2f1e4 100755 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -965,5 +965,17 @@ function xmldb_zoom_upgrade($oldversion) { upgrade_mod_savepoint(true, 2024030100, 'zoom'); } + if ($oldversion < 2024030101) { + // Update existing recording names to default for translatable recordingtype strings. + $meetings = $DB->get_records('zoom'); + + foreach ($meetings as $meeting) { + $DB->set_field_select('zoom_meeting_recordings', 'name', $meeting->name, 'zoomid = ?', [$meeting->id]); + } + + // Zoom savepoint reached. + upgrade_mod_savepoint(true, 2024030101, 'zoom'); + } + return true; } diff --git a/lang/en/zoom.php b/lang/en/zoom.php index a5bee66f..e6084ded 100644 --- a/lang/en/zoom.php +++ b/lang/en/zoom.php @@ -333,8 +333,25 @@ $string['recordings'] = 'Recordings'; $string['recordingshow'] = 'Show Recording (Currently Hidden)'; $string['recordingshowtoggle'] = 'Toggle Show Recording'; -$string['recordingtypeaudio'] = 'Audio only'; -$string['recordingtypevideo'] = 'Video and Audio'; +$string['recordingtype_active_speaker'] = 'Active Speaker'; +$string['recordingtype_audio_interpretation'] = 'Audio Interpretation'; +$string['recordingtype_audio_only'] = 'Audio Only'; +$string['recordingtype_audio_transcript'] = 'Audio Transcript'; +$string['recordingtype_chat'] = 'Chat File'; +$string['recordingtype_closed_caption'] = 'Closed Caption'; +$string['recordingtype_gallery'] = 'Gallery View'; +$string['recordingtype_poll'] = 'Poll'; +$string['recordingtype_production_studio'] = 'Production Studio'; +$string['recordingtype_shared'] = 'Shared Screen'; +$string['recordingtype_shared_gallery'] = 'Shared Screen with Gallery View'; +$string['recordingtype_shared_speaker'] = 'Shared Screen with Speaker View'; +$string['recordingtype_shared_speaker_cc'] = 'Shared Screen with Speaker View (CC)'; +$string['recordingtype_sign'] = 'Sign Interpretation'; +$string['recordingtype_speaker'] = 'Speaker View'; +$string['recordingtype_summary'] = 'Summary'; +$string['recordingtype_summary_next_steps'] = 'Summary Next Steps'; +$string['recordingtype_summary_smart_chapters'] = 'Summary Smart Chapters'; +$string['recordingtype_timeline'] = 'Timeline'; $string['recordingurl'] = 'Recording URL'; $string['recordingview'] = 'View Recordings'; $string['recordingvisibility'] = 'Are recordings for this meeting visible by default?'; diff --git a/recordings.php b/recordings.php index ed8dd20f..98d1bacd 100644 --- a/recordings.php +++ b/recordings.php @@ -122,9 +122,10 @@ $recordingshowhtml = html_writer::div($recordingshowbuttonhtml); } + $recordingname = trim($recording->name) . ' (' . zoom_get_recording_type_string($recording->recordingtype). ')'; $params = ['id' => $cm->id, 'recordingid' => $recording->id]; $recordingurl = new moodle_url('/mod/zoom/loadrecording.php', $params); - $recordinglink = html_writer::link($recordingurl, $recording->name); + $recordinglink = html_writer::link($recordingurl, $recordingname); $recordinglinkhtml = html_writer::span($recordinglink, 'recording-link', ['style' => 'margin-right:1rem']); $recordinghtml .= html_writer::div($recordinglinkhtml, 'recording', ['style' => 'margin-bottom:.5rem']); } @@ -135,6 +136,44 @@ } } +/** + * Get the display name for a Zoom recording type. + * + * @package mod_zoom + * @param string $recordingtype Zoom recording type. + * @return string + */ +function zoom_get_recording_type_string($recordingtype) { + $recordingtypestringmap = [ + 'active_speaker' => 'recordingtype_active_speaker', + 'audio_interpretation' => 'recordingtype_audio_interpretation', + 'audio_only' => 'recordingtype_audio_only', + 'audio_transcript' => 'recordingtype_audio_transcript', + 'chat_file' => 'recordingtype_chat', + 'closed_caption' => 'recordingtype_closed_caption', + 'gallery_view' => 'recordingtype_gallery', + 'poll' => 'recordingtype_poll', + 'production_studio' => 'recordingtype_production_studio', + 'shared_screen' => 'recordingtype_shared', + 'shared_screen_with_gallery_view' => 'recordingtype_shared_gallery', + 'shared_screen_with_speaker_view' => 'recordingtype_shared_speaker', + 'shared_screen_with_speaker_view(CC)' => 'recordingtype_shared_speaker_cc', + 'sign_interpretation' => 'recordingtype_sign', + 'speaker_view' => 'recordingtype_speaker', + 'summary' => 'recordingtype_summary', + 'summary_next_steps' => 'recordingtype_summary_next_steps', + 'summary_smart_chapters' => 'recordingtype_summary_smart_chapters', + 'timeline' => 'recordingtype_timeline', + ]; + + // Return some default string in case new recordingtype values are added in the future. + if (empty($recordingtypestringmap[$recordingtype])) { + return $recordingtype; + } + + return get_string($recordingtypestringmap[$recordingtype], 'mod_zoom'); +} + echo html_writer::table($table); echo $OUTPUT->footer(); From 506b6490d0acb830d21616bcac7dc984f5429597 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Thu, 18 Apr 2024 15:15:10 -0400 Subject: [PATCH 2/3] savepoint: bump version date --- db/upgrade.php | 4 ++-- version.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/upgrade.php b/db/upgrade.php index f2d2f1e4..707f5bc9 100755 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -965,7 +965,7 @@ function xmldb_zoom_upgrade($oldversion) { upgrade_mod_savepoint(true, 2024030100, 'zoom'); } - if ($oldversion < 2024030101) { + if ($oldversion < 2024041900) { // Update existing recording names to default for translatable recordingtype strings. $meetings = $DB->get_records('zoom'); @@ -974,7 +974,7 @@ function xmldb_zoom_upgrade($oldversion) { } // Zoom savepoint reached. - upgrade_mod_savepoint(true, 2024030101, 'zoom'); + upgrade_mod_savepoint(true, 2024041900, 'zoom'); } return true; diff --git a/version.php b/version.php index 5fee5114..4690ee79 100755 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_zoom'; -$plugin->version = 2024030100; +$plugin->version = 2024041900; $plugin->release = 'v5.2.0'; $plugin->requires = 2019052000; $plugin->maturity = MATURITY_STABLE; From fea6409d5ba4f207b5139979543eec8f033423de Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 22 Apr 2024 11:32:33 -0400 Subject: [PATCH 3/3] recordings: compare UUIDs; use correct variable --- classes/task/get_meeting_recordings.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/classes/task/get_meeting_recordings.php b/classes/task/get_meeting_recordings.php index 6d995770..6e8beb0f 100644 --- a/classes/task/get_meeting_recordings.php +++ b/classes/task/get_meeting_recordings.php @@ -104,28 +104,26 @@ public function execute() { $zoomrecordings = $service->get_user_recordings($hostid, $from, $to); foreach ($zoomrecordings as $recordingid => $recording) { - if (empty($meetings[$recording->meetingid])) { - // Skip meetings that are not in Moodle. - mtrace('Meeting id: ' . $recording->meetingid . ' does not exist...skipping'); - continue; - } - - $zoom = $meetings[$recording->meetingid]; - if (isset($localrecordings[$recording->meetinguuid][$recordingid])) { mtrace('Recording id: ' . $recordingid . ' exists...skipping'); $localrecording = $localrecordings[$recording->meetinguuid][$recordingid]; - if ($localrecording->recordingtype !== $zoom->recordingtype) { + if ($localrecording->recordingtype !== $recording->recordingtype) { $updatemeeting = (object) [ 'id' => $localrecording->id, - 'recordingtype' => $zoom->recordingtype, + 'recordingtype' => $recording->recordingtype, ]; $DB->update_record('zoom_meeting_recordings', $updatemeeting); } continue; } + if (empty($meetings[$recording->meetingid])) { + // Skip meetings that are not in Moodle. + mtrace('Meeting id: ' . $recording->meetingid . ' does not exist...skipping'); + continue; + } + // As of 2023-09-24, 'password' is not present in the user recordings API response. if (empty($meetingpasscodes[$recording->meetinguuid])) { try { @@ -136,6 +134,7 @@ public function execute() { } } + $zoom = $meetings[$recording->meetingid]; $recordingtype = $recording->recordingtype; $record = new stdClass();