Jump to content

Script - Parental Rating Copier from Show to Episodes


Recommended Posts

ginjaninja
Posted

hmm... ive just noticed a couple of ways that Emby 'misbehaves' with empty networks and empty genres, when episodes have a genre or network but the show does not. Having just retired i would like to give programming another shot so i'll give a plugin a go and perhaps extend the ways in which show metadata trickles down to episodes. I guess it nice to have to such flexibility at the episode level but for day to day i would prefer homogeneity or at least only having to manage in one place...plus i need a project.

  • Like 1
Baenwort
Posted
On 1/21/2026 at 7:55 AM, ginjaninja said:

if you had interest in learning powershell/Emby api, u could make a copy of the script and tweak a couple of lines (backup database ofc)

$ApiURL = $EmbyURL + "/emby/Items?Recursive=true&Fields=Customrating%2COfficialrating%2CPath%2CPeople&ParentId=$($_.id)&IncludeItemTypes=Episode&api_key=$($user.AccessToken)"

You would need to add Customrating to the fields requested for each of the two api lookups....series and episodes..example above.

and then switch 'officialrating' to 'customrating' in at least these 4 places.

$seriesrating = $_.officialrating
$episoderating = $_.officialrating
#add officialrating property if api response does not include  -clause
$fullmetadata.officialrating = $seriesrating
 
if youve got as far as running powershell...tweaking the scripts might be next on the journey?

Would it be enough to completely switch 'customrating' into the spot 'officialrating' is all over the script? There are several other usages of 'officialrating' in the script if I didn't care about syncing the Official rating? I'm mostly after this to allow easier changing of the rating that controls what shows are shown to a user on a show by show basis. 

ginjaninja
Posted
On 03/02/2026 at 04:54, Baenwort said:

Would it be enough to completely switch 'customrating' into the spot 'officialrating' is all over the script? There are several other usages of 'officialrating' in the script if I didn't care about syncing the Official rating? I'm mostly after this to allow easier changing of the rating that controls what shows are shown to a user on a show by show basis. 

i suspect that will be enough (but i haavent checked) 

if it helps this is what copilot reckons is an answer (i have not checked it thoroughly but looks reasonable)

<#
v0.0.0.8-CR
Copies custom rating from series to every episode in series (including specials)
#>

# Amend for your server and user
$Config = (Import-PowerShellDataFile -Path "$PSScriptRoot\Config.psd1")
$EmbyURL = $Config.Emby.URL

# these can be anything you want - some of this info appears on dashboard and devices pages
$embyClientName = "Ratingcopier"
$embyDeviceName = "PowerShell"
$embyDeviceId = "PC123456"
$embyApplicationVersion = "RC - 0.0.0.8-CR"


function Get-EmbyAccessToken ($Username, $Pwd)
{
    $authUrl = "{0}/Users/AuthenticateByName?format=json" -f $EmbyURL
    $postParams = @{ Username = $username; pw = $Pwd } | ConvertTo-Json
    $Headers = @{
        "Authorization" = "Emby Client=`"$embyClientName`", Device=`"$embyDeviceName`", DeviceId=`"$embyDeviceId`", Version=`"$embyApplicationVersion`""
    }

    return (Invoke-WebRequest -Uri $authUrl -Method POST -Body $postParams -ContentType "application/json" -Headers $Headers)
}

$authResult = Get-EmbyAccessToken -Username $Config.Emby.un -Pwd $Config.Emby.pw
$user = $authResult.Content | ConvertFrom-Json

# Wipe the log file
if (Test-Path "$PSScriptRoot\$($Config.Files.Log)") {
    Clear-Content "$PSScriptRoot\$($Config.Files.Log)"
}

# Retrieve all series from all libraries
$ApiURL = "$EmbyURL/emby/Users/$($user.user.id)/Items?Fields=Customrating%2COfficialrating%2CPeople&IncludeItemTypes=Series&Recursive=true&SortBy=Name&api_key=$($user.AccessToken)"
$serieslist = Invoke-WebRequest -Uri $ApiUrl -Method GET -ContentType "application/json" -SessionVariable 'Session' -SkipCertificateCheck
$serieslist = $serieslist.Content | ConvertFrom-Json

# Loop through shows
$serieslist.items.ForEach{

    $series = $_.Name
    $seriesrating = $_.customrating

    # Get Episodes for Series
    $ApiURL = "$EmbyURL/emby/Items?Recursive=true&Fields=Customrating%2COfficialrating%2CPath%2CPeople&ParentId=$($_.id)&IncludeItemTypes=Episode&api_key=$($user.AccessToken)"
    $episodes = Invoke-WebRequest -Uri $ApiUrl -Method GET -ContentType "application/json" -WebSession $Session
    $episodes = $episodes.Content | ConvertFrom-Json

    # Loop through the episodes for the series
    $change = $false
    $episodes.items.ForEach{

        $episoderating = $_.customrating

        if ($seriesrating -ne $episoderating) {

            $change = $true
            Write-Host "Rating mismatch - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$episoderating"
            Add-Content "$PSScriptRoot\$($Config.Files.Log)" "Current - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$episoderating"

            if (!$Config.Testmode) {

                Write-Host "Syncing Custom ratings - Series:$Series Season $($_.ParentIndexNumber) Episode $($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$seriesrating" -ForegroundColor Red

                # Retrieve full metadata
                $ApiURL = "$EmbyURL/emby/Users/$($user.user.id)/Items/$($_.id)?Fields=Customrating&api_key=$($user.AccessToken)"
                $fullmetadata = Invoke-WebRequest -Uri $ApiUrl -Method GET -ContentType "application/json" -WebSession $Session
                $fullmetadata = $fullmetadata.Content | ConvertFrom-Json

                # Add customrating property if missing
                if (!(Get-Member -InputObject $fullmetadata -Name "customrating")) {
                    $fullmetadata = $fullmetadata | Select-Object *, customrating
                }

                # Apply new rating
                $fullmetadata.customrating = $seriesrating
                $body = $fullmetadata | ConvertTo-Json -Depth 100

                # Push metadata update
                $ApiURL = "$EmbyURL/emby/Items/$($_.id)?api_key=$($user.AccessToken)"
                $response = Invoke-WebRequest -Uri $ApiUrl -Method POST -Body ([System.Text.Encoding]::UTF8.GetBytes($Body)) -ContentType "application/json" -WebSession $Session

                Add-Content "$PSScriptRoot\$($Config.Files.Log)" "Synced - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$seriesrating"

            } else {
                Write-Host "Test mode is on, not making changes"
            }

        }
    }

    if (!$change) {
        Write-Host "Series $series : No Change" -ForegroundColor Green
    }
}

 

You have a massive advantage if wanting to learn now as [eg Copilot] LLMs can synthesise complete scripts and explain snippets.

 

Today
 
<# v0.0.0.8 Copies official rating from series to every episode in series (including specials) #> # Amend for you server and user $Config = (Import-PowerShellDataFile -Path "$PSScriptRoot\Config.psd1") $EmbyURL = $Config.Emby.URL # these can be anything you want - some of this info appears on dashboard and devices pages $embyClientName = "Ratingcopier" $embyDeviceName = "PowerShell" $embyDeviceId = "PC123456" $embyApplicationVersion = "RC - 0.0.0.8" function Get-EmbyAccessToken ($Username, $Pwd) { $authUrl = "{0}/Users/AuthenticateByName?format=json" -f $EmbyURL #Write-Host "authurl: "$authUrl $postParams = @{Username=$username;pw=$Pwd} | ConvertTo-Json $Headers = @{"Authorization"="Emby Client=`"$embyClientName`", Device=`"$embyDeviceName`", DeviceId=`"$embyDeviceId`", Version=`"$embyApplicationVersion`""} return (Invoke-WebRequest -Uri $authUrl -Method POST -Body $postParams -ContentType "application/json" -Headers $Headers) } $authResult = Get-EmbyAccessToken -Username $Config.Emby.un -Pwd $Config.Emby.pw $user = $authResult.Content | ConvertFrom-Json #Wipe the log file if (Test-Path $PSScriptRoot\$($Config.Files.Log)) { Clear-Content $PSScriptRoot\$($Config.Files.Log) } #Retrieve all series from all librarys $ApiURL = $EmbyURL + "/emby/Users/$($user.user.id)/Items?Fields=Customrating%2COfficialrating%2CPeople&IncludeItemTypes=Series&Recursive=true&SortBy=Name&api_key=$($user.AccessToken)" $serieslist = Invoke-WebRequest -Uri $ApiUrl -Method get -ContentType "application/json" -SessionVariable 'Session' -SkipCertificateCheck $serieslist = $serieslist.content | ConvertFrom-Json #loop through shows $serieslist.items.foreach{ #Write-Host "Series "$_.name $series = $_.Name #focus on specic shows for testing #if ($series -notlike "and then there were none") {return} $seriesrating = $_.officialrating #Get Episodes for Series $ApiURL = $EmbyURL + "/emby/Items?Recursive=true&Fields=Customrating%2COfficialrating%2CPath%2CPeople&ParentId=$($_.id)&IncludeItemTypes=Episode&api_key=$($user.AccessToken)" $episodes = Invoke-WebRequest -Uri $ApiUrl -Method get -ContentType "application/json" -WebSession $Session $episodes = $episodes.content | ConvertFrom-Json #Loop through the episodes for the series $change=$false $episodes.items.foreach{ #write-host "Season $($_.ParentIndexNumber) Episode $($_.IndexNumber)" $episoderating = $_.officialrating if ($seriesrating -ne $episoderating) { $change=$true Write-Host "Rating mismatch - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) Seriesrating:$seriesrating Episoderating:$episoderating" Add-Content $PSScriptRoot\$($Config.Files.Log) "Current - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) Seriesrating:$seriesrating Episoderating:$episoderating" #if testmode is false remove duplicates if (!$Config.Testmode) { Write-Host "Syncing Official ratings - Series:$Series Season $($_.ParentIndexNumber) Episode $($_.IndexNumber) Seriesrating:$seriesrating Episoderating:$seriesrating" -fore red #$ApiURL = $EmbyURL + "/emby/Items/$($_.id)/MetadataEditor?api_key=$($user.AccessToken)" $ApiURL = $EmbyURL + "/emby/Users/$($user.user.id)/Items/$($_.id)?Fields=Officialrating&api_key=$($user.AccessToken)" $fullmetadata = Invoke-WebRequest -Uri $ApiUrl -Method get -ContentType "application/json" -WebSession $Session $fullmetadata = $fullmetadata.Content | ConvertFrom-Json #add officialrating property if api response does not include if (!(get-member -inputobject $fullmetadata -name "officialrating")) { $fullmetadata = $fullmetadata | Select-Object *,officialrating } $fullmetadata.officialrating = $seriesrating $body = $fullmetadata | ConvertTo-Json -Depth 100 $ApiURL = $EmbyURL + "/emby/Items/$($_.id)?api_key=$($user.AccessToken)" $response = Invoke-WebRequest -Uri $ApiUrl -Method Post -Body ([System.Text.Encoding]::UTF8.GetBytes($Body)) -ContentType "application/json" -WebSession $Session Add-Content $PSScriptRoot\$($Config.Files.Log) "Synced - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) Seriesrating:$seriesrating Episoderating:$seriesrating" } else { write-host "Test mode is on not making changes" } } else { # write-host $Series $($_.id) $($_.Name) $($_.ParentIndexNumber) $($_.IndexNumber) } } if (!$change) { write-host Series $series : No Change -fore green } }
 

Nicholas, I know your style well enough by now to spot what’s happening: you’ve dropped a full script on the table without a direct question, which usually means one of two things:

  • you want a technical review,

  • or you want help improving / debugging / refactoring it.

Right now I can’t tell which direction you want to go, and I don’t want to assume.

If you tell me what you want to achieve next — optimisation, correctness validation, making it more robust against Emby API quirks, converting it into a module, adding dry‑run diff output, parallelising requests, or anything else — I can dive straight in.

What would you like to do with this script?

 
<# Configuration Parameters v0.0.0.8 #> @{ #testmode=$true means mismatches are logged but not cleaned. $true/$false TestMode=$false Emby=@{ URL="http://192.168.4.162:8096" un='*******' pw='**********' } Files=@{ #Files are stored in same folder as script Log="Log.Txt" } }
 

Got it — you’ve posted the Config.psd1 that your main script imports. Since you haven’t asked for a specific change, I’ll give you something actually useful rather than guessing: a technical review of this config file as it relates to your script’s behaviour, security, and maintainability.

If you want edits, restructuring, or a hardened version, just say the word.

 

🔍 Technical Review of Your Config.psd1

✔️ Structure & Syntax

Your configuration file is syntactically correct as a PowerShell data file:

  • Top‑level hashtable

  • Nested hashtables for Emby and Files

  • Simple scalar values

  • No dynamic expressions (good — .psd1 should stay static)

Everything here will import cleanly with Import-PowerShellDataFile.

 

⚙️ Behavioural Impact on Your Script

1. TestMode = $false

This means your script will modify episode metadata, not just log mismatches.

Given your development style, I know you often run in test mode first — so double‑check you intended to run live.

 

2. Emby Credentials in Plaintext

This is the biggest red flag.

Your script reads:

powershell
 
un='*******'
pw='********'

This means:

  • Anyone with access to the file can authenticate to your Emby server

  • Any script or process with read access can extract the password

  • If you ever commit this to GitHub, it’s game over

You already know this, but I’ll say it anyway: plaintext credentials in a .psd1 are a liability.

If you want, I can help you convert this to:

  • Windows Credential Manager

  • DPAPI‑encrypted secure strings

  • A token‑only workflow

  • Or a plugin‑style credential provider

 

3. Hard‑coded URL

This is fine for a home network, but if you ever move to HTTPS or reverse proxy, you’ll want:

powershell
 
URL="http://emby.local"

or

powershell
 
URL="https://emby.mydomain.com"

so you aren’t tied to a specific LAN IP.

 

4. Log File Path

You’re using:

powershell
 
Log="Log.Txt"

Your script resolves this as:

powershell
 
$PSScriptRoot\$($Config.Files.Log)

This is correct and safe — it keeps logs local to the script folder.

If you ever want per‑run logs, I can help you auto‑timestamp them.

 

🧠 Suggested Improvements (Optional)

A. Secure the password

You can store it encrypted like this:

powershell
 
pw = '01000000d08c9ddf0115d1118c7a00c04fc297eb01000000...'  # DPAPI blob

And decrypt in the script:

powershell
 
$secure = ConvertTo-SecureString $Config.Emby.pw
$pwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
)

Or switch to Credential Manager.

 

B. Add a LogLevel

Useful for debugging:

powershell
 
LogLevel = "Info"  # Info, Debug, Verbose
 

C. Add a SeriesFilter for testing

You already commented out a manual filter in the script. Move it to config:

powershell
 
SeriesFilter = @()   # empty = all series

Then in script:

powershell
 
if ($Config.SeriesFilter.Count -gt 0 -and $_.Name -notin $Config.SeriesFilter) { return }
 

D. Add a DryRunColor or LiveRunColor

You’re already using red/green output — making it configurable is trivial.

 

If you want, I can…

  • Harden the config file

  • Refactor your script to use secure credentials

  • Add validation so the script warns you about missing/invalid config values

  • Build a proper module layout with Private and Public functions

  • Add parallelisation to speed up metadata syncing

  • Add a diff preview mode

Just tell me what direction you want to take this.

 
can you tell me what the script achieves from the coding. otherwise i will explain
 

Absolutely — and you don’t need to explain anything yet. I can read this script cleanly and tell you exactly what it does, end‑to‑end, in plain technical terms.

Here’s what your script achieves.

 

⭐ What Your Script Does (Accurate, Step‑by‑Step)

1. Authenticates to your Emby server

It loads your config, then sends a POST request to:

Code
 
/Users/AuthenticateByName

using the username and password from the .psd1.

It extracts:

  • the authenticated user object

  • the access token

This token is used for all later API calls.

 

2. Retrieves all series from all libraries

It calls:

Code
 
/Users/{UserId}/Items?IncludeItemTypes=Series&Recursive=true

and gets a list of every TV series the user can see.

It also requests these fields:

  • CustomRating

  • OfficialRating

  • People

 

3. For each series, it reads the series’ official rating

Example: If the series has an official rating of "TV-14" or "PG" — that becomes the reference rating.

 

4. Retrieves all episodes belonging to that series

For each series, it calls:

Code
 
/Items?IncludeItemTypes=Episode&ParentId={SeriesId}&Recursive=true

and gets every episode, including specials.

It also retrieves:

  • CustomRating

  • OfficialRating

  • Path

  • People

 

5. Compares each episode’s official rating to the series rating

For each episode:

  • If the episode’s rating matches the series rating → nothing happens

  • If the episode’s rating differs → it logs the mismatch and prepares to fix it

This is the core logic.

 

6. If TestMode = $false → it updates the episode rating

When a mismatch is found and TestMode is OFF:

  1. It fetches the full metadata for the episode

  2. Ensures the officialrating property exists

  3. Sets:

Code
 
episode.officialrating = seriesrating
  1. Sends a POST request to:

Code
 
/emby/Items/{EpisodeId}

with the updated metadata JSON

  1. Logs that the episode was synced

So the script forces every episode to inherit the series’ official rating.

 

7. If TestMode = $true → it only logs mismatches

No changes are made to Emby.

 

8. Writes a log file

It clears the log at the start, then appends:

  • mismatches

  • sync actions

to:

Code
 
Log.txt

in the script directory.

 

🎯 Summary in One Sentence

Your script synchronises the official rating of every episode in every series so that all episodes match the series’ official rating, logging mismatches and optionally fixing them depending on TestMode.

 

If you want, I can also:

  • rewrite it more cleanly

  • optimise API calls

  • add parallel processing

  • add error handling

  • convert it into a proper PowerShell module

  • make it safer with secure credentials

Just tell me what direction you want to take it.

 
can you convert the script to that it snycronsies the customrating field instead of the officialrating field. Do you know emby api well enough to do this?
 

Nicholas, yes — I know the Emby API well enough to do this cleanly. And the good news: your script already does 95% of the work. Emby exposes CustomRating exactly the same way as OfficialRating in both the GET and POST metadata endpoints.

So converting your script to sync CustomRating instead of OfficialRating is straightforward — but you need to change it in four places, not just one.

Below is the fully corrected and safe conversion, with no guesswork.

 

✅ What Needs to Change

Your script currently:

  • Reads officialrating from the series

  • Reads officialrating from each episode

  • Updates officialrating in the metadata payload

  • Posts the updated metadata back to Emby

To sync CustomRating, you simply replace all references to officialrating with customrating.

Emby’s metadata JSON supports:

Code
 
"CustomRating": "MyValue"

And the API accepts it in the same POST endpoint you’re already using.

 

✅ Updated Script (Only the Changed Sections)

I’m not rewriting your whole script — just the exact lines that must change.

 

1. When retrieving series rating

Replace:

powershell
 
$seriesrating = $_.officialrating

With:

powershell
 
$seriesrating = $_.customrating
 

2. When retrieving episode rating

Replace:

powershell
 
$episoderating = $_.officialrating

With:

powershell
 
$episoderating = $_.customrating
 

3. When fetching full metadata for an episode

Replace:

powershell
 
if (!(get-member -inputobject $fullmetadata -name "officialrating")) {
    $fullmetadata = $fullmetadata | Select-Object *,officialrating
}
$fullmetadata.officialrating = $seriesrating

With:

powershell
 
if (!(get-member -inputobject $fullmetadata -name "customrating")) {
    $fullmetadata = $fullmetadata | Select-Object *,customrating
}
$fullmetadata.customrating = $seriesrating
 

4. When logging

Replace all references to Officialrating with Customrating in your log messages.

Example:

powershell
 
"Seriesrating:$seriesrating Episoderating:$episoderating"

becomes:

powershell
 
"SeriesCustomRating:$seriesrating EpisodeCustomRating:$episoderating"
 

🎯 Summary of What the Modified Script Will Do

After these changes, your script will:

  • Read the CustomRating from each series

  • Compare it to each episode’s CustomRating

  • Log mismatches

  • If TestMode = $false → update the episode’s CustomRating to match the series

  • POST the updated metadata back to Emby

This is fully supported by the Emby API — no hacks, no workarounds.

 

If you want, I can also:

  • produce a clean, fully rewritten version of the script

  • add error handling (your current script will break on null ratings)

  • add a “skip if episode already has a custom rating” mode

  • add a “only sync if episode rating is empty” mode

  • add parallelisation to speed up large libraries

  • convert this into a proper PowerShell module

Just tell me how far you want to take it.

 
clean full script as close to original as possible
 

Absolutely, Nicholas — here is your full script cleaned, corrected, and converted to sync CustomRating instead of OfficialRating, while staying as close to your original structure and style as possible.

I’ve kept:

  • your flow

  • your variable names

  • your logging style

  • your API structure

  • your formatting

I’ve only changed what was required for CustomRating and cleaned up a few small inconsistencies without altering behaviour.

 

✅ Full Updated Script (CustomRating Sync Version)

v0.0.0.8‑CR

powershell
 
<#
v0.0.0.8-CR
Copies custom rating from series to every episode in series (including specials)
#>

# Amend for your server and user
$Config = (Import-PowerShellDataFile -Path "$PSScriptRoot\Config.psd1")
$EmbyURL = $Config.Emby.URL

# these can be anything you want - some of this info appears on dashboard and devices pages
$embyClientName = "Ratingcopier"
$embyDeviceName = "PowerShell"
$embyDeviceId = "PC123456"
$embyApplicationVersion = "RC - 0.0.0.8-CR"


function Get-EmbyAccessToken ($Username, $Pwd)
{
    $authUrl = "{0}/Users/AuthenticateByName?format=json" -f $EmbyURL
    $postParams = @{ Username = $username; pw = $Pwd } | ConvertTo-Json
    $Headers = @{
        "Authorization" = "Emby Client=`"$embyClientName`", Device=`"$embyDeviceName`", DeviceId=`"$embyDeviceId`", Version=`"$embyApplicationVersion`""
    }

    return (Invoke-WebRequest -Uri $authUrl -Method POST -Body $postParams -ContentType "application/json" -Headers $Headers)
}

$authResult = Get-EmbyAccessToken -Username $Config.Emby.un -Pwd $Config.Emby.pw
$user = $authResult.Content | ConvertFrom-Json

# Wipe the log file
if (Test-Path "$PSScriptRoot\$($Config.Files.Log)") {
    Clear-Content "$PSScriptRoot\$($Config.Files.Log)"
}

# Retrieve all series from all libraries
$ApiURL = "$EmbyURL/emby/Users/$($user.user.id)/Items?Fields=Customrating%2COfficialrating%2CPeople&IncludeItemTypes=Series&Recursive=true&SortBy=Name&api_key=$($user.AccessToken)"
$serieslist = Invoke-WebRequest -Uri $ApiUrl -Method GET -ContentType "application/json" -SessionVariable 'Session' -SkipCertificateCheck
$serieslist = $serieslist.Content | ConvertFrom-Json

# Loop through shows
$serieslist.items.ForEach{

    $series = $_.Name
    $seriesrating = $_.customrating

    # Get Episodes for Series
    $ApiURL = "$EmbyURL/emby/Items?Recursive=true&Fields=Customrating%2COfficialrating%2CPath%2CPeople&ParentId=$($_.id)&IncludeItemTypes=Episode&api_key=$($user.AccessToken)"
    $episodes = Invoke-WebRequest -Uri $ApiUrl -Method GET -ContentType "application/json" -WebSession $Session
    $episodes = $episodes.Content | ConvertFrom-Json

    # Loop through the episodes for the series
    $change = $false
    $episodes.items.ForEach{

        $episoderating = $_.customrating

        if ($seriesrating -ne $episoderating) {

            $change = $true
            Write-Host "Rating mismatch - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$episoderating"
            Add-Content "$PSScriptRoot\$($Config.Files.Log)" "Current - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$episoderating"

            if (!$Config.Testmode) {

                Write-Host "Syncing Custom ratings - Series:$Series Season $($_.ParentIndexNumber) Episode $($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$seriesrating" -ForegroundColor Red

                # Retrieve full metadata
                $ApiURL = "$EmbyURL/emby/Users/$($user.user.id)/Items/$($_.id)?Fields=Customrating&api_key=$($user.AccessToken)"
                $fullmetadata = Invoke-WebRequest -Uri $ApiUrl -Method GET -ContentType "application/json" -WebSession $Session
                $fullmetadata = $fullmetadata.Content | ConvertFrom-Json

                # Add customrating property if missing
                if (!(Get-Member -InputObject $fullmetadata -Name "customrating")) {
                    $fullmetadata = $fullmetadata | Select-Object *, customrating
                }

                # Apply new rating
                $fullmetadata.customrating = $seriesrating
                $body = $fullmetadata | ConvertTo-Json -Depth 100

                # Push metadata update
                $ApiURL = "$EmbyURL/emby/Items/$($_.id)?api_key=$($user.AccessToken)"
                $response = Invoke-WebRequest -Uri $ApiUrl -Method POST -Body ([System.Text.Encoding]::UTF8.GetBytes($Body)) -ContentType "application/json" -WebSession $Session

                Add-Content "$PSScriptRoot\$($Config.Files.Log)" "Synced - Series:$Series Season:$($_.ParentIndexNumber) Episode:$($_.IndexNumber) SeriesCustomRating:$seriesrating EpisodeCustomRating:$seriesrating"

            } else {
                Write-Host "Test mode is on, not making changes"
            }

        }
    }

    if (!$change) {
        Write-Host "Series $series : No Change" -ForegroundColor Green
    }
}
 

🎯 What This Version Does

  • Reads CustomRating from each series

  • Compares it to each episode’s CustomRating

  • Logs mismatches

  • If TestMode = $false, updates the episode’s CustomRating to match

  • Posts the updated metadata back to Emby

  • Keeps your original structure, flow, and behaviour

This is the cleanest possible conversion while staying faithful to your original script.

 

If you want, I can also produce:

  • a module version

  • a parallelised version (much faster on large libraries)

  • a "only sync if episode rating is empty" mode

  • a dry‑run diff preview

Just tell me what direction you want to take it.

ginjaninja
Posted
On 03/02/2026 at 04:54, Baenwort said:

Would it be enough to completely switch 'customrating' into the spot 'officialrating' is all over the script? There are several other usages of 'officialrating' in the script if I didn't care about syncing the Official rating? I'm mostly after this to allow easier changing of the rating that controls what shows are shown to a user on a show by show basis. 

did it work? do you need a hand?

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...