Functions/GenXdev.Console/Invoke-VLCPlayer.ps1

################################################################################
<#
.SYNOPSIS
Plays media files using VLC player with flexible filtering options.
 
.DESCRIPTION
This function scans for media files in the specified directory, filters them based
on file type and optional keywords, creates a playlist, and launches VLC player.
It supports videos, audio files and pictures with automatic installation of VLC
if needed.
 
.PARAMETER DirectoryPath
The directory path to scan for media files. Defaults to current directory.
 
.PARAMETER Keywords
Keywords to search for in subtitle files (.srt) and media descriptions.
 
.PARAMETER SearchMask
File pattern to filter media files. Default is "*".
 
.PARAMETER OnlyVideos
Filter to only include video files in the playlist.
 
.PARAMETER OnlyAudio
Filter to only include audio files in the playlist.
 
.PARAMETER OnlyPictures
Filter to only include picture files in the playlist.
 
.PARAMETER IncludeVideos
Additionally include video files in the playlist.
 
.PARAMETER IncludeAudio
Additionally include audio files in the playlist.
 
.PARAMETER IncludePictures
Additionally include picture files in the playlist.
 
.EXAMPLE
# Play all media in current directory
Invoke-VLCPlayer
 
.EXAMPLE
# Play only pictures from Pictures folder
vlc ~\Pictures -OnlyPictures
 
.EXAMPLE
# Play videos containing "birthday" in subtitles
media ~\Videos -Keywords "birthday" -OnlyVideos
#>

function Invoke-VLCPlayer {

    [CmdletBinding()]
    [Alias("vlc", "media")]
    param(
        ########################################################################
        [Parameter(
            Mandatory = $false,
            Position = 0,
            HelpMessage = "Specify the directory path containing media files."
        )]
        [string]$DirectoryPath = ".\*",
        ########################################################################
        [Parameter(
            Mandatory = $false,
            Position = 1,
            HelpMessage = "Keywords to search in file metadata"
        )]
        [string[]]$Keywords = @(),
        ########################################################################
        [Parameter(
            Mandatory = $false,
            Position = 2,
            HelpMessage = "File pattern to filter media files"
        )]
        [string]$SearchMask = "*",
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Only include video files in the playlist"
        )]
        [switch]$OnlyVideos,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Only include audio files in the playlist"
        )]
        [switch]$OnlyAudio,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Only include pictures in the playlist"
        )]
        [switch]$OnlyPictures,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Additionally include videos in the playlist"
        )]
        [switch]$IncludeVideos,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Additionally include audio files in the playlist"
        )]
        [switch]$IncludeAudio,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Additionally include pictures in the playlist"
        )]
        [switch]$IncludePictures
        ########################################################################
    )

    begin {

        # define supported file extensions for each media type
        $videoFiles = @(".mp4", ".avi", ".mkv", ".mov", ".wmv")
        $audioFiles = @(".mp3", ".flac", ".wav", ".midi", ".mid", ".au",
            ".aiff", ".aac", ".m4a", ".ogg", ".wma", ".ra", ".ram", ".rm",
            ".rmm")
        $pictureFiles = @(".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff",
            ".tif")
    }

    process {

        Write-Verbose "Scanning directory: $DirectoryPath"

        # expand path and build full search pattern
        $searchMask = "$((Expand-Path $DirectoryPath))\$SearchMask"

        # get sorted list of all matching files
        $files = Find-Item -SearchMask $searchMask -PassThru |
        Sort-Object -Property FullName

        # determine which file types to include based on parameters
        $validExtensions = $OnlyVideos ? $videoFiles : (
            $OnlyAudio ? $audioFiles : (
                $OnlyPictures ? $pictureFiles : (
                    $videoFiles + $audioFiles + $pictureFiles
                )
            )
        )

        # add additional file types if specified
        if ($IncludeVideos) {
            $validExtensions += $videoFiles
        }
        if ($IncludeAudio) {
            $validExtensions += $audioFiles
        }
        if ($IncludePictures) {
            $validExtensions += $pictureFiles
        }

        # filter files by extension and keywords
        $files = $files | Where-Object {
            try {
                if (-not ($validExtensions -contains $PSItem.Extension.ToLower())) {
                    return $false;
                }

                if ($Keywords.Length -gt 0) {
                    $srtSearchMask = [io.Path]::Combine([IO.Path]::GetDirectoryName($PSItem.FullName), [IO.Path]::GetFileNameWithoutExtension($PSItem.FullName) + "*.srt");

                    Get-ChildItem $srtSearchMask -File -ErrorAction SilentlyContinue | ForEach-Object {
                        $srt = [IO.File]::ReadAllText($PSItem.FullName);

                        foreach ($keyword in $Keywords) {
                            if ($srt -like "*$keyword*") {
                                return $true;
                            }
                        }
                    }

                    if ([IO.File]::Exists("$($PSItem.FullName):description.json")) {
                        $description = [IO.File]::ReadAllText("$($PSItem.FullName):description.json");

                        foreach ($keyword in $Keywords) {
                            if ($description -like "*$keyword*") {
                                return $true;
                            }
                        }
                    }

                    return $false;
                }

                return $true;
            }
            catch {
                return $false;
            }
        }

        if ($files.Length -eq 0) {
            Write-Host "No media files found in the specified directory."
            return
        }

        # create temporary playlist file
        $playlistPath = [System.IO.Path]::ChangeExtension(
            [System.IO.Path]::GetTempFileName(),
            ".m3u"
        )

        Write-Verbose "Creating playlist at: $playlistPath"

        # generate m3u playlist content
        $m3uContent = "#EXTM3U`r`n"
        foreach ($file in $files) {
            $m3uContent += "#EXTINF:-1, $($file.Name.Replace("_", " ").Replace(".", " ").Replace(" ", " "))`r`n$(($file.FullName))`r`n";
        }

        # save playlist file
        $m3uContent | Out-File -FilePath $playlistPath -Encoding utf8 -Force

        # copy path to clipboard
        "$playlistPath" | Set-Clipboard

        # ensure VLC is installed
        if (-not (Test-Path "C:\Program Files\VideoLAN\VLC\vlc.exe")) {
            # check if powershell module 'Microsoft.WinGet.Client' is installed
            if (-not (Get-Module -ListAvailable -Name 'Microsoft.WinGet.Client')) {
                # install module 'Microsoft.WinGet.Client'
                Install-Module -Name 'Microsoft.WinGet.Client' -Force -Scope CurrentUser -AllowClobber -SkipPublisherCheck

                # import module
                Import-Module -Name 'Microsoft.WinGet.Client'
            }

            # install VLC player using 'Microsoft.WinGet.Client'
            Install-WinGetPackage -Name 'VideoLAN.VLC' -Scope System -Force
        }

        # launch VLC with playlist
        Write-Verbose "Starting VLC player"
        Get-Process vlc -ErrorAction SilentlyContinue | Stop-Process -Force

        $p = Start-Process `
            -FilePath "C:\Program Files\VideoLAN\VLC\vlc.exe" `
            -ArgumentList @($playlistPath) `
            -PassThru

        try {
            Write-Verbose "Launching VLC player with the playlist file: $playlistPath"

            [System.Threading.Thread]::Sleep(4000) | Out-Null

            Set-VLCPlayerFocused

            # Set fullscreen
            if ($p -and $p.MainWindowHandle -ge 0) {
                [System.Threading.Thread]::Sleep(1000) | Out-Null

                if (($Global:DefaultSecondaryMonitor -gt 0) -and ((Get-MonitorCount) -gt 1)) {
                    Set-WindowPosition -Process $p -Fullscreen -Monitor -2
                }
                else {
                    Set-WindowPosition -Process $p -Right
                    Set-WindowPosition -Process (Get-PowershellMainWindowProcess) -Left
                }
            }
        }
        finally {
            Set-VLCPlayerFocused

            # send F11
            $helper = New-Object -ComObject WScript.Shell;
            $helper.sendKeys("F");

            Write-Verbose "Sending F"

            [System.Threading.Thread]::Sleep(1000) | Out-Null

            (Get-PowershellMainWindow).SetForeground();
        }
    }

    end {
    }
}
################################################################################