Downloading My GOG Library the Easy Way

With all of the recent controversy about games getting censored for content, not necessarily against the law, but that other private companies have decided is “questionable”, I decided it might be a good idea to backup all of my Good Old Games (GOG) titles. Especially since I now have my new 10TB drive to dump them on. Because of course, GOG sells it’s games without DRM.

I started to do this manually, but it’s incredibly tedious. Surely someone has a script, and sure enough, they have (Link). It works well enough. It’s a single file, which is nice.

It needed a few things to get going though. Firstly, a Python Library.

pip install html5lib

You then have to log in, which surprisingly, didn’t ask for any 2 factor credentials, even though normally it does. That’s a bit worrying.

python.exe .\gogrepoc.py login

The next step is to build a little local database-ish file by letting it scan your library.

python.exe .\gogrepoc.py update

Finally it’s ready to download. But here is where it gets a bit fun. I have, just under 400 games on GOG. I know it’s going to take a long time to download everything, and potentially it’s going to eat up a lot of bandwidth, which screws up my gaming, and it’s likely going to get interrupted due to a myriad of potential reasons.

You can however, download games based on IDs, individually. For example, this will download Fallout 1 Classic.

python.exe .\gogrepoc.py download -id 1

I just, randomly tried ID 1 and it worked. However, the IDs are not sequential at all, nor are they all single digits. Maybe have multiple digits. Thankfully, the database-ish thing we built earlier, gog-manifest.dat, is human readable, it’s just an XML file. Each title has a lot of data in the file, but what we want is the first line for each game, “{‘_id_mirror’: 1,”.

I took this file, threw it at Claude.ai and asked it for a script that will strip out all the IDs and put them in a simple text file list, one on each line. Which is did.

The code for that file is here:

#!/usr/bin/env python3
"""
Script to extract _id_mirror values from a JSON-style file containing GOG game data.
Usage: python extract_gog_ids.py <input_filename>
Output: Creates gog_ids.txt with one ID per line
"""

import json
import sys
import ast

def extract_gog_ids(input_filename):
    """
    Extract _id_mirror values from the input file and save to gog_ids.txt

    Args:
        input_filename (str): Path to the input JSON-style file
    """
    try:
        with open(input_filename, 'r', encoding='utf-8') as file:
            content = file.read().strip()

        # Try to parse as JSON first
        try:
            data = json.loads(content)
        except json.JSONDecodeError:
            # If JSON parsing fails, try to evaluate as Python literal
            # This handles cases where the file might be Python dict/list format
            try:
                data = ast.literal_eval(content)
            except (ValueError, SyntaxError) as e:
                print(f"Error: Could not parse the file as JSON or Python literal: {e}")
                return False

        # Extract _id_mirror values
        id_mirrors = []

        if isinstance(data, list):
            # Data is a list of dictionaries
            for item in data:
                if isinstance(item, dict) and '_id_mirror' in item:
                    id_mirrors.append(str(item['_id_mirror']))
        elif isinstance(data, dict) and '_id_mirror' in data:
            # Data is a single dictionary
            id_mirrors.append(str(data['_id_mirror']))
        else:
            print("Error: Input data format not recognized. Expected list of dicts or single dict with '_id_mirror' key.")
            return False

        # Write to output file
        with open('gog_ids.txt', 'w', encoding='utf-8') as output_file:
            for id_mirror in id_mirrors:
                output_file.write(f"{id_mirror}\n")

        print(f"Successfully extracted {len(id_mirrors)} IDs to gog_ids.txt")
        return True

    except FileNotFoundError:
        print(f"Error: File '{input_filename}' not found.")
        return False
    except Exception as e:
        print(f"Error processing file: {e}")
        return False

def main():
    """Main function to handle command line arguments"""
    if len(sys.argv) != 2:
        print("Usage: python extract_gog_ids.py <input_filename>")
        print("Example: python extract_gog_ids.py paste.txt")
        sys.exit(1)

    input_filename = sys.argv[1]
    success = extract_gog_ids(input_filename)

    if not success:
        sys.exit(1)

if __name__ == "__main__":
    main()

So what I have now is an easy to use list of IDs.

Then, in a new chat, I asked Claude.ai to modify my Powershell command to run through the list, one at a time, running the download command above. BUT I also stipulated that it let me specify how many to download, this way I can do them in blocks of however many I want. This command will download the top ten IDs.

Get-Content gog_ids.txt | Select-Object -First 10 | ForEach-Object { python.exe .\gogrepoc.py download -id $_ }

Now, I could get fancier, but this works pretty well for my needs. Once it finishes, I can just, manually edit the IDs file and chop the top then IDs off.

I also found that the bandwidth needs must not be too huge because it didn’t add any additional lag while playing games while it was running.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.