PSZoom not downloading correct files

I am using the amazingly useful PSZoom module from Joseph McEvoy https://github.com/JosephMcEvoy/PSZoom
Many many thanks to him for wrapping the Zoom API for use in powershell.
However, I think I messed something up when trying to use it.
Intent:
To download, on a regular basis, all the recorded classes for my school (I am in IT at the school) and place them into named folders, then delete them from zoom if I so choose.
Process:
The script looks for all paid accounts (I dont want student recordings, if there are any). Then looks for recordings for those accounts. It uses the UUID of the meetings on those recordings to get the download url for the meeting, which then allows the download of the recording.
It seems backwards, but the get-zoomrecordings function does not seem to have a download url return option, just a share/view which is not the same.
Then I use webclient to download everything.

Choices:
I chose to use CSV files for the information instead of processing everything in powershell directly because I wanted the ability to trace what is going on after the fact, as well as the option to comment out the existing variables and the portion that writes to them and simple define what $fromfile1 has in it.

Specific Issue:
Zoom shows that I have 68 meetings with recordings, but my script only sees 4 items to download, and then only offers to download two of them.
Worse, there are no errors shown that would in any way explain this. I get errors for those meetings that are not done processing, but that’s expected. Everything else… either gets ignored or something… I have clearly done something wrong, and your help is greatly appreciated.

#pre-reqs, PSZoom powershell module, an admin account on zoom, your oauth set and your JWT Auth set.
#update this to match your environment - you will need a JWT token, and they expire regularly, https://marketplace.zoom.us/develop/apps/[APPID]/feature
#you will need your from and to dates, your download location, 
#and to choose if you want auto delete (change no to yes AND uncomment the remove section on line 38
import-module PSZoom
$Global:ZoomApiKey    = 'REDACTED FOR SAFETY' 
$Global:ZoomApiSecret = 'REDACTED FOR SAFETY'
$base = "C:\working\Testing\"
$fromfile1 = "C:\working\zoomusers_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$fromfile2 = "c:\working\rcdngs_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$fromfile3 = "c:\working\DWNLD_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$downlog = "c:\working\ZoomDownloads_$((Get-Date).ToString('MM-dd-yyyy')).log"
$deletelog = "c:\working\ZoomDelete_$((Get-Date).ToString('MM-dd-yyyy')).log"
$exportPath = 'c:\working\files\'
$jwt='REDACTED FOR SAFETY'
$AToken='/?access_token='
$autodelete= 'NO'
$count=0
$FDATE="2021-11-11"
$TDATE="2022-02-28"

Get-ZoomUsers -AllPages -status active | Where-Object {$_.type -eq "2"} |Select-Object id,first_name,last_name,email,pmi | export-csv $fromfile1 -NoTypeInformation
write-host "obtained users"
$Users = Import-Csv -Path $FromFile1
Write-Host "getting recording list"
foreach ($user in $Users) {
Get-zoomRecordings -userId $user.email -From $FDATE -PageSize 300 -To $TDATE |Select-Object -ExpandProperty meetings| export-csv -append $fromfile2 -NoTypeInformation 
$MIDS = Import-Csv -Path $fromfile2
foreach ($MID in $MIDS) {
Get-ZoomMeetingRecordings -MeetingId $MID.uuid | Select-Object -property topic,start_time -ExpandProperty recording_files| Select-Object -property topic,start_time,meeting_id,download_url| export-csv $fromfile3 -Append -NoTypeInformation
$count=$count+1
} 
}
write-host "got recording list, $count items long"
Write-Host "converting to download links, and begining downloads"
$Links = Import-Csv -Path $fromfile3
foreach ($Link in $Links) {
$MTD = $Link.start_time.substring(0,10)
$path = ($base + $Link.topic)
If(!(test-path $path))
{
      New-Item -ItemType Directory -Force -Path $path
}
$uri= ($link.download_url + $AToken + $jwt)
$inc = 1
$file=($path + "\" + $mtd + "-" + $inc + ".mp4")
If(!(test-path $file)) {
$wc=New-Object System.Net.WebClient
$wc.DownloadFile($uri,$file)  
Write-Output "downloading $file" | out-file $downlog -append}
else {
$items=(Get-ChildItem $path | Measure-Object).Count
$inc=$inc+$items
$file=($path + "\" + $mtd + "-" + $inc + ".mp4")
$wc=New-Object System.Net.WebClient
$wc.DownloadFile($uri,$file)  
Write-Output "downloading $file" | out-file $downlog -append}

if($autodelete -eq 'YES'){
    #Remove-ZoomMeetingRecordings -MeetingId $link.meeting_id -Action delete
    Write-Output ($link.topic + " on " + $MTD + " was succesfully delted on $((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss'))") | out-file $deletelog -append
    }
}

Joshua,
Welcome to the forum. :wave:t4:

Due to my lack of experience with or even access to Zoom, I can’t reproduce your problem and therefore can’t determine if the error is really in your code. But I’d like to give at least some general tipps.

The biggest advantage of PowerShell is working with rich and powerful objects and properties instead of stupid and boring text or strings. :wink: With using CSV files as intermediate data store you limit yourself to a few properties transfered by strings. Therefore most of them no longer have their original data type.

As it might be enough for the purpose of your code it is unnecessary complex and time consuming. Because writing something to disk is always slow. For example - to get the Zoom recordings I would do it htis way:

$AllZoomUserList = 
    Get-ZoomUsers -AllPages -status active | 
        Where-Object { $_.type -eq '2' }

$Result = 
foreach ($ZoomUser in $AllZoomUserList) {
    $ZoomRecordingList = 
        Get-zoomRecordings -userId $ZoomUser.email -From $FDATE -PageSize 300 -To $TDATE
    foreach ($ZoomRecording in $ZoomRecordingList.meetings) {
        Get-ZoomMeetingRecordings -MeetingId $ZoomRecording.uuid |
            Select-Object -property topic, start_time -ExpandProperty recording_files |
                Select-Object -property topic, start_time, meeting_id, download_url
    }
}

If you insist to export the data to a file at the end you still can do that … like this:

$Result |
    Export-Csv -Path  $fromfile3 -NoTypeInformation 

And BTW: You can get the count of elements very easy this way:

$Result.count

Because I don’t know how the output of the cmdlet Get-ZoomMeetingRecordings looks like I cannot say something for the rest of your code. I’m sorry.

Following your feedback, I went ahead and ran basicaly your code:

Get-ZoomUsers -AllPages -status active | Where-Object { $_.type -eq '2' } 
$Result = foreach ($ZoomUser in $AllZoomUserList) {
$ZoomRecordingList =  Get-zoomRecordings -userId $ZoomUser.email -From $FDATE -PageSize 300 -To $TDATE
foreach ($ZoomRecording in $ZoomRecordingList.meetings) {
    Get-ZoomMeetingRecordings -MeetingId $ZoomRecording.uuid | Select-Object -property topic, start_time -ExpandProperty recording_files | Select-Object -property topic, start_time, meeting_id, download_url | Export-Csv -Path  $fromfile3 -NoTypeInformation -Append
}

}

Plus my oauth and output a file… fyi the commands naturally output as json data…
The output created a file with 57 lines, but it is for the most part the same 18 entries repeated over and over again.
So, I have greatly sped up the basic grab portion of the script thanks to your advice.
But I am still getting less than excellent results.

here is an example of what I am seeing on the zoom website… 68 recorded sessions.

and here is a subset of the output with the meeting IDs changed and the URLs partially redacted.

topic	start_time	meeting_id	download_url
Science Lab - Grade 3A 2021	2/28/2022 19:14	jflsHTJnQUac44rM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3A 2021	2/28/2022 19:14	jflsHTJnQUac44rM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3A 2021	2/28/2022 19:14	jflsHTJnQUac44rM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3A 2021	2/28/2022 19:14	jflsHTJnQUac44rM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3A 2021	2/28/2022 19:14	jflsHTJnQUac44rM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3A 2021	2/28/2022 19:14	jflsHTJnQUac44rM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3 B 2021	2/28/2022 18:30	jflsHTJnQUac44sM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3 B 2021	2/28/2022 18:30	jflsHTJnQUac44sM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3 B 2021	2/28/2022 18:30	jflsHTJnQUac44sM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3 B 2021	2/28/2022 18:30	jflsHTJnQUac44sM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3 B 2021	2/28/2022 18:30	jflsHTJnQUac44sM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Science Lab - Grade 3 B 2021	2/28/2022 18:30	jflsHTJnQUac44sM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Math 7 CP Period G	2/28/2022 18:46	jflsHTJnQUac44tM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Math 7 CP Period G	2/28/2022 18:46	jflsHTJnQUac44tM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Math 7 CP Period G	2/28/2022 18:46	jflsHTJnQUac44tM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]
Math 7 CP Period G	2/28/2022 18:46	jflsHTJnQUac44tM26HGRQ==	https://ehillel.zoom.us/rec/download/[97 random characters]

JSON data usually are hierarchical data. It is mostly impossible to boil them down to structrured data to be able to export them to CSV file without loosing some parts of the information. It might be easier to parse the JSON data structure instead of trying to flatten them.
I’d try to get one JSON data set and figure out what’s in it and what’s useful.

Another thought I just had … you are enumerating the users and their according recordings. Shouldn’t get users attended the same Zoom meeting get the same recording anyway? So you might unravel it from the wrong end.

At least that’s something. :+1:t4: :smiley: :kissing_closed_eyes:

I’d love to unravel it from the other end, BUT… the commands do not seem to give me the recording download link going the other way. Only the view link. I know that should be enough, but its not.

Maybe what I need is someone to help me figure out the internal logic of the PSzoom functions to get what i need out of it… Really not sure.
Is there any specific data you can think of that i can post that will help figure out why I am getting these discrepancies and repetitions in my acquired data?

Hmmm … what a pity.

Unfortunately not, sorry.

I could imagine that the data are actually correct - but just not what you expect. :wink:

For example: when you list some users who attended to the same Zoom meeting it should be expected for them to get the same recording to download, right?

I’d start with one user and figure out how to query the needed information and how to parse these infromation to get what’s needed.

No, it lists the person who owns the meeting, not the attendees.

I am doing one person testing, as suggested.

@Olaf
THANK YOU.
I followed all your advice, and low and behold, I got my answers and I can say i am better for the effort.
If anyone else ends up on this issue with PSZoom - go read the API documentation on the Zoom developer website.

I used the response sample in the pane on the right to see what i SHOULD be getting back, and realized that in fact the info I needed was in the recordings function and I did not need the meeting_recordings function.

In the end, the important part of my code looks like this:

$AllZoomUserList = Get-ZoomUsers -AllPages -status active | Where-Object { $_.type -eq '2' } 
$Result = foreach ($ZoomUser in $AllZoomUserList) {
$ZoomRecordingList =  Get-zoomRecordings -userId $ZoomUser.email -From $FDATE -PageSize 300 -To $TDATE | Select-Object -ExpandProperty meetings | select-object topic -ExpandProperty recording_files | Where-Object {$_.status -eq "completed"} | where-object {$_.file_type -eq "MP4"} | select-object -property topic, recording_start, meeting_id, download_url, file_type, status 

I am certain my code can be even better, but for now it works.
Thank you again for your help.
Its much appreciated.

1 Like

Hey hey … cool. I’m glad you found it. :+1:t4: :partying_face: Now you are the expert. :wink:

1 Like