Get-Subtitle.ps1

<#PSScriptInfo
 
.VERSION 0.2.4
 
.GUID 550cd282-235c-4d77-83ab-789eec91f5c5
 
.AUTHOR JeB94
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS subtitle movies series api subdb srt sub subtitles tv
 
.LICENSEURI https://github.com/JeB94/PoSh/blob/master/LICENSE
 
.PROJECTURI https://github.com/JeB94/PoSh
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
#>


<#
 
.DESCRIPTION
 Download subtitles from theSUBDB API.
 
 
.EXAMPLE
Get-Subtitle.ps1 -InstallShortCut
 
.EXAMPLE
Get-Subtittle.ps1 -UninstallShortCut
 
.EXAMPLE
Get-Subtitle.ps1 -Path 'D:\movies\movie.mp4' -Language es
 
.EXAMPLE
Get-ChildItem "D:\movies\*.mp4" | Select-Object -ExpandProperty FullName | Get-Subtitle.ps1 -Language es -DestinationPath D:\Subtitles
 
.EXAMPLE
Get-Subtitle.ps1 (Get-ChildItem -Filter *.mkv -Path 'D:\movies' | select-Object -ExpandProperty FullName) -Language it -Verbose
#>
 

#Requires -Version 5

[CmdletBinding(DefaultParameterSetName = 'Download',
    SupportsShouldProcess = $True,
    ConfirmImpact = 'High')]

param (
    [Parameter(Mandatory, 
        ValueFromPipeline,
        Position = '0', ParameterSetName = 'Download')]
    [String[]]
    $Path,

    [Parameter(ParameterSetName = 'Download')]
    [String]
    $DestinationPath, 

    [Parameter(ParameterSetName = 'Download', Position = '1')]
    [ValidateSet('es', 'en',
        'fr', 'it',
        'nl', 'pl',
        'pt', 'ro',
        'sv', 'tr') ]
    [String]
    $Language = 'en',

    [Switch]
    $Force,

    [Parameter(ParameterSetName = 'Download')]
    [ValidateSet('srt', 'sub')]
    [String]
    $SubtitleExtension = 'srt',

    [Parameter(ParameterSetName = 'Install')]
    [Switch]
    $InstallShortCut, 

    [Parameter(ParameterSetName = 'Uninstall')]
    [Switch]
    $UninstallShortCut

)

begin { 
    Write-Verbose "[BEGIN ] Starting $($MYINVOCATION.MyCommand)"

    # Install BurntToast for notifications
    $requiredModule = 'BurntToast'
    if ($requiredModule -notin (Get-Module -ListAvailable -Verbose:$False).Name) {
        Write-Verbose -Message "[BEGIN ] BurntToast module not found "
        try {

            Write-Verbose -Message "[BEGIN ] Installing BurntToast module" -Verbose
            Install-Module 'BurntToast' -Force
        }
        catch {
            Write-Warning 'Could not install BurntToast Module'
        }
    }

    function Get-SubDBFileHash {
        param (
            [ValidateNotNullOrEmpty()]
            [String]
            $Path)

        try {

            $file = Get-Item -LiteralPath $Path -ErrorAction Stop
            if ($file -isnot [System.IO.FileInfo]) {
                throw "$Path is not a file."
            }

            if ($file.Length -le 128KB) {
                $data = [System.IO.File]::ReadAllBytes($file.FullName)
            }
            else {
                $data = New-Object byte[](128KB)

                $stream = $file.OpenRead()

                $stream.Read($data , 0, 64KB) | Out-Null

                $stream.Position = $stream.Length - 64KB

                $stream.Read($data, 64KB, 64KB) | Out-Null

                $stream.Close()
            }
            Get-FileHash -InputStream ([IO.MemoryStream]$data) -Algorithm MD5 | Select-Object -ExpandProperty Hash
        }
        catch {
            Write-Error $_
        }
    } # Get-SubDBFileHash
    
    function Rename-FileItem {
        param (
            [String]
            $Path
        )
        ($Path -replace '`+\[', '[') -replace '`+\]', ']'
    }

    $ValidateExtensions = @(
        ".avi", ".mp4",
        ".mkv", ".mpg",
        ".mpeg", ".mov",
        ".rm", ".vob",
        ".wmv", ".flv", 
        ".flv", ".3gp",
        "3g2"
    ) 

    $RegistryPath = 'Registry::HKEY_CLASSES_ROOT\``*\Shell'
    $SubtitlePath = Join-Path $RegistryPath 'Subtitle'
} # begin

PROCESS {

    if ($PsCmdlet.ParameterSetName -eq 'install') {

        if ($PsCmdlet.ShouldProcess("Registry::HKEY_CLASSES_ROOT", "InstallShortCut")) {

            Write-Verbose '[PROCESS] Creating registry values'

            if (!(Test-Path $SubtitlePath) -or $Force) {

                try {
                    $Param = @{
                        ErrorAction = 'Stop'
                    }

                    if ($Force) {
                        $Param.Add("Force", $True)
                    } 

                    Write-Verbose "[PROCESS] Creating Subtitle value"
                    New-Item -Path $RegistryPath -Name 'Subtitle' -ItemType Directory @Param | Out-Null
                    New-ItemProperty -Path $SubtitlePath -Name 'SubCommands' @Param | Out-Null

                    # create child folder
                    New-Item -Path $SubtitlePath -Name 'shell' @Param | Out-null
                    $shellPath = Join-Path $SubtitlePath 'shell'

                    # create folder for each language
                    # spanish
                    Write-Verbose "[PROCESS] Adding Spanish option"
                    $LanguagePath = Join-Path $shellPath "Spanish"
                    New-Item -Path $shellPath -Name "Spanish" -Value "Spanish" @Param | Out-Null
                    New-Item -Path $LanguagePath -Name Command @Param -Value "powershell.exe -WindowStyle Hidden -ExecutionPolicy ByPass -noprofile -command Get-Subtitle.ps1 '`"%1`"' es -Force"  | Out-Null
                    Write-Verbose "[PROCESS] Added spanish language"

                    # english
                    Write-Verbose "[PROCESS] Adding English option"
                    $LanguagePath = Join-Path $shellPath "English"
                    New-Item -Path $shellPath -Name "English" -Value "English" @Param | Out-Null
                    New-Item -Path $LanguagePath -Name Command @Param -Value "powershell.exe -WindowStyle Hidden -ExecutionPolicy ByPass -noprofile -command Get-Subtitle.ps1 '`"%1`"' en -Force"  | Out-Null
                    Write-Verbose "[PROCESS] Added English language"
                }
                catch {
                    Write-Error $_
                } # try catch
            }
            else {
                Write-Warning "[PROCESS] Shorcut is already configured"
            }
        } # if
    }
    
    # Uninstall ParameterSet
    elseif ($PsCmdlet.ParameterSetName -eq 'uninstall' ) {
        Write-Verbose "[PROCESS] Removing registry values"

        if ($PsCmdlet.ShouldProcess("Registry::HKEY_CLASSES_ROOT", "UninstallShortCut")) {
            if (Test-Path $SubtitlePath) {

                try {
                    Write-Verbose "[PROCESS] Removing values"
                    Remove-Item -Path $SubtitlePath -Recurse -ErrorAction Stop | Out-Null  
                    Write-Verbose "[PROCESS] Shortcut was uninstalled"
                }
                catch {
                    Write-Error "Denied Access. Run cmdlet as Administrator"
                }
            }
            else {
                Write-Warning "[PROCESS] Shorcut have already been uninstalled"
            }

        } # if should process

    }

    # Download ParameterSet
    else {
        Foreach ($File in $Path ) {
            Write-Verbose "[PROCESS] Analyzing file $File"

            # avoid problems with wildcards
            $File = Rename-FileItem $File

            # check file
            if (-not (Test-Path -LiteralPath $File)) { 
                Write-Error "File not found"
                Continue
            }
            # check extension
            elseif ([io.Path]::GetExtension($file) -notin $ValidateExtensions) {
                Write-Error "File extension is not valid"
                continue
            }

            $VideoFile = Get-Item -LiteralPath $File
            $VideoFilePath = Rename-FileItem $VideoFile.FullName

            Write-Verbose "[PROCESS] Calculating hash of $($VideoFile.BaseName)"
            $HashFile = Get-SubDBFileHash -Path $VideoFilePath
            Write-Verbose "[PROCESS] Hash was calculated correctly"

            # api info
            $Url = "http://api.thesubdb.com/?action={0}&hash={1}&language={2}" -f "download" , $HashFile, $Language
            $UserAgent = 'SubDB/1.0 (Get-Subtitle/1.0; http://github.com/JeB94/Posh)'


            try {
                # query api
                $QuerySubtitle = Invoke-RestMethod -UserAgent $UserAgent -Uri $url -ErrorAction Stop
                $VideoBaseName = '{0}.{1}' -f $VideoFile.BaseName, $SubtitleExtension
                
                $params = @{
                    Encoding = 'default'
                }

                # supports force
                if ($Force) {
                    $params.Force = $True
                }

                # adding destination path
                if (!($PSBoundParameters.ContainsKey('DestinationPath'))) {
                    $DestinationPath = $VideoFile.Directory
                }

                $params.LiteralPath = Join-Path -Path $DestinationPath -ChildPath  $VideoBaseName

                # saving file
                try {
                    $QuerySubtitle | Out-File @Params 
                    Write-Verbose "[PROCESS] File saved on $DestinationPath"

                    # send desktop notificacion
                    try {
                        New-BTAppId -WarningAction SilentlyContinue
                        New-BurntToastNotification -Text "Subtitle Downloaded!", "$VideoBaseName" -Silent
                    } 
                    catch {
                        # BurntToast module is not installed
                        Out-Null
                    }

                    $Output = @{
                        File     = $VideoFile.BaseName
                        Language = $Language
                    }

                    [PSCustomObject]$output
                }
                catch {
                    Write-Error $_
                }
            }
            catch {
                # search for available languages
                Write-Warning "$($language.ToUpper()) subtitle not found"
                Write-Warning "Searching for available subtitles"
                $Url = "http://api.thesubdb.com/?action={0}&hash={1}" -f "search" , $HashFile

                # query api for available languages
                try {
                    $SearchSubtitle = Invoke-RestMethod -UserAgent $userAgent -Uri $Url -ErrorAction Stop
                    Write-Verbose "[PROCESS] Found available subtitle languages"
                    $Output = @{
                        AvailableLanguages = $SearchSubtitle -split ","
                    }

                    [PSCustomObject]$Output
                }
                catch {
                    Write-Warning "Not found any available subtitle for the file $($Videofile.BaseName)"
                }
            } # try catch
        } # foreach
    } # parameter set download
} # process

end {
    Write-Verbose "[END ] Ending $($MyInvocation.MyCommand)"
}