Skip to content

Instantly share code, notes, and snippets.

@bburky
Last active February 19, 2024 13:28
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bburky/38c29a5fd8fbf2fc72ef77d553d09c6a to your computer and use it in GitHub Desktop.
Save bburky/38c29a5fd8fbf2fc72ef77d553d09c6a to your computer and use it in GitHub Desktop.

Is There Any Deal Collection Sync Playnite Extension

Installation

Download extension .pext from the forum post

Usage

  1. Select games to add to your Is There Any Deal collection.
  2. Right-click the games and select "Add Games to Is There Any Deal Collection" in the menu.
  3. Log into Is There Any Deal if it prompts you to. Close the window and rerun the extension after logging in.
  4. Review the list of games and scroll to the bottom. Check "Remove from Waitlist" if desired. Click "Import (safe option)".
  5. Close the window after importing.

Games are only imported to Is There Any Deal by name, it is possible it could guess the wrong game.

Note, while playtime and completion status can be imported to Is There Any Deal, it does not always seem to update. You can try using "Replace" while importing or deleting games from your collection to force it to update.

This extension does not support importing your list of games from Is There Any Deal into Playnite.

Id: bburky-IsThereAnyDeal
Name: Is There Any Deal
Author: Blake Burkhart
Version: 1.4
Module: IsThereAnyDeal.psm1
Type: Script
Links:
- Name: Forum
Url: https://playnite.link/forum/thread-270.html
- Name: Source Code
Url: https://gist.github.com/bburky/38c29a5fd8fbf2fc72ef77d553d09c6a
function ConvertGamesToITAD ($allGames) {
foreach ($group in $allGames | Group-Object -Property Name) {
$games = $group.Group
$playtime = ($games.Playtime | Sort-Object)[-1]
$status = ($games.CompletionStatus | Sort-Object)[-1]
@{
title = $games[0].Name
status = ([string]$status).ToLower()
playtime = $playtime / 60
copies = @(foreach ($game in $games) {
@{
type = switch ($game.Source) {
"Battle.net" { "battlenet" }
"itch.io" { "itchio" }
{ !$_ } { "playnite" }
Default { $_.Name.ToLower() }
}
owned = 1
}
})
}
}
}
function ImportGamesInITAD ($games) {
$data = @{
version = "02"
data = @(ConvertGamesToITAD $games)
} | ConvertTo-Json -Depth 5
$b64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($data))
$html = "<!DOCTYPE html>
<body onload='form.submit()'>
<form id='form' action='https://isthereanydeal.com/collection/import/' method='post'>
<input type='hidden' name='file' value='$b64'>
<input type='hidden' name='upload' value='Import ITAD Collection'>
</form>
</body>"
$webView = $PlayniteApi.WebViews.CreateView(1000, 800)
foreach ($cookie in $webView.GetCookies()) {
if ($cookie.Domain -match "\.?isthereanydeal\.com" -and $cookie.Name -eq "user") {
# Chrome 80+ now enforces SameSite cookies which breaks this ITAD API
# HACK: Abuse Chrome's 2 minute timer from its "Lax + POST mitigation" https://www.chromium.org/updates/same-site/faq
# Delete and recreate the "user" cookie to reset its creation date
$webView.DeleteCookies("https://isthereanydeal.com/", "user")
$webView.SetCookies("https://isthereanydeal.com/", $cookie.Domain, $cookie.Name, $cookie.Value, $cookie.Path, $cookie.Expires)
}
}
$webView.Navigate("data:text/html," + $html)
$webView.OpenDialog()
$webView.Dispose()
}
function IsThereAnyDeal {
param($scriptGameMenuItemActionArgs)
ImportGamesInITAD $scriptGameMenuItemActionArgs.Games
}
function GetGameMenuItems()
{
param($getGameMenuItemsArgs)
$menuItem = New-Object Playnite.SDK.Plugins.ScriptGameMenuItem
$menuItem.Description = "Add to Is There Any Deal Collection"
$menuItem.FunctionName = "IsThereAnyDeal"
return $menuItem
}
@bburky
Copy link
Author

bburky commented Dec 27, 2020

Update 1.3

  • Update for Playnite 8, add Id to manifest and repackage
  • Work around for Chrome 80+ SameSite enforcement breaking IsThereAnyDeal's API

The IsThereAnyDeal collection import API this script uses is a cross site form POST. Chrome 80+ (and Chromium Embedded Framework which Playnite uses for it's browser) now enforces a stricter policy for cross site cookies:

Cookies that do not specify a SameSite attribute will be treated as if they specified SameSite=Lax, i.e. they will be restricted to first-party or same-site contexts by default.

By carefully abusing the Lax + POST mitigation, "a cookie that is at most 2 minutes old will be sent on a top-level cross-site POST request", I can get the old behavior back and make the ITAD API still work.

@bburky
Copy link
Author

bburky commented Sep 29, 2021

Update 1.4

  • Update for Playnite 9
  • Move menu option to right click game menu

@bburky
Copy link
Author

bburky commented Sep 29, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment