Glitch project bulk-downloading

How to bulk-download all your pojects

With the project hosting shutdown in July, many of you have asked for a way to bulk-download your projects: this thread is for you.

We’ve created a python script that you can run that will download all your projects, and also download all your assets from the asset manager into a glitch-assets folder for each project.


How long do I have?!

Project hosting will shut down in July, but you will continue to be able to download your projects for the entirety of 2025. So you have some time yet.

Where to get it

Download the script here: https://raw.githubusercontent.com/Pomax/glitch-bulk-downloader/refs/heads/main/download.py

(the script itself lives in the https://github.com/Pomax/glitch-bulk-downloader GitHub repository)

What do I need to run this?

1. You need Python 3.x

You will need Python 3 installed. If you’re on linux or macos, this will almost certainly already be the case, and you should be able to use python3 without installing anything. On Windows, you may need to go to the Windows Store, find Python, and install it first. This will add the python command (note that on Windows there is no “3” at the end). Alternatively, you can visit Python Releases for Windows | Python.org and download the installer for the current version of Python 3 that’s appropriate to your Windows version.

2. You will also want tar

In order to unpack your projects and put them in properly named subfolders, you will need the tar command to be available. If you’re on Linux or MacOS, you’re (again) already good to go, but if you’re on Windows you will need to “install” tar.

installing tar on Windows

If you use git, the easiest way to install tar is to just run the windows git installer, and tell it to install the GNU tools. After that finishes you should be able to confirm tar is available by opening a command prompt or powershell and just running tar --help.

If you don’t, you can also exclusively install tar by heading over to Tar for Windows and clicking on the binary setup link. This will take you to a Sourceforge (the place to be for open source before GitHub showed up) download page, which should automatically download the setup program.

Run the setup, and then you’ll need to update your windows PATH to include the C:\Program Files (x86)\GnuWin32\bin folder: you can either do this persistently (instructions are easily found on the web for that), or you can run set PATH=%PATH%;"C:\Program Files (x86)\GnuWin32\bin" in your command prompt or powershell before you run the download script.


How to use the script

The script will download your regular projects into an ./active folder, and will download all your archived projects into an ./archived folder. It can do this in two “modes”:

Interactive mode

You can invoke the script by calling:

python3 download.py

(or python download.py on Windows)

Run in this way, the script will ask you to provide your Glitch user id and token. Both of these can be found by going to glitch.com, opening your dev tools, and typing:

const { id, persistentToken: token} = JSON.parse(localStorage.cachedUser);
console.log(id, token);

This will log something along the lines of 123456 '3a6f55bd-8206-43d3-a149-176f13f32c45', where the first number is your id, and the second string is your token.

Automatic mode

You can also run the script with those values directly, e.g.

python3 download.py 123456 '3a6f55bd-8206-43d3-a149-176f13f32c45'

In which case the script won’t ask for anything and will just get to downloading.

Optional runtime flags

The script supports two runtime flags, with flags needing to come after your user id and token:

  1. --no_assets, which will make the script download your project, but not download the associated assets, and
  2. --no-skip, which will make the script re-download any projects you already downloaded if you run it more than once.

Replacing CDN URLs in your source code

Asset URLs are not automatically replaced in any source code, mostly because that’s much harder than you’d think it’d be, so you will still need to replace CDN URLs in your code with relative links to the ./glitch-assets folder that all your project’s assets were downloaded into.


Known issues

While this script will download your projects and their associated assets, some assets may be on a very old CDN that currently has an expired certificate, so Python will refuse to download those assets out of security concerns (note that if your project’s 8 years old, that might affect you. If not, it probably won’t).

We’re looking into updating that certificate, but if enough folks run into this we can also add a flag to the download script to bypass security for those assets for folks who think that’s an acceptable risk.

Questions or comments

If you have any questions about this script, or suggestions on how to improve it, feel free to leave a reply here in the thread, or file an issue over on GitHub · Where software is built

10 Likes

I’ve got a project that is broken in the main UI and causing the bulk download script to terminate early…

Could someone possibly nuke or fix this project so that I can keep archiving?

1 Like

The editor state has nothing to do with this script, it simply automates the process of physically clicking the “download” button on your dashboard by calling the same URL with the same auth information. If your project is broken in the editor, please write in to support@glitch.com

However, if you get a specific error while running the script, put it in your post and I can see if that’s something that “makes sense” and something I can write some defensive code for.

1 Like

Here’s the tail of where it stalls out:

[...snip...]

Downloading 'flower-tasty-doom'...
Traceback (most recent call last):
  File "D:\glitch_rescue\download.py", line 158, in <module>
    download_project(user_token, project)
  File "D:\glitch_rescue\download.py", line 84, in download_project
    result = urlretrieve(url, file)
             ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 241, in urlretrieve
    with contextlib.closing(urlopen(url, data)) as fp:
                            ^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 216, in urlopen
    return opener.open(url, data, timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 525, in open
    response = meth(req, response)
               ^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 634, in http_response
    response = self.parent.error(
               ^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 563, in error
    return self._call_chain(*args)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 496, in _call_chain
    result = func(*args)
             ^^^^^^^^^^^
  File "C:\Python311\Lib\urllib\request.py", line 643, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 500: Internal Server Error

The run terminated like that a couple times.. I then tried adding an empty folder by that name in the hopes it would skip trying to retrieve that project, but it didn’t help.

I’d be happy with just deleting it, but theres no delete facility, and it won’t load in the editor.
I’ll hold off on re-running it because I don’t want to hammer the servers too much.. but perhaps checking for “directory exists” before re-downloading could be an option?

1 Like

(removing this for OP’s preference :+1:)

1 Like

@manthrax Cheers, I’ve added a try/catch with a console warning that the downloader was unable to get that specific project, and skip over it rather than erroring out.

2 Likes

Hi, I’m having some troubles with the bulk downloader hitting an error and quitting. Would be great to have it move on to the next project rather than quitting.

Additional info:
I tried rerunning the script and this time it got further along but again quit with the same error.

Thanks for any help.

Most recent:

Unpacking...
Traceback (most recent call last):
  File "/usr/lib/python3.13/shutil.py", line 856, in move
    os.rename(src, real_dst)
    ~~~~~~~~~^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: './app' -> './discord-botkit'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/void/Sites/glitch-download/download.py", line 92, in download_project
    shutil.move("./app", f"./{project_title}")
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/shutil.py", line 876, in move
    copy_function(src, real_dst)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/shutil.py", line 468, in copy2
    copyfile(src, dst, follow_symlinks=follow_symlinks)
    ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/shutil.py", line 260, in copyfile
    with open(src, 'rb') as fsrc:
         ~~~~^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: './app'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/void/Sites/glitch-download/download.py", line 161, in <module>
    download_project(user_token, project)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
  File "/home/void/Sites/glitch-download/download.py", line 96, in download_project
    except HTTPError as e:
           ^^^^^^^^^
NameError: name 'HTTPError' is not defined. Did you mean: 'TabError'?

@Lee I’ve updated the script to wait for the “./app” dir that the untar should create before moving on, let me know if that fixes things for you.

That’s fantastic! Thank you.. I’ll give it a shot!

1 Like

Unfortunately I’m still getting an error, and then it quits.

Unpacking...
Traceback (most recent call last):
  File "/home/void/Sites/glitch-download/download.py", line 177, in <module>
    download_project(user_token, project)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
  File "/home/void/Sites/glitch-download/download.py", line 104, in download_project
    wait_for_dir(unpacked_dir)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/void/Sites/glitch-download/download.py", line 84, in wait_for_dir
    time.sleep(poll_interval)
    ^^^^^^^^^^
AttributeError: 'builtin_function_or_method' object has no attribute 'sleep'

Okay, no, that’s just me being really dumb. I’ve updated the time imports and that should no longer happen =_=

I’ll see if I can find a bit more time to make sure the script skips already downloaded projects, unless instructed not to with a --noskip flag, because for most folks redownloading 5~10 projects isn’t a problem, but 250+ can get real annoying real fast.

1 Like

Added the --no-skip flag, so that the script skips projects you already downloaded unless you explicitly add that flag.

2 Likes

Nice. The script still chokes on Internal Server Error 500, but then I can just make an empty folder of the broken projects name and restart the script.

Currently 150 downloads in and still going.. have had to manually create 2 folders so far to get them skipped.

:smiley:

Hmm, got an error log anyway? Always nicer to catch those 500’s in the right place.

The script worked for me this time. THank you. I had 251 projects and 237 were downloaded succesfully.

Now…anyone have a bash script to bulk rewrite asset urls to glitch-assets/filename ? :slight_smile:

2 Likes

Worked for me.. all 350 projects downloaded. :slight_smile: Only had to map out 2 bad ones.

1 Like

The reason I didn’t bake that in is because “I have no idea which file types you need” - the proper way to do it in HTML is to DOM parse the whole thing, then querySelect for href and src that start with the CDN prefix, and replace those, but for JS/TS it’s an AST parse and then updating the fetch or XMLHttpRequest argument, as well as updating href and src attributes for anything that’s an HTMLElement instance, for Python it’s… ehhh you get the idea. It gets way too tricky to get right reliably :wink:

But: I’d recommend opening VS Code on your “top level” dir with all the projects in it and doing a find and replace there if you just want “I have too many projects, just do the thing” =P

Ah hah. Totally makes sense that it’s a bit more complicated for JavaScript.

I’m thinking of the HTML usecase for images: jpeg, jpg, png, gif, webp and audio files mp3, wav, ogg.

I was thinking the main need is to replace in the <img src=" from glitch.com/randomnumbers-filename.png?randomstring to glitch-assets/filename.png for example.

I may need to do some regex work I guess.

Yeah, for the most part, you should be able to replace https://cdn.glitch.global/a-uuid-v4-string-here/your.filename.dot.extension?v=a-number with ./glitch-assets/your.filename.dot.extension and have that work.

2 Likes