kinopoisk.ps1

### Includes:
. "$PSScriptRoot\network.ps1"
. "$PSScriptRoot\text.ps1"


### Types:
class FindKinopoiskResult {
  [string]$Name
  [string]$NameTranslit
  [int]$Year
  [string[]]$CountriesAny
  [string]$Type
  $Result = $null
  $AllResults = @()
  [bool]$Success = $false
  [string]$Message = ""
}

### Variables:
$kinopoisk_content_types = @{
  'movie'     = @('movie', 'cartoon')
  'film'      = @('movie', 'cartoon')
  'tvshow'    = @('tv-series', 'animated-series', 'anime')
  'tv-series' = @('tv-series', 'animated-series', 'anime')
  'tvseries'  = @('tv-series', 'animated-series', 'anime')
}

$kinopoisk_country_languages = @{
  'Россия' = 'ru'
  'США'    = 'en'
}

### Functions:

### Example:
#curl.exe -H "X-API-KEY: ..." "https://api.kinopoisk.dev/v1.4/movie/search?page=1&limit=10&query=farang" -o test_result1.json

function Invoke-KinopoiskRequest {
  [CmdletBinding()]
  param (
    [string]$Url,
    [ValidateSet('JSON', 'Text', IgnoreCase = $true)]
    [string]$ResponseType = 'JSON',
    [System.Collections.Specialized.OrderedDictionary]$Query = @{ } ### ordered - обязательно, чтобы был одинаковый порядок параметров
  )
  Write-Verbose "Invoke-KinopoiskRequest: begin"
  Write-Verbose "Invoke-KinopoiskRequest: Url: '$Url'"
  
  $config = Get-Config
  $kinopoisk_config = $config.Kinopoisk
  
  $api_url = $kinopoisk_config.ApiUrl
  if (!$api_url) { throw "Kinopoisk API URL is empty" }
  Write-Verbose "Invoke-KinopoiskRequest: api_url: '$api_url'"
  
  $api_key = $kinopoisk_config.ApiKey
  if (!$api_key) { throw "Kinopoisk API key is empty" }
  Write-Verbose "Invoke-KinopoiskRequest: api_key: '$($api_key.Substring(0, 2) + "****")'"
  
  $full_url = Combine-Url -Segments $api_url, $Url
  Write-Verbose "Invoke-KinopoiskRequest: full_url: '$full_url'"
  
  $headers = @{
    'X-API-KEY' = $api_key
  }
  
  Get-UrlContent -Url $full_url -ResponseType $ResponseType -Headers $headers -Query $Query
  
}

### Возвращает все результаты поиска:
function Find-KinopoiskMovie {
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory = $true,
               ValueFromPipeline = $true)]
    [string]$Name,
    [int]$Limit = 10,
    [ValidateSet('movie', 'film', 'tvshow', 'tv-series', 'tvseries', IgnoreCase = $true)]
    [string]$Type = 'movie'
  )
  
  process {
    Write-Verbose "Find-KinopoiskMovie: Name: '$Name'"
    
    if (!$kinopoisk_content_types.ContainsKey($Type)) {
      throw "Invalid Kinopoisk content type '$Type'"
    }
    
    $kp_types = $kinopoisk_content_types[$Type]
    
    # Invoke-KinopoiskRequest -Url "movie/search?page=1&limit=$($Limit)&query=$([URI]::EscapeUriString($Name))" `
    # | select -ExpandProperty docs | ? { $_.type -eq $Type }
    
    ### [ordered] обязательно, иначе в PS5, PS7 будет разный порядок параметров, из-за этого разный URL и ключ кэша:
    $order = 100
    Invoke-KinopoiskRequest -Url 'movie/search' -Query ([ordered]@{
        page  = 1
        limit = $Limit
        query = $Name
      }) | select -ExpandProperty docs | ? { $_.type -in $kp_types } | % {
      Add-Member -InputObject $_ -MemberType NoteProperty -Name order -Value ($order--)
      
      ### Определить язык ru/en/... по стране:
      $original_language = ''
      $countries = @($_.countries.name | % { "$_".Trim() } | ? { $_ })
      foreach ($country in $countries) {
        if ($kinopoisk_country_languages.ContainsKey($country)) {
          $original_language = $kinopoisk_country_languages[$country]          
          break
        }
      }
      Write-Verbose "Find-KinopoiskMovie: original_language: '$original_language'"
      Add-Member -InputObject $_ -MemberType NoteProperty -Name OriginalLanguage -Value $original_language
      
      $_
    }
  }
}

### Возвращает один результат поиска:
function Find-KinopoiskMovieSingle {
  [CmdletBinding()]
  param (
    [Parameter(Mandatory = $true,
               ValueFromPipeline = $true)]
    [string]$Name,
    [int]$Year,
    [string[]]$CountriesAny,
    [ValidateSet('movie', 'film', 'tvshow', 'tv-series', 'tvseries', IgnoreCase = $true)]
    [string]$Type = 'movie',
    [switch]$TryTranslitName
  )
  
  Write-Verbose "Find-KinopoiskMovieSingle: Name: '$Name', Type: '$Type', Year: $Year, CountriesAny: [$CountriesAny], TryTranslitName: $TryTranslitName"
  
  if (!$Year) {
    Write-Warning "Find-KinopoiskMovieSingle: Year is not specified, result may be inaccurate"
  }
  
  $kp_info_all = @(Find-KinopoiskMovie -Name $Name -Type $Type | ? {
      $_.name
      ### Разница в длине имен не более 5:
      # $_.name -and ([math]::Abs($Name.Length - $_.name.Length) -le 5) ### !!!
    })
  
  $result = [FindKinopoiskResult]@{
    Name         = $Name
    Year         = $Year
    CountriesAny = $CountriesAny
    Type         = $Type
  }
  
  try {
    ### Если не нашли, пробуем транслитеровать имя eng->rus и искать снова:
    if (!$kp_info_all) {
      if ($TryTranslitName) {
        Write-Verbose "Find-KinopoiskMovieSingle:"
        $name_translit = Translit-EngToRus $Name
        Write-Verbose "Find-KinopoiskMovieSingle: try find by transliterated name (begin) '$name_translit'"
        $result = Find-KinopoiskMovieSingle -Name $name_translit -Year $Year -CountriesAny $CountriesAny -Type $Type
        $result.NameTranslit = $name_translit
        $result.Message += ", name transliterated"
        return $result
      } else {
        $result.Message = "Cannot find movie '$($Name)'"
        return $result
      }
    }
    
    $result.AllResults = $kp_info_all
    
    Write-Host "Find-KinopoiskMovieSingle: Before filtration:`r`n$($kp_info_all | select id, name, alternativeName, type, year, countries | ft -AutoSize | Out-String)" -fo Cyan
    
    ### Фильтруем по странам:
    if ($CountriesAny) {
      $kp_info_all = @($kp_info_all | ? {
          $kp_info_countries = @($_.countries.name)
          $matched_countries = @($CountriesAny | ? { $_ -in $kp_info_countries })
          [bool]$matched_countries
        })
      
      if (!$kp_info_all) {
        if ($TryTranslitName) {
          $name_translit = Translit-EngToRus $Name
          Write-Verbose "Find-KinopoiskMovieSingle: try find by transliterated name (country) '$name_translit'"
          $result = Find-KinopoiskMovieSingle -Name $name_translit -Year $Year -CountriesAny $CountriesAny -Type $Type
          $result.NameTranslit = $name_translit
          $result.Message += ", name transliterated"
          return $result
        } else {
          $result.Message = "Cannot find movie '$($Name)' with filter by any country: [$($CountriesAny -join ", ")]"
          return $result
        }
      }
    }
    
    ### Ищем по году +-1:
    if ($Year) {
      $years = @($Year, ($Year - 1), ($Year + 1)) ### !!! скобки обязательно
      foreach ($y in $years) {
        Write-Host "Find by year $y" -fo Cyan
        $delta = $y - $Year
        $delta_msg = if ($delta) { " ($('{0:+#;-#;0}' -f $delta))" } else { '' }
        $kp_info_year = @($kp_info_all | ? { $_.year -eq $y })
        if ($kp_info_year) {
          if ($kp_info_year.Length -eq 1) {
            $result.Result = $kp_info_year[0]
            $result.Success = $true
            $result.Message = "Found single by year $($y)$delta_msg"
            return $result
            # break
          } else {
            $result.Result = $kp_info_year[0]
            $result.Success = $true
            $result.Message = "Found multiple by year $($y)$delta_msg, select 1st"
            return $result
            # break
          }
        }
      }
      
      if ($TryTranslitName) {
        $name_translit = Translit-EngToRus $Name
        Write-Verbose "Find-KinopoiskMovieSingle: try find by transliterated name (year) '$name_translit'"
        $result = Find-KinopoiskMovieSingle -Name $name_translit -Year $Year -CountriesAny $CountriesAny -Type $Type
        $result.NameTranslit = $name_translit
        $result.Message += ", name transliterated"
        return $result
      } else {
        $result.Message = "Cannot find movie '$($Name)' by year $Year"
        return $result
      }
    }
    
    ### Если результат еще не установлен
    if ($kp_info_all -and (!$result.Success)) {
      if ($kp_info_all.Length -eq 1) {
        $result.Result = $kp_info_all[0]
        $result.Success = $true
        $result.Message = "Found single after filtration"
      } else {
        $result.Result = $kp_info_all | Sort-Object order, year -Descending | select -First 1
        # $result.Result = $kp_info_all | Sort-Object year, order -Descending | select -First 1
        $result.Success = $true
        $result.Message = "Found multiple after filtration, select 1st"
      }
    }
    
    if (!$result.Success) {
      if ($TryTranslitName) {
        $name_translit = Translit-EngToRus $Name
        Write-Verbose "Find-KinopoiskMovieSingle: try find by transliterated name (end) '$name_translit'"
        $result = Find-KinopoiskMovieSingle -Name $name_translit -Year $Year -CountriesAny $CountriesAny -Type $Type
        $result.NameTranslit = $name_translit
        $result.Message += ", name transliterated"
        return $result
      } else {
        $result.Message = "Cannot find movie '$($Name)' after filtering"
        # return $result
      }
    }
    
    return $result
    
  } finally {
    Write-Verbose "Find-KinopoiskMovieSingle: $($result.Message)"
  }
  
}