aquilax1 / Plex-IMDb-Extras-Downloader

Powershell script for Plex to download extra content from IMDb
GNU General Public License v3.0
10 stars 0 forks source link

(401) Unauthorized #2

Open victarmas opened 7 years ago

victarmas commented 7 years ago

When trying to run the script, I get a 401 Unauthorized error. Should I be passing a token somehow?

Exception calling "DownloadString" with "1" argument(s): "The remote server ret
urned an error: (401) Unauthorized."
At C:\Users\Plex\Desktop\Plex-IMDb-Extras-Downloader-master\PlexIMDbExtrasDownl
oader.ps1:10 char:50
+ $movies=$dirs | foreach{([xml]$web.DownloadString <<<< (($plex+"/library/sect
ions/{0}/all") -F $_.key)).MediaContainer.Video | where {$_.type -eq "movie"} |
 select -p key, title, year, @{Name="path"; Expression={(split-path -Path ([Sys
tem.Uri]::UnescapeDataString($_.Media.Part.file)))}}, @{Name="imdb"; Expression
={}} | where {!(test-path -LiteralPath ($_.path+"\imdb_extras.xml"))}} | sort -
p title
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

1/1/2017 2:39:36 PM All the movies have already been processed
pallemannen commented 7 years ago

I run into the same problem. The nice solution would be a "-username" parameter to the script, and then getting prompted for the password. The second best would be variables to set at the top of the script.

TylerAlmeida commented 7 years ago

You just need to edit the powershell script and add in your token. If you're a plex pass subscriber, I believe this is easy to get. If you don't, the easiest way I've found is to go to any item in your library, look at its info and click "view XML". The end of that string in your address bar will be your token "X-Plex-Token=****"

Take that token, and append it to the 3 times you see "$plex" string line 8 and line 10: $plex+"/library/sections/{0}/all?X-Plex-Token=xxxxxxxx" line 15: $doc.Load($plex+$_.key+"?X-Plex-Token=xxxxxx"

This should take care of authorization. If I was any better at Git, I'd upload the fixed file here. Maybe tonight :)

wheelerj commented 6 years ago

TylerAlmeida,

Thank you for your work on this! However, I am unable to get the script working even after fixing the token piece. Do you have an updated script? currently, I am getting the following errors:

image

The code I am using is:

param([String[]] $extras, [String[]] $libraries, [String] $plex="http://localhost:32400", [Int] $max=0)
Function Remove-InvalidFileNameChars { param([Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String]$Name) $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''; $re = "[{0}]" -f [RegEx]::Escape($invalidChars); return ($Name -replace $re)}
$web=New-Object System.Net.WebCLient
$doc=New-Object System.Xml.XmlDocument
#imdb video types to plex extra content type mapping
$map=@{"Clip"="Scene";"Featurette"="Featurette";"Interview"="Interview";"Promo"="Short";"Trailer"="Trailer";"Video"="BehindTheScenes"}
#get all the library and filter them if $libraries is defined
$dirs=([xml]$web.DownloadString($plex+"/library/sections/{0}/all?X-Plex-Token=MYTOKEN")).MediaContainer.Directory | where {$libraries -eq $Null -or $libraries -contains $_.title }
#get all  the movies of the libraries, select only the needed data and esclude all the already processed movies, which have the imdb_extras.xml file in their folder
$movies=$dirs | foreach{([xml]$web.DownloadString(($plex+"/library/sections/{0}/all?X-Plex-Token=MYTOKEN") -F $_.key)).MediaContainer.Video | where {$_.type -eq "movie"} | select -p key, title, year, @{Name="path"; Expression={(split-path -Path ([System.Uri]::UnescapeDataString($_.Media.Part.file)))}}, @{Name="imdb"; Expression={}} | where {!(test-path -LiteralPath ($_.path+"\imdb_extras.xml"))}} | sort -p title
if ($movies -eq $Null) { write-host (get-date) "All the movies have already been processed" }
else
{
    #get imdb id from the movie details
    $movies | foreach { $doc.Load($plex+$_.key+"?X-Plex-Token=MYTOKEN"); $_.imdb=[regex]::match($doc.MediaContainer.Video.guid,"imdb://(.*)\?").Groups[1].Value}
    #search imdb id of unmatched movies with ombd with movie title and year 
    $movies | where { [System.String]::IsNullOrEmpty($_.imdb) } | foreach { $doc.Load(("http://www.omdbapi.com/?t={0}&y={1}&plot=short&r=xml" -F [System.Uri]::EscapeDataString($_.title), $_.year)); $_.imdb=$doc.root.movie.imdbID}
    #get titles of unmatched movies 
    $nomatches=$movies | where { [System.String]::IsNullOrEmpty($_.imdb) } | foreach {$_.title}
    if ($nomatches -ne $Null)  { write-host (get-date) "There are unmached movies: " ($nomatches -join ", ") }
    #removing movies without imdb key, which aren't yet identified by plex or with omdb
    $movies=$movies | where { ![System.String]::IsNullOrEmpty($_.imdb) }
    if ($movies -eq $Null) { write-host (get-date) "All other movies have already been processed" }
    else
    {
        #if $max is defined processing only $max movies (it was a debugging feature, but it could be useful)
        if ($max -gt 0) { $movies=$movies | select -first $max }
        #get videos on imdb, max 60 videos (2 pages) per movie, the download url is missing here
        $movies=$movies | select *,@{Name="videos"; Expression={$path=$_.path; $page=$web.DownloadString("http://www.imdb.com/title/{0}/videogallery?ref_=tt_ov_vi_sm" -F $_.imdb)+$web.DownloadString("http://www.imdb.com/title/{0}/videogallery?ref_=tt_ov_vi_sm&page=2" -F $_.imdb); [regex]::matches($page,',-1_ZA(.*?),.*?<a href="/video/imdb/(.*?)"[.|\s|\n|\r]*?>(.*?)</a>') | select @{Name="id"; Expression={$_.Groups[2].Value}},@{Name="type"; Expression={$map[$_.Groups[1].Value]}},@{Name="file"; Expression={(Remove-InvalidFileNameChars($_.Groups[3].Value))+".mp4"}},@{Name="url"; Expression={}} | where {$extras -eq $Null -or $extras -contains $_.type} }}
        #get the download url of the videos, some videos require the age verification and thus an account and to login, skip those videos
        $movies | foreach { write-host (get-date) "Processing" $_.title; if ($_.videos -eq $Null) { $_.videos = @() } else { $_.videos | foreach { $page=$web.DownloadString("http://www.imdb.com/video/imdb/{0}/imdb/single?vPage=1" -F $_.id); $_.url=[regex]::Match($page,'"videoMimeType":"video/mp4","videoUrl":"(.*?)"').Groups[1].Value } } }
        #download videos and creating the imdb_extras.xml file, to avoid to process the movies again
        $movies | foreach { $path=$_.path; $_.videos | foreach { $dir=$path+"\"+$_.type; if (!(test-path -LiteralPath $dir)) {New-Item -ItemType directory -Path $dir}; while(test-path -LiteralPath ($dir+"\"+$_.file)) {$_.file=$_.file.Replace(".mp4"," .mp4")}; if (!([System.String]::IsNullOrEmpty($_.url))) {$web.DownloadFile($_.url,$dir+"\"+$_.file)} }; [System.IO.File]::WriteAllText(($path+"\imdb_extras.xml"),($_ | ConvertTo-XML -Depth 2).OuterXML); } 
    }
}
TylerAlmeida commented 6 years ago

I ended up shelling out for a plex membership, which includes this feature :) So I haven't messed with this in a while, but feel free compare to what I have: (note I've added the "$token" parameter to make it a bit simpler)

param([String[]] $extras, [String[]] $libraries, [String] $plex="http://localhost:32400", [Int] $max=0, [String] $token)
Function Remove-InvalidFileNameChars { param([Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String]$Name) $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''; $re = "[{0}]" -f [RegEx]::Escape($invalidChars); return ($Name -replace $re)}
$web=New-Object System.Net.WebCLient
$doc=New-Object System.Xml.XmlDocument
#imdb video types to plex extra content type mapping
$map=@{"Clip"="Scene";"Featurette"="Featurette";"Interview"="Interview";"Promo"="Short";"Trailer"="Trailer";"Video"="BehindTheScenes"}
#get all the library and filter them if $libraries is defined
$dirs=([xml]$web.DownloadString($plex+"/library/sections?X-Plex-Token="+$token)).MediaContainer.Directory | where {$libraries -eq $Null -or $libraries -contains $_.title }
#get all  the movies of the libraries, select only the needed data and esclude all the already processed movies, which have the imdb_extras.xml file in their folder
$movies=$dirs | foreach{([xml]$web.DownloadString(($plex+"/library/sections/{0}/all?X-Plex-Token="+$token) -F $_.key)).MediaContainer.Video | where {$_.type -eq "movie"} | select -p key, title, year, @{Name="path"; Expression={(split-path -Path ([System.Uri]::UnescapeDataString($_.Media.Part.file)))}}, @{Name="imdb"; Expression={}} | where {!(test-path -LiteralPath ($_.path+"\imdb_extras.xml"))}} | sort -p title
if ($movies -eq $Null) { write-host (get-date) "All the movies have already been processed" }
else
{
    #get imdb id from the movie details
    $movies | foreach { $doc.Load($plex+$_.key+"?X-Plex-Token="+$token); $_.imdb=[regex]::match($doc.MediaContainer.Video.guid,"imdb://(.*)\?").Groups[1].Value}
    #search imdb id of unmatched movies with ombd with movie title and year 
    $movies | where { [System.String]::IsNullOrEmpty($_.imdb) } | foreach { $doc.Load(("http://www.omdbapi.com/?t={0}&y={1}&plot=short&r=xml" -F [System.Uri]::EscapeDataString($_.title), $_.year)); $_.imdb=$doc.root.movie.imdbID}
    #get titles of unmatched movies 
    $nomatches=$movies | where { [System.String]::IsNullOrEmpty($_.imdb) } | foreach {$_.title}
    if ($nomatches -ne $Null)  { write-host (get-date) "There are unmached movies: " ($nomatches -join ", ") }
    #removing movies without imdb key, which aren't yet identified by plex or with omdb
    $movies=$movies | where { ![System.String]::IsNullOrEmpty($_.imdb) }
    if ($movies -eq $Null) { write-host (get-date) "All other movies have already been processed" }
    else
    {
        #if $max is defined processing only $max movies (it was a debugging feature, but it could be useful)
        if ($max -gt 0) { $movies=$movies | select -first $max }
        #get videos on imdb, max 60 videos (2 pages) per movie, the download url is missing here
        $movies=$movies | select *,@{Name="videos"; Expression={$path=$_.path; $page=$web.DownloadString("http://www.imdb.com/title/{0}/videogallery?ref_=tt_ov_vi_sm" -F $_.imdb)+$web.DownloadString("http://www.imdb.com/title/{0}/videogallery?ref_=tt_ov_vi_sm&page=2" -F $_.imdb); [regex]::matches($page,',-1_ZA(.*?),.*?<a href="/video/imdb/(.*?)"[.|\s|\n|\r]*?>(.*?)</a>') | select @{Name="id"; Expression={$_.Groups[2].Value}},@{Name="type"; Expression={$map[$_.Groups[1].Value]}},@{Name="file"; Expression={(Remove-InvalidFileNameChars($_.Groups[3].Value))+".mp4"}},@{Name="url"; Expression={}} | where {$extras -eq $Null -or $extras -contains $_.type} }}
        #get the download url of the videos, some videos require the age verification and thus an account and to login, skip those videos
        $movies | foreach { write-host (get-date) "Processing" $_.title; if ($_.videos -eq $Null) { $_.videos = @() } else { $_.videos | foreach { $page=$web.DownloadString("http://www.imdb.com/video/imdb/{0}/imdb/single?vPage=1" -F $_.id); $_.url=[regex]::Match($page,'"videoMimeType":"video/mp4","videoUrl":"(.*?)"').Groups[1].Value } } }
        #download videos and creating the imdb_extras.xml file, to avoid to process the movies again
        $movies | foreach { $path=$_.path; $_.videos | foreach { $dir=$path+"\"+$_.type; if (!(test-path -LiteralPath $dir)) {New-Item -ItemType directory -Path $dir}; while(test-path -LiteralPath ($dir+"\"+$_.file)) {$_.file=$_.file.Replace(".mp4"," .mp4")}; if (!([System.String]::IsNullOrEmpty($_.url))) {$web.DownloadFile($_.url,$dir+"\"+$_.file)} }; [System.IO.File]::WriteAllText(($path+"\imdb_extras.xml"),($_ | ConvertTo-XML -Depth 2).OuterXML); } 
    }
}
wheelerj commented 6 years ago

I've had the Plex Pass for a long time, however, it doesn't seem to always be on-point for downloading trailers, this why I was looking for an alternate route. I also checked into Media Center Master. It does an 'OK' job, but it also sometimes has issues searching YouTube for trailers. So do I need to assign the $token variable my actual token to make things work?

TylerAlmeida commented 6 years ago

Correct, pass the argument -token MYTOKEN when calling the script.