Скенер на фолдери
Прочитај ги патеките и должините на видео фајловите директно од компјутерот.
ЧЕКОР 1
Преземи и стартувај
Зачувај ја скриптата во фолдер по избор. Десен клик → Run with PowerShell, или во PowerShell: .\Skener-Biblioteka.ps1
ЧЕКОР 2
Наведи фолдери
Стандардно скенира \\192.168.88.88\Lokalen и \\NASVISTV\Lokalen. За свои: .\Skener-Biblioteka.ps1 -Folders "\\NAS\Lokalen\MUZIKA"
ЧЕКОР 3
Качи biblioteka.csv
Добиениот CSV качи го на страницата Библиотека. Секое ново скенирање само ги дополнува новите фајлови.
Формат на CSV
Точка-запирка како разделник, UTF-8. Апликацијата чита и вредности како00:32:35 за должина и хексадецимална големина.
path;duration_ms;size_bytes;width;height;fps_num;fps_den;audio_rate;audio_channels;audio_lang
Со ffprobe.exe до скриптата се читаат точни fps и аудио податоци; без него се користат метаподатоците на Windows.
Skener-Biblioteka.ps1
# ===============================================================
# Vistel Playout - Media Library Scanner
# Creates a CSV with path, duration, size, resolution, fps and audio.
# Usage (PowerShell):
# .\Skener-Biblioteka.ps1
# Or with custom folders:
# .\Skener-Biblioteka.ps1 -Folders "\\192.168.88.88\Lokalen\EMISII","\\NASVISTV\Lokalen\MUZIKA"
# For exact fps/audio metadata, put ffprobe.exe next to this script (optional).
# ===============================================================
param(
[string[]]$Folders = @(
"\\192.168.88.88\Lokalen",
"\\NASVISTV\Lokalen"
),
[string]$Output = "",
[string[]]$Extensions = @(".mp4",".mov",".mxf",".mkv",".avi",".mpg",".mpeg",".ts",".wmv",".m4v"),
[string[]]$ExcludeFolders = @(
"\\NASVISTV\Lokalen\ARHIVA KADRI"
)
)
$ErrorActionPreference = "Continue"
# Use the script directory, or Desktop when no script directory is available.
$baseDir = $PSScriptRoot
if ([string]::IsNullOrWhiteSpace($baseDir)) { $baseDir = [Environment]::GetFolderPath("Desktop") }
if ([string]::IsNullOrWhiteSpace($baseDir)) { $baseDir = $env:USERPROFILE }
if ([string]::IsNullOrWhiteSpace($Output)) { $Output = Join-Path $baseDir "biblioteka.csv" }
$ffprobe = Join-Path $baseDir "ffprobe.exe"
$useFfprobe = Test-Path $ffprobe
$shell = New-Object -ComObject Shell.Application
$rows = New-Object System.Collections.Generic.List[string]
$rows.Add("path;duration_ms;size_bytes;width;height;fps_num;fps_den;audio_rate;audio_channels;audio_lang")
function Get-ShellMeta($file) {
try {
$folder = $shell.Namespace($file.DirectoryName)
if ($null -eq $folder) { return $null }
$item = $folder.ParseName($file.Name)
if ($null -eq $item) { return $null }
$dur = $folder.GetDetailsOf($item, 27) # Duration hh:mm:ss
$dim = $folder.GetDetailsOf($item, 31) # Dimensions
$rate = $folder.GetDetailsOf($item, 315) # Frame rate (1000 * fps)
$ms = 0
if ($dur -match "(\d+):(\d+):(\d+)") {
$ms = ([int]$Matches[1]*3600 + [int]$Matches[2]*60 + [int]$Matches[3]) * 1000
}
$w = 0; $h = 0
if ($dim -match "(\d+)\s*x\s*(\d+)") { $w = [int]$Matches[1]; $h = [int]$Matches[2] }
$fpsNum = 25; $fpsDen = 1
if ($rate -match "(\d+)") {
$f = [int]$Matches[1]
if ($f -gt 0) { $fpsNum = $f; $fpsDen = 1000 }
}
return [pscustomobject]@{ ms=$ms; w=$w; h=$h; fpsNum=$fpsNum; fpsDen=$fpsDen; rate=48000; ch=2; lang="und" }
} catch { return $null }
}
function Get-FfprobeMeta($file) {
try {
$json = & $ffprobe -v quiet -print_format json -show_format -show_streams "$($file.FullName)" | Out-String
if ([string]::IsNullOrWhiteSpace($json)) { return $null }
$data = $json | ConvertFrom-Json
$v = $data.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
$a = $data.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1
$ms = [int]([double]$data.format.duration * 1000)
$fpsNum = 25; $fpsDen = 1
if ($v -and $v.r_frame_rate -match "^(\d+)/(\d+)$") { $fpsNum = [int]$Matches[1]; $fpsDen = [int]$Matches[2] }
return [pscustomobject]@{
ms = $ms
w = if ($v) { [int]$v.width } else { 0 }
h = if ($v) { [int]$v.height } else { 0 }
fpsNum= $fpsNum
fpsDen= $fpsDen
rate = if ($a) { [int]$a.sample_rate } else { 48000 }
ch = if ($a) { [int]$a.channels } else { 2 }
lang = if ($a -and $a.tags -and $a.tags.language) { $a.tags.language } else { "und" }
}
} catch { return $null }
}
$count = 0
$excluded = 0
foreach ($root in $Folders) {
if (-not (Test-Path $root)) { Write-Warning "Folder is not available: $root"; continue }
Write-Host "Scanning: $root"
Get-ChildItem -LiteralPath $root -Recurse -File -ErrorAction SilentlyContinue |
Where-Object {
$ok = $Extensions -contains $_.Extension.ToLower()
if ($ok -and $ExcludeFolders) {
foreach ($ex in $ExcludeFolders) {
if ($_.FullName -like "*$ex*") { $ok = $false; break }
}
}
if (-not $ok -and ($Extensions -contains $_.Extension.ToLower())) { $excluded++ }
$ok
} |
ForEach-Object {
$file = $_
$meta = $null
if ($useFfprobe) { $meta = Get-FfprobeMeta $file }
if ($null -eq $meta -or $meta.ms -le 0) { $meta = Get-ShellMeta $file }
if ($null -eq $meta) { return }
$line = "{0};{1};{2};{3};{4};{5};{6};{7};{8};{9}" -f $file.FullName, $meta.ms, $file.Length, $meta.w, $meta.h, $meta.fpsNum, $meta.fpsDen, $meta.rate, $meta.ch, $meta.lang
$rows.Add($line)
$count++
if ($count % 200 -eq 0) { Write-Host " ... $count files" }
}
}
$enc = New-Object System.Text.UTF8Encoding($true)
try {
[System.IO.File]::WriteAllLines($Output, $rows, $enc)
} catch {
$fallback = Join-Path ([Environment]::GetFolderPath("Desktop")) "biblioteka.csv"
if ($fallback -eq $Output) { $fallback = Join-Path $env:TEMP "biblioteka.csv" }
Write-Warning "Cannot write to $Output. Writing to $fallback"
[System.IO.File]::WriteAllLines($fallback, $rows, $enc)
$Output = $fallback
}
Write-Host ""
Write-Host "Done: $count files -> $Output" -ForegroundColor Green
if ($excluded -gt 0) { Write-Host "Excluded: $excluded files from ARHIVA KADRI" -ForegroundColor Yellow }
Write-Host "Upload this CSV in the application on the Library page."
