Forum Discussion
Powershell Script to list ALL videos in your 365 Stream environment
I hope this is useful to everyone.
My goal was to get a list of all videos in my stream so that I could contact each video creator about the changes that are coming to stream. Also so I could figure out how much work there was to do in moving, and how much video is created and not used.
My original solution (posted here 7 Oct 2020) I've retired, as thanks to Twan van Beers code here https://neroblanco.co.uk/2022/02/get-list-of-videos-from-microsoft-stream/ I've been able to build a single ps1 script that does all I need. I'll leave the the original code at the bottom of this post.
Take a look a the comments I've put into the code for how to use this script.
I found that my original script gave me information about the videos but it was hard to use AND didn't tell me which videos were in which channels. This new version of Twan van Beers script gives me both.
Save the new code Oct 2022 as a PowerShell script e.g. get-stream-video-info.ps1 Then open a powershell screen, navigate to the folder get-stream-video-info.ps1 is in.
To run it enter .\get-stream-video-info.ps1 "C:\Temp\streamanalysis\stream7Oct22-getnbstreamvideoinfo.csv"
Follow the on screen prompts.
>>> New Code Oct 2022 <<<
using namespace System.Management.Automation.Host
[CmdletBinding()]
param (
[parameter(Position=0,Mandatory=$False)]
[string]$OutputCsvFileName,
[parameter(Position=1,Mandatory=$False)]
[switch]$OpenFileWhenComplete = $False
)
# ----------------------------------------
# How to use
<#--
Open a powershell window then type in the following and click enter
.\get-stream-video-info.ps1 "C:\Temp\streamanalysis\stream7Oct22-getnbstreamvideoinfo.csv"
You'll then be prompted for 3 options
[V] Videos [C] ChannelVideos [A] All [?] Help (default is "V"):
V - videos, which will get a list of all the videos in your STREAM environment.
NOTE you may need to alter the variables in the code if you have more than 1000s of videos
C - ChannelVideos, will get a list of all the videos and the channels they are in.
NOTE this returns a filtered view of all the videos associated with a channel
A - All, returns both the Videos and the ChannelVideos.
You'll then be prompted for a user to login to the STREAM portal with, this is so the script can get a security token to do it's work with.
Choose/use an account with full access to STREAM.
If you used a CSV file path after the script name, then this powershell script will export one or two CSV files based on the option chosen
<your folder path, your filename>-videos<your file ending>
and or
<your folder path, your filename>-channelVideos<your file ending>
If you don't want to export file names, this powershell creates objects you can use in other ways
V or A - will create an object $ExtractData, which is a list of every video and key properties for each video.
C or A - wil create an object $videosPerChannel, which lists key information about each video AND the channel they are part of.
----------------------------------------------------------------------------------------------
original source
my script
https://techcommunity.microsoft.com/t5/microsoft-stream-classic/powershell-script-to-list-all-videos-in-your-365-stream/m-p/1752149
which inspired Twan van Beers to write
https://neroblanco.co.uk/2022/02/get-list-of-videos-from-microsoft-stream/
I've then taken Twan's script and modified it to do what I require in my environment.
Namely - get the video information AND the channels they are part of. For my 1000 or so videos and 35 channels, it takes about 1 min to run using the All option.
This meant I was able to setup an intranet video library with a channel metadata column, folders per channel
(so i could give edit rights to channel owners, without opening up the entire library or having to use multiple libraries), and eventually
download the videos, then upload them into the library using ShareGate to reinstate some of the key metadata, i.e. created date, person who created them etc
--#>
# ----------------------------------------------------------------------------------------------
function Show-OAuthWindowStream {
param (
[string]$Url,
[string]$WindowTitle
)
$Source = `
@"
[DllImport("wininet.dll", SetLastError = true)]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int lpdwBufferLength);
"@
$WebBrowser = Add-Type -memberDefinition $Source -passthru -name $('WebBrowser'+[guid]::newGuid().ToString('n'))
$INTERNET_OPTION_END_BROWSER_SESSION = 42
# Clear the current session
$WebBrowser::InternetSetOption([IntPtr]::Zero, $INTERNET_OPTION_END_BROWSER_SESSION, [IntPtr]::Zero, 0) | out-null
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width = 600; Height = 800 }
$Script:web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width = 580; Height = 780; Url = ($URL -f ($Scope -join "%20")) }
$Web.ScriptErrorsSuppressed = $True
$Form.Controls.Add($Web)
$Featured = {
$Head = $Web.Document.GetElementsByTagName("head")[0];
$ScriptEl = $Web.Document.CreateElement("script");
$Element = $ScriptEl.DomElement;
# Javascript function to get the sessionInfo including the Token
$Element.text = `
@'
function CaptureToken() {
if( typeof sessionInfo === undefined ) {
return '';
} else {
outputString = '{';
outputString += '"AccessToken":"' + sessionInfo.AccessToken + '",';
outputString += '"TenantId":"' + sessionInfo.UserClaim.TenantId + '",';
outputString += '"ApiGatewayUri":"' + sessionInfo.ApiGatewayUri + '",';
outputString += '"ApiGatewayVersion":"' + sessionInfo.ApiGatewayVersion + '"';
outputString += '}';
return outputString;
}
}
'@;
$Head.AppendChild($ScriptEl);
$TenantInfoString = $Web.Document.InvokeScript("CaptureToken");
if( [string]::IsNullOrEmpty( $TenantInfoString ) -eq $False ) {
$TenantInfo = ConvertFrom-Json $TenantInfoString
if ($TenantInfo.AccessToken.length -ne 0 ) {
$Script:tenantInfo = $TenantInfo;
$Form.Controls[0].Dispose()
$Form.Close()
$Form.Dispose()
}
}
}
$Web.add_DocumentCompleted($Featured)
$Form.AutoScaleMode = 'Dpi'
$Form.ShowIcon = $False
$Form.Text = $WindowTitle
$Form.AutoSizeMode = 'GrowAndShrink'
$Form.StartPosition = 'CenterScreen'
$Form.Add_Shown( { $Form.Activate() })
$Form.ShowDialog() | Out-Null
write-output $Script:tenantInfo
}
# ----------------------------------------------------------------------------------------------
function Get-RequestedAssets([PSCustomObject]$Token, [string]$Url, [string]$Label) {
$Index = 0
$MainUrl = $Url
$AllItems = @()
do {
$RestUrl = $MainUrl.Replace("`$skip=0", "`$skip=$Index")
Write-Host " Fetching ... $($Index) to $($Index+100)"
$Items = @((Invoke-RestMethod -Uri $RestUrl -Headers $Token.headers -Method Get).value)
$AllItems += $Items
$Index += 100
} until ($Items.Count -lt 100)
Write-Host " Fetched $($AllItems.count) items"
$Assets = $AllItems | Select-Object `
@{Name='Type';Expression={$Label}},`
Id, Name,`
@{Name='Size(MB)';Expression={$_.AssetSize/1MB}}, `
PrivacyMode, State, VideoMigrationStatus, Published, PublishedDate, ContentType, Created, Modified, `
@{name='Media.Duration';Expression={$_.Media.Duration}},`
@{name='Media.Height';Expression={$_.Media.Height}},`
@{name='Media.Width';Expression={$_.Media.Width}},`
@{name='Media.isAudioOnly';Expression={$_.media.isAudioOnly}},`
@{name='Metrics.Comments';Expression={$_.Metrics.Comments}},`
@{name='Metrics.Likes';Expression={$_.Metrics.Likes}},`
@{name='Metrics.Views';Expression={$_.Metrics.Views}}, `
@{name='ViewVideoUrl';Expression={("https://web.microsoftstream.com/video/" + $_.Id)}}, `
@{name='VideoCreatorName';Expression={$_.creator.name}}, `
@{name='VideoCreatorEmail';Expression={$_.creator.mail}}, `
@{name='VideoDescription';Expression={$_.description}}
write-output $Assets
}
function Get-VideoChannels([PSCustomObject]$Token, [string]$Url, [string]$Label) {
#this will get the list of channels
$Index = 0
$MainUrl = $Url
$AllItems = @()
do {
$RestUrl = $MainUrl.Replace("`$skip=0", "`$skip=$Index")
Write-Host " Fetching ... $($Index) to $($Index+100)"
$Items = @((Invoke-RestMethod -Uri $RestUrl -Headers $Token.headers -Method Get).value)
$AllItems += $Items
$Index += 100
} until ($Items.Count -lt 100)
Write-Host " Fetched $($AllItems.count) items"
#to add properties to this section look at https://aase-1.api.microsoftstream.com/api/channels?$skip=0&$top=100&adminmode=true&api-version=1.4-private
$Channels = $AllItems | Select-Object `
@{Name='Type';Expression={$Label}},`
Id, Name, Description,`
@{Name='MetricsVideos';Expression={$_.metrics.videos}}
#write-host $channels.count
write-output $Channels
}
function Get-channelVideos([PSCustomObject]$Token, [PSCustomObject]$allChannels, [string]$Label) {
#this will get the list of channels
$MainUrl = "https://aase-1.api.microsoftstream.com/api/channels/ChannelIDToSwap/videos?`$top=50&`$skip=0&`$filter=published%20and%20(state%20eq%20%27completed%27%20or%20contentSource%20eq%20%27livestream%27)&`$expand=creator,events&adminmode=true&`$orderby=name%20asc&api-version=1.4-private"
#for each channel URL go through all the videos, capture the channel name against the video id and name
$allVideosPerChannel = @()
foreach($chan in $allChannels) {
$thisChannelid = $chan.id
$chanUrl = @( $MainUrl.Replace("ChannelIDToSwap", $thisChannelid) )
$chanName = $chan.name
$AllItems = ""
$items = ""
$thischanvideos = ""
$Index = 0
#write-host $chanUrl
#loop the index
do {
$RestUrl = $chanUrl.Replace("`$skip=0", "`$skip=$Index")
#write-host $restUrl
#Write-Host "$chanName | Fetching ... $($Index) to $($Index+50)"
$Items = @((Invoke-RestMethod -Uri $RestUrl -Headers $Token.headers -Method Get).value)
$allItems = $items | select id,name, @{Name='Channel';Expression={$chanName}},@{Name='Type';Expression={$Label}}
#write-host $allItems.count
#foreach($x in $items ) {
# write-host $x.name
#write-host $x.id
#write-host $label
#write-host $chanName
#}
$Index += 50
} until ($Items.Count -lt 100)
#got videos into $items, now mist with $chan info and put into $allVideosPerChannel object
$allVideosPerChannel += $AllItems
$AllItems = ""
$items = ""
}
Write-Host " Fetched $($allVideosPerChannel.count) videos in $($allChannels.count) channels"
#to add properties to this section look at https://aase-1.api.microsoftstream.com/api/channels?$skip=0&$top=100&adminmode=true&api-version=1.4-private
write-output $allVideosPerChannel
}
# ----------------------------------------------------------------------------------------------
function Get-StreamToken() {
$TenantInfo = Show-OAuthWindowStream -url "https://web.microsoftstream.com/?noSignUpCheck=1" -WindowTitle "Please login to Microsoft Stream ..."
$Token = $TenantInfo.AccessToken
$Headers = @{
"Authorization" = ("Bearer " + $Token)
"accept-encoding" = "gzip, deflate, br"
}
$UrlTenant = $TenantInfo.ApiGatewayUri
$ApiVersion = $TenantInfo.ApiGatewayVersion
$UrlBase = "$UrlTenant{0}?`$skip=0&`$top=100&adminmode=true&api-version=$ApiVersion"
$RequestToken = [PSCustomObject]::new()
$RequestToken | Add-Member -Name "token" -MemberType NoteProperty -Value $Token
$RequestToken | Add-Member -Name "headers" -MemberType NoteProperty -Value $Headers
$RequestToken | Add-Member -Name "tenantInfo" -MemberType NoteProperty -Value $TenantInfo
$Urls = [PSCustomObject]::new()
$RequestToken | Add-Member -Name "urls" -MemberType NoteProperty -Value $Urls
$RequestToken.urls | Add-Member -Name "Videos" -MemberType NoteProperty -Value ($UrlBase -f "videos")
$RequestToken.urls | Add-Member -Name "Channels" -MemberType NoteProperty -Value ($UrlBase -f "channels")
$RequestToken.urls | Add-Member -Name "Groups" -MemberType NoteProperty -Value ($UrlBase -f "groups")
$UrlBase = $UrlBase.replace("`$skip=0&", "")
$RequestToken.urls | Add-Member -Name "Principals" -MemberType NoteProperty -Value ($UrlBase -f "principals")
write-output $RequestToken
}
function New-Menu {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Title,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Question
)
$videos = [ChoiceDescription]::new('&Videos', 'Videos')
$channelvideos = [ChoiceDescription]::new('&ChannelVideos', 'All Videos by Channel')
$all = [ChoiceDescription]::new('&All', 'All videos AND all videos by channel')
$options = [ChoiceDescription[]]($videos, $channelvideos, $all)
$result = $host.ui.PromptForChoice($Title, $Question, $options, 0)
switch ($result) {
0 { 'Videos' }
1 { 'ChannelVideos' }
2 { 'All' }
}
}
$menuoutome = New-Menu -title 'Stream videos' -question 'What do you want to output?'
#write-host $menuoutome
$StreamToken = Get-StreamToken
$urlQueryToUse = $StreamToken.Urls.Videos
#default $StreamToken.Urls.Videos is something like https://aase-1.api.microsoftstream.com/api/videos?$skip=900&$top=100&adminmode=true&api-version=1.4-private
#To get creator and event details you need to add $expand=creator,events to the URL , not to do that you need to use &`$expand=creator,events with out the ` powershell thinks $expand is a variable.
# e.g. use
$urlQueryToUse = $StreamToken.Urls.Videos+"&`$expand=creator,events"
#Other option
# use the following if you want to only see files that have privacymode eq 'organization' i.e. video is visible to EVERYONE in the organisation
# Thanks to Ryechz for this
#
# $urlQueryToUse = $StreamToken.Urls.Videos + "&orderby=publishedDate%20desc&`$expand=creator,events&`$filter=published%20and%20(state%20eq%20%27Completed%27%20or%20contentSource%20eq%20%27livestream%27)%20and%20privacymode%20eq%20%27organization%27%20"
if($menuoutome -eq 'Videos' -Or $menuoutome -eq 'All'){
#modify the -URL submitted to get more data or filter data or order the output
$ExtractData = Get-RequestedAssets -token $StreamToken -Url $urlQueryToUse -Label "Videos"
write-host ""
write-host "The `$ExtractData object contains all the details about each video. Use `$ExtractData[0] to see the first item in the object, and it's properties."
if( $OutputCsvFileName ) {
$thisOutputCsvFileName = $OutputCsvFileName.replace(".csv", '-'+$menuoutome+'-videos.csv')
$ExtractData | Export-CSV $thisOutputCsvFileName -NoTypeInformation -Encoding UTF8
write-host "The following file has been created: $thisOutputCsvFileName"
if( $OpenFileWhenComplete ) {
Invoke-Item $thisOutputCsvFileName
}
}
}
if($menuoutome -eq 'ChannelVideos' -Or $menuoutome -eq 'All'){
#Get the list of channels , filter the result for the channel id and name
$channelList = Get-VideoChannels -token $StreamToken -Url $StreamToken.Urls.Channels -Label "Channels"
#for each channel get the videos that are in that channel, so that we can match them up to the ExtractData , which is the list of all videos)
$videosPerChannel = get-channelvideos -token $StreamToken -allChannels $channelList -Label "ChannelVideos"
write-host ""
write-host "The `$videosPerChannel object contains key information about each video and the channel it is in. Use `$videosPerChannel[0] to see the first video, and it's properties."
write-host "The `$channelList object contains a list of channel's and their properties."
if( $OutputCsvFileName ) {
$thisOutputCsvFileName = $OutputCsvFileName.replace(".csv", '-'+$menuoutome+'-channelVideos.csv')
$videosPerChannel | Export-CSV $thisOutputCsvFileName -NoTypeInformation -Encoding UTF8
write-host "The following file has been created: $thisOutputCsvFileName"
if( $OpenFileWhenComplete ) {
Invoke-Item $thisOutputCsvFileName
}
}
}
>>> Original Code Oct 2020 <<<
I've left this here in case it is useful to anyone. See the script above for a better solution.
That said this solution relies on you knowing a bit about PowerShell, being a Stream Admin and being happy to manually save some files (I couldn't figure out how to do pass through windows authentication for the script) so you have to manually save each paged JSON file of 100 videos.
It took me about 20minutes to export information about 591 videos (about 3 hours to make the script).
To use the script
- update each variable marked with #<<<< Update this value
- in a normal (not admin) powershell window run the script (i copy and paste logical parts into the powershell window)
- you will be given several urls, to visit and save the JSON files from
- once you have the JSON files the final part of the script reads those, and exports a CSV file with a row per video in STREAM
NOTE : as an admin you see all videos, so before you share the CSV with others be aware that there may be sensitive information in it that most users can't see due to STREAM's in built security.
I don't have much time, hence I made this script so please don't expect quick answers to any questions. This script is rough, use it if it helps, but ... be professional and check it before you use it.
##>> Update 5 Aug 2021 <<##
Thanks to everyone who has commented, I've updated the code below with your suggestions
You still have to manually save the JSON browser tabs that show up as JSON files into a folder, but other than that I hope it is now easier for you to use 🙂
#reference https://techcommunity.microsoft.com/t5/microsoft-stream-forum/powershell-script-to-audit-and-export-channel-content-details-of/m-p/354832
# goal of this script
#- get list of all videos in stream for analysis
#- it takes about 20 minutes to do this for 500 stream videos.
#First
# find out what your api source is
# go to the following URL in chrome as an admin of Stream https://web.microsoftstream.com/browse
# using Developer tools look at the "console" search for .api.microsoftstream to find out what is between https:// and .api.microsoftstream in my case https://aase-1.api.microsoftstream.com/api/
[string]$rootAPIlocation = "aase-1" #<<<< Update this value to the one you find in the console view
#[string]$rootAPIlocation = "uswe-1" # use this for Western US
#[string]$rootAPIlocation = "euno-1" # use this for the Europe North region
#enter where you on your computer you want the files to go
[string]$PowerShellScriptFolder = "C:\Temp\streamanalysis" #<<<< Update this value
#json files will be saved into "VideosJSON" folder
[string]$streamJSONfolder = Join-Path -Path $PowerShellScriptFolder -ChildPath "VideosJSON" #<<<< Update this value if you want a different folder name
#>>> REMOVES all exiisting JSON files <<<<
#remove all JSON items in this folder
Remove-Item -path $streamJSONfolder\* -include *.json -Force -Recurse
#guess approx number of videos you think you have divide by 100 e.g. 9 = 900 videos
[int]$Loopnumber = 9 #<<<< Update this value
#put in your stream portal url
[string]$StreamPortal = "https://web.microsoftstream.com/?NoSignUpCheck=1"
#put in the url where you see all videos from in stream
[string]$StreamPortalVideoRoot = "https://web.microsoftstream.com/browse/" #$StreamPortalChannelRootForFindingVideos
[string]$StreamPortalVideoViewRoot= "https://web.microsoftstream.com/video/" # for watching a video
#this builds from the info you've put in a URL which will give back the JSON info about all your videos.
[string]$StreamAPIVideos100 = "https://$rootAPIlocation.api.microsoftstream.com/api/videos?NoSignUpCheck=1&`$top=100&`$orderby=publishedDate%20desc&`$expand=creator,events&`$filter=published%20and%20(state%20eq%20%27Completed%27%20or%20contentSource%20eq%20%27livestream%27)&adminmode=true&api-version=1.4-private&`$skip=0" #$StreamAPIVideos100
# use the following if you want to only see files that have privacymode eq 'organization' i.e. video is visible to EVERYONE in the organisation
#Thanks to Ryechz for this
#
# [string]$StreamAPIVideos100 = "https://$rootAPIlocation.api.microsoftstream.com/api/videos?NoSignUpCheck=1&`$top=100&`$orderby=publishedDate%20desc&`$expand=creator,events&`$filter=published%20and%20(state%20eq%20%27Completed%27%20or%20contentSource%20eq%20%27livestream%27)%20and%20privacymode%20eq%20%27organization%27%20&adminmode=true&api-version=1.4-private&`$skip=0"
[int]$skipCounter
[int]$skipCounterNext = $skipCounter+100
[string]$fileName = "jsonfor-$skipCounter-to-$skipCounterNext.json"
#next section creates the URLS you need to manually download the json from , it was too hard to figure out how to do this programatically with authentication.
Write-Host " Starting Chrome Enter your credentials to load O365 Stream portal" -ForegroundColor Magenta
#Thanks Conrad Murray for this tip
Start-Process -FilePath 'chrome.exe' -ArgumentList $StreamPortal
Read-Host -Prompt "Press Enter to continue ...."
Write-host " -----------------------------------------" -ForegroundColor Green
Write-host " --Copy and past each url into chrome-----" -ForegroundColor Green
Write-host " --save JSON output into $streamJSONfolder" -ForegroundColor Green
for($i=0;$i -lt $Loopnumber; $i++) {
$skipCounter = $i*100
if($skipCounter -eq 0) {
write-host $StreamAPIVideos100
Start-Process -FilePath 'chrome.exe' -ArgumentList $StreamAPIVideos100
} else {
write-host $StreamAPIVideos100.replace("skip=0","skip=$skipCounter")
#following code opens browser tabs for each of the jsonfiles
#Thanks Conrad Murray for this tip
Start-Process -FilePath 'chrome.exe' -ArgumentList $StreamAPIVideos100.replace("skip=0","skip=$skipCounter")
}
}
Write-host " --save each browser window showing JSON output into $streamJSONfolder" -ForegroundColor Green
Write-host " -----------------------------------------------------------------------------------" -ForegroundColor Green
Write-host " -----------------------------------------" -ForegroundColor Green
Read-Host -Prompt "Press Enter to continue ...."
Write-host " -----------------------------------------" -ForegroundColor Green
$JSONFiles = Get-ChildItem -Path $streamJSONfolder -Recurse -Include *.json
[int]$videoscounter = 0
$VideosjsonAggregateddata=@()
$data=@()
foreach($fileItem in $JSONFiles)
{
Write-host " -----------------------------------------" -ForegroundColor Green
Write-Host " =====>>>> getting content of JSON File:", $fileItem, "- Path:", $fileItem.FullName -ForegroundColor Yellow
$Videosjsondata = Get-Content -Raw -Path $fileItem.FullName | ConvertFrom-Json
$VideosjsonAggregateddata += $Videosjsondata
Write-host " -----------------------------------------" -ForegroundColor Green
#Write-Host " =====>>>> Channel JSON Raw data:", $Videosjsondata -ForegroundColor green
#Read-Host -Prompt "Press Enter to continue ...."
}
write-host "You have " $VideosjsonAggregateddata.value.count " videos in Stream , using these selection criteria"
foreach($myVideo in $VideosjsonAggregateddata.value)
{
$videoscounter += 1
$datum = New-Object -TypeName PSObject
Write-host " -----------------------------------------" -ForegroundColor Green
Write-Host " =====>>>> Video (N°", $videoscounter ,") ID:", $myVideo.id -ForegroundColor green
Write-Host " =====>>>> Video Name:", $myVideo.name," created:", $myVideo.created,"- modified:", $myVideo.modified -ForegroundColor green
Write-Host " =====>>>> Video Metrics views:", $myVideo.metrics.views, "- comments:", $myVideo.metrics.comments -ForegroundColor Magenta
Write-Host " =====>>>> Video Creator Name: ", $myVideo.creator.name , " - Email:", $myVideo.creator.mail -ForegroundColor Magenta
Write-Host " =====>>>> Video Description: ", $myVideo.description -ForegroundColor Magenta
$datum | Add-Member -MemberType NoteProperty -Name VideoID -Value $myVideo.id
$datum | Add-Member -MemberType NoteProperty -Name VideoName -Value $myVideo.name
$datum | Add-Member -MemberType NoteProperty -Name VideoURL -Value $($StreamPortalVideoViewRoot + $myVideo.id)
$datum | Add-Member -MemberType NoteProperty -Name VideoCreatorName -Value $myVideo.creator.name
$datum | Add-Member -MemberType NoteProperty -Name VideoCreatorEmail -Value $myVideo.creator.mail
$datum | Add-Member -MemberType NoteProperty -Name VideoCreationDate -Value $myVideo.created
$datum | Add-Member -MemberType NoteProperty -Name VideoModificationDate -Value $myVideo.modified
$datum | Add-Member -MemberType NoteProperty -Name VideoLikes -Value $myVideo.metrics.likes
$datum | Add-Member -MemberType NoteProperty -Name VideoViews -Value $myVideo.metrics.views
$datum | Add-Member -MemberType NoteProperty -Name VideoComments -Value $myVideo.metrics.comments
#the userData value is for the user running the JSON query i.e. did that user view this video. It isn't for information about all users who may have seen this video. There seems to be no information about that other than, total views = metrics.views
#$datum | Add-Member -MemberType NoteProperty -Name VideoComments -Value $myVideo.userData.isViewed
$datum | Add-Member -MemberType NoteProperty -Name Videodescription -Value $myVideo.description
#thanks Johnathan Ogden for these values
$datum | Add-Member -MemberType NoteProperty -Name VideoDuration -Value $myVideo.media.duration
$datum | Add-Member -MemberType NoteProperty -Name VideoHeight -Value $myVideo.media.height
$datum | Add-Member -MemberType NoteProperty -Name VideoWidth -Value $myVideo.media.width
$datum | Add-Member -MemberType NoteProperty -Name VideoIsAudioOnly -Value $myVideo.media.isAudioOnly
$datum | Add-Member -MemberType NoteProperty -Name VideoContentType -Value $myVideo.contentType
$data += $datum
}
$datestring = (get-date).ToString("yyyyMMdd-hhmm")
$csvfileName = ($PowerShellScriptFolder + "\O365StreamVideoDetails_" + $datestring + ".csv") #<<<< Update this value if you want a different file name
Write-host " -----------------------------------------" -ForegroundColor Green
Write-Host (" >>> writing to file {0}" -f $csvfileName) -ForegroundColor Green
$data | Export-csv $csvfileName -NoTypeInformation
Write-host " ------------------ DONE -----------------------" -ForegroundColor Green
Disclaimer : You can use that solution as you want and modify it depending of your case.
Many thanks to
Fromelard and his
https://techcommunity.microsoft.com/t5/microsoft-stream-forum/powershell-script-to-audit-and-export-channel-content-details-of/m-p/354832 which gave me enough to figure out how to do this.
Anyone looking at this script should also look at the PowerShell script that was released by Microsoft as part of the Stream (Classic) migration tools. The PowerShell script released by Microsoft can be downloaded directly from Stream (Classic) admin page and also has a PowerBI Desktop template that can be used to analyze the output CSVs.
See this part of the migration help articles for more info:
https://learn.microsoft.com/stream/streamnew/migration-details#stream-classic-video-report
- mfarleyCopper Contributor
Dorje-McKinnon Wow! This is a life saver! We are using it to clean up our Stream library. I can't believe more people haven't replied or +1 Liked. I created an account specifically to reply here and say Thank You!
- RyechzBrass Contributor
US West Region is: https://uswe-1.api.microsoftstream.com/api/
Wish I could get info on whether or not the video is shared and who it's shared with. Including the Entire Company or not. Thanks for your good work! Also, there was no Make Directory commands setup for the folder structure. I had to manually create these: stream-analysis\VideosJSON.
I just processed 27 pages worth.
- RyechzBrass Contributor
Here are some additional updates. Back in March 2020, when MS deployed the admin mode and admin page our default sharing changed so that every new recording had "organization" permissions on it. This means anyone in the domain could watch any of our recorded meetings. This was not good, hence why I am here. I found out how to filter the output of this script to only include those made available to the whole org.
In the StreamAPIVideos100 string, add the following right before &adminmode
%20and%20privacymode%20eq%20%27organization%27%20
Additionally, in the $dataum string building area add the following line:
$datum | Add-Member -MemberType NoteProperty -Name PrivacyMode -Value $myVideo.privacymode
This new line will add a column in the .csv file output, so if you want to not do the first edit, but simply list the privacy mode for each video, only add the datum code above. I had 2600+ videos, so for me, filtering by "organization" works much better as it get's rid of most of the extra unnecessary work.
Here is the entire string for the org filter in the first edit.
[string]$StreamAPIVideos100 = "https://$rootAPIlocation.api.microsoftstream.com/api/videos?NoSignUpCheck=1&`$top=100&`$orderby=publishedDate%20desc&`$expand=creator,events&`$filter=published%20and%20(state%20eq%20%27Completed%27%20or%20contentSource%20eq%20%27livestream%27)%20and%20privacymode%20eq%20%27organization%27%20&adminmode=true&api-version=1.4-private&`$skip=0" #$streamapichannels100
- Jonathan_OgdenCopper Contributor
First of all HUGE THANK YOU for this script Dorje-McKinnon
Running your code that generates the URLs, I found we have 10,400+ videos in Stream. I knew we were over 1.3 TB of space, but I couldn't find a way in the MS Stream Admin pages to understand the size and scope of the problem. We are a global company and with everyone in lock-down for a year, this got out of hand in a hurry.
I also wanted to share some of the additional '$datum' I've added and found useful
$datum | Add-Member -MemberType NoteProperty -Name VideoDuration -Value $myVideo.media.duration
$datum | Add-Member -MemberType NoteProperty -Name VideoHeight -Value $myVideo.media.height
$datum | Add-Member -MemberType NoteProperty -Name VideoWidth -Value $myVideo.media.width
$datum | Add-Member -MemberType NoteProperty -Name VideoIsAudioOnly -Value $myVideo.media.isAudioOnly$datum | Add-Member -MemberType NoteProperty -Name VideoContentType -Value $myVideo.contentType
these return values like the following
VideoDuration VideoHeight VideoWidth VideoIsAudioOnly
PT28M10.25S 360 640 FALSE PT1H55M13.6S 360 640 FALSE PT56M19.6S 360 640 FALSE PT28M14.144S 1080 1920 FALSE PT1H10M34.56S 1080 1920 FALSE Video Duration is going to be a bit tricky to parse out in Excel. And the VideoIsAudioOnly is returning FALSE on all videos so far, so I will leave that one out.
But again GREAT code here Dorje
Thank you,
Jonathan Ogden
- Dorje-McKinnonSteel Contributor
I'm glad this has helped you and your users out.
Thanks so much for the extra properties of the videos, I've just this week been asked if I can get out the length of the videos, so great timing!
Dorje
- abraraliCopper Contributor
Dorje-McKinnonThanks for your efforts to build the script and make our lives easier. However, when I rum the URL (After changing the values) I'm getting a blank excel file.
Can someone help me out here?
- rrivero11Microsoft
Hi Jonathan_Ogden , did you find a way to get the size of the videos? I only get Width and Height but that is not helpful at all for the size.
Thanks a lot!
- Dorje-McKinnonSteel Contributor
- SuperLucasBrosCopper Contributor
Many thanks - this was very helpful. I have also tweaked mine to include privacy information.
- SeanCaddenCopper ContributorGreat script, exactly what I needed. I wanted to get all the public video's that everyone in the org could see, so I changed below, and logged in as a standard user.
-----------------------
[string]$StreamAPIVideos100 = "https://$rootAPIlocation.api.microsoftstream.com/api/videos?NoSignUpCheck=1&`$top=100&`$orderby=publishedDate%20desc&`$expand=creator,events&api-version=1.4-private&`$skip=0" #$streamapichannels100
------------------------------------ - ConradMurrayBrass ContributorHi. This is "almost" working perfectly. Once I got the Data Centre and realised the URLs in PowerShell were generated to use to paste into a browser, then copy and paste to a text file with a .son extension inside a directory I had to create called VideosJSON I was away.
However... When I run the code from this blog (https://euno-1.api.microsoftstream.com/api/videos?NoSignUpCheck=1&$top=100 ... etc) , I get one page of JSON, but when I use a similar solution link here: https://techcommunity.microsoft.com/t5/microsoft-stream-forum/powershell-script-to-audit-and-export-channel-content-details-of/m-p/354832 that calls Channels (https://euno-1.api.microsoftstream.com/api/channels?$top=100... etc ) I get two pages of JSON and more videos listed. But that code doesn't give me the duration and video.
I realised that Ryechz down below said to add this: %20and%20privacymode%20eq%20%27organization%27%20 before &adminmode
Once I removed that, I then got all the videos.
Still no size 😞 But I think I might be able to fudge that based on duration and height/width to at least get a rough idea. 720p HD is about 5 MB per minute
One trap for noobies is that in the scripit it first goes and purges and json files in the target directory. So if you have run this once to generate them, and realised you didn't have enough URLs, and run it a second time... so a bit of defensive code to compress-archive and move might be better 🙂
Oh also, I guess you could probably add Start-Process -FilePath 'chrome.exe' -ArgumentList 'https://euno-1.api.microsoftstream.com/api/videos?NoSignUpCheck=1...... etc' to where you generate the URLs to save some time.
Cool piece of work though!- Dorje-McKinnonSteel Contributor
Thanks ConradMurray
for the updates and comments, I'm sure they'll help everyone out.
When I have to come back to this code for work - next few weeks I'll incorporate what you've provided and update the code.
Dorje
- DazzaRSteel Contributor
Don't suppose you could record a video of setting it up and running it? I tried to get it working but failed somewhere and wasn't sure from the instructions what to expect so I parked it for a later date.
edit: got it working thanks
- JavierBachCopper Contributor
ConradMurray Hi there is a way to get who uploaded the video ?
Thanks
- Marc MrozMicrosoft
Anyone looking at this script should also look at the PowerShell script that was released by Microsoft as part of the Stream (Classic) migration tools. The PowerShell script released by Microsoft can be downloaded directly from Stream (Classic) admin page and also has a PowerBI Desktop template that can be used to analyze the output CSVs.
See this part of the migration help articles for more info:
https://learn.microsoft.com/stream/streamnew/migration-details#stream-classic-video-report
- Dorje-McKinnonSteel ContributorJust in case you're following this thread : I've updated it to include code suggestions from the folks who've commented on it. Many thanks.
- swhitestrathBrass ContributorHas anyone found an easier way to download the JSON files? We have over 30,000 videos so trying to find anyway to not have to download 300 tabs manually.
- Dorje-McKinnonSteel Contributor
Hi swhitestrath , I did try with the most recent version of my code, but I found keeping the security context of the browser window inside the code was the issue.
I'm sure someone who is better at that could send an HTTP request asking for the JSON files, then save them out as individual files, or combine them into one file. The issue I had was getting the code to run and ask for the files as the user account that has admin rights to the STREAM back end.
All I've been able to find on API access is the following
Which leads here (lots of the other links don't work)
https://docs.microsoft.com/en-us/stream/streamnew/new-stream
which mentions the Graph API
But this is all talking about New Stream (after the files are moved to SharePoint) AND my situation (and yours too I think swhitestrath is old Stream).
Sorry I don't have a better option for you.- swhitestrathBrass ContributorThanks for the reply I think I was just trying my luck. I remember looking for the API awhile ago but I think it was very limited. There is probably a better way but don't really want to commit too much time to it, especially with the new stream coming along. I did try a few things myself using some chrome extensions but ran into authentication errors. Will just need to go with the manual option.
- JanLandazuriCopper Contributor
Thank you for sharing the code, but to America what is the variable? I found the following