Thursday, June 18, 2026

How to Automatically Download, Organize, and Sync All Your Canvas Course Files to Google Drive (Without Losing Your Mind)

How to Automatically Download, Organise, and Sync All Your Canvas Course Files to Google Drive (Without Losing Your Mind)

Navigating Canvas is a deadweight time tax that students hate. When you are juggling intense coursework alongside executive and professional commitments, wasting cognitive bandwidth hunting through nested web subfolders for a reading from three weeks ago is pure friction. You need your academic library offline, cleanly indexed, and instantly searchable.

Below is the blueprint for a localised file engine that completely automates this process. With a single terminal command, it queries Canvas, extracts your materials, converts cluttered presentation slides into clean PDFs, and streams organised folders straight to your secure Google Drive cloud backup. Note that files locked behind library paywalls or external university login gates cannot be scraped directly, but the system automatically flags these in a custom checklist for quick manual download.

Setting up this pipeline requires no coding experience—just about 20 minutes of initial configuration and the ability to copy and paste text into a terminal window.


What This System Does For You

  • Smart Incremental Downloading: It queries Canvas for changes, skipping files you already have and only downloading brand-new materials your lecturers just uploaded.
  • Automatic PDF Generation: Before touching the cloud, it sweeps your local folders, finds every Word document and PowerPoint slide deck, and builds an exact PDF mirror right beside the source file.
  • Throttled, Non-Destructive Cloud Archiving: It mirrors your local directories to Google Drive using calibrated API rate-limiting. It copies without deleting older files, so clearing local storage never harms your cloud backup.
  • Human-Readable Course Mapping: Canvas organizes storage using cryptic numerical IDs. This pipeline maps those directories to clean, human-readable subject names (e.g., Business Strategy or Corporate Governance).
  • The "To-Do" Fallback Checklist: If a lecturer links to a locked library database, external paywall, or authenticated proxy, the script extracts those links into a clean audit file (login_links.txt) so you can grab them manually in seconds.

Step 1: Set Up Your Computer's Free Toolkit

We need to install three free tools: Python (to communicate with Canvas), LibreOffice (to handle headless background PDF conversions), and Rclone (to securely sync with Google Drive). Find your operating system below and copy-paste the commands into your terminal window.

🌐 For Chromebook (ChromeOS) & Modern Linux Users

Modern Linux and ChromeOS environments use Debian, which safely locks down Python environments to protect the system. We use an isolated app manager called pipx to bypass this barrier natively. Open your Terminal app, select Penguin, and paste this block:

sudo apt update
sudo apt install -y python3 python3-pip libreoffice rclone pipx
pipx install canvas-downloader
pipx ensurepath
source ~/.bashrc

🪟 For Windows Users

Windows handles file paths and Linux shell scripts poorly on its own, so we use the built-in Windows Subsystem for Linux (WSL) to run this smoothly.

  1. Open your Windows Start Menu, type PowerShell, right-click it, and choose Run as Administrator.
  2. Type this command and hit Enter: wsl --install
  3. Restart your computer.
  4. Open the newly installed Ubuntu terminal from your Windows Start menu. Follow the quick prompt to create a simple username and password.
  5. Inside that new terminal window, paste the setup commands:
sudo apt update
sudo apt install -y python3 python3-pip libreoffice rclone pipx
pipx install canvas-downloader
pipx ensurepath
source ~/.bashrc

🍏 For Mac Users

  1. Open your Terminal app. (If you don't have the Homebrew tool manager installed, get it first from brew.sh).
  2. Run this block to install the tools, utilizing pipx to keep your environment pristine:
brew install rclone python pipx
pipx install canvas-downloader
pipx ensurepath
brew install --cask libreoffice
sudo ln -s /Applications/LibreOffice.app/Contents/MacOS/soffice /usr/local/bin/libreoffice
source ~/.zshrc

📱 For iOS Users (iPhone & iPad)

The Mobile Reading Strategy: Due to iOS background sandbox constraints, you cannot run background sync automation directly on an iPhone or iPad.

Instead, run this automation script on your laptop or workstation. Because the engine automatically compiles and mirrors all slides into PDFs on Google Drive, your iPad or iPhone becomes the ideal reading terminal. Simply open the native Google Drive app, mark your subject folders as "Available Offline", and review your readings seamlessly anywhere.

Step 2: Link Your Google Drive Securely

We use Rclone to talk directly to your Google account. It relies on standard OAuth web authorization, meaning it never handles or stores your plain-text password.

  1. In your terminal window, type:
    rclone config
  2. Type n and hit Enter to create a new remote.
  3. Enter a simple identifier for your drive profile (e.g., gdrive) and press Enter. Remember this name; you will reference it in Step 4.
  4. In the storage type prompt, type drive and hit Enter.
  5. Leave client_id and client_secret blank by pressing Enter twice.
  6. Select full access scope (usually option 1).
  7. Press Enter through the next default configuration prompts.
  8. When prompted with Use auto-config?, type y. A browser tab will open automatically. Grant permissions to your Google account and close the tab when authenticated.
  9. Confirm n for Shared Drives (unless your university folder is a Team Drive), verify settings with y, and press q to exit the wizard.

Step 3: Generate Your Canvas API Access Token

To authorize file queries, the script requires an automated personal access token from your Canvas account.

  1. Log into your university Canvas portal in your browser.
  2. Click on Account (profile icon) in the global navigation bar, then select Settings.
  3. Scroll down to Approved Integrations and click + New Access Token.
  4. Provide a label (such as "Coursework Sync Pipeline") and select Generate Token.
  5. Copy the alphanumeric token string immediately.
⚠️ Security Advisory: Treat your API token like an active password. Never commit it to public repositories or paste it into shared communication channels.

Step 4: Create Your Hidden Environment Config

We store your credentials and remote endpoints in a private configuration file outside the execution repository to keep secrets secure.

  1. Open your terminal and create the configuration file:
    nano ~/.canvas_sync.conf
  2. Paste the template below, replacing the placeholder values with your real Canvas token, your institution's Canvas URL, and your Rclone remote name:
# Secure Canvas Configuration File
TOKEN="YOUR_CANVAS_API_TOKEN"
CANVAS_URL="https://youruniversity.instructure.com"
GDRIVE_PATH="gdrive:University/Coursework"
  1. Press Ctrl + O, hit Enter to write the file, and press Ctrl + X to exit nano.
  2. Lock file access permissions so only your local user account can read it:
    chmod 600 ~/.canvas_sync.conf

Step 5: Deploy the Hardened Sync Engine

This upgraded version fixes common pitfalls seen in large student environments: it converts documents before initiating cloud uploads, handles complex file paths and slide deck formats cleanly, and throttles network transactions so Google Drive doesn't throttle or rate-limit your account.

  1. Create and enter your local workspace directory:
    mkdir -p ~/Downloads/Coursework && cd ~/Downloads/Coursework
  2. Open the script editor:
    nano canvas_sync.sh
  3. Paste the complete, production-ready script below:
#!/bin/bash

# Ensure script executes out of the current directory context
cd "$(dirname "$0")" || exit 1

# Establish execution baseline timestamp for reporting
touch .sync_start

# =====================================================================
# SOURCE CONFIGURATIONS
# =====================================================================
CONFIG_FILE="$HOME/.canvas_sync.conf"
if [ -f "$CONFIG_FILE" ]; then
    . "$CONFIG_FILE"
else
    echo "[!] CRITICAL ERROR: Configuration file not found at $CONFIG_FILE"
    echo "    Please create it with secure permissions before running this script."
    exit 1
fi

# Dynamically locate the downloader binary across environments
if command -v canvas-downloader &> /dev/null; then
    DOWNLOADER_BIN=$(command -v canvas-downloader)
else
    DOWNLOADER_BIN="$HOME/.local/bin/canvas-downloader"
fi

CHECKLIST_FILE="login_links.txt"
RAW_LOG=".raw_sync_log.txt"
CONVERSION_ERRORS="conversion_failures.txt"

# Clear temporary execution artifacts
rm -f "$RAW_LOG" "$CHECKLIST_FILE" "$CONVERSION_ERRORS"

echo "========================================="
echo " STEP 0: Verifying Canvas API Access"
echo "========================================="
if ! python3 -c "import requests; r=requests.get('$CANVAS_URL/api/v1/users/self', headers={'Authorization': 'Bearer $TOKEN'}); assert r.status_code == 200" 2>/dev/null; then
    echo "[!] ERROR: Canvas API token is invalid, expired, or URL is unreachable."
    echo "    Please verify credentials in $CONFIG_FILE."
    exit 1
fi
echo "[+] API token authentication verified."

echo "========================================="
echo " STEP 1: Streaming Live Course Data Sync"
echo "========================================="

# Fetch enrolled subjects and stream modules cleanly
while IFS=":::" read -r course_id course_name; do
    [ -z "$course_id" ] && continue
    
    # Clean non-standard filesystem characters
    clean_name=$(echo "$course_name" | tr -d '[:cntrl:]' | sed 's/[/\\*?:"<>|]/_/g' | sed 's/[[:space:]]*$//')
    
    echo "-> Processing: $clean_name ($course_id)"
    
    # 1. Download course files, pages, and module contents
    "$DOWNLOADER_BIN" --canvas-url "$CANVAS_URL" --api-token "$TOKEN" --output-dir "." --include-assignments --course-id "$course_id" 2>&1 | tee -a "$RAW_LOG"
    
    # 2. Symlink numbered course folder to readable subject title
    if [ -d "$course_id" ]; then
        ln -sfn "$course_id" "$clean_name"
    fi
    
    sleep 1

done < <(python3 -c "
import requests
try:
    r = requests.get('$CANVAS_URL/api/v1/courses?per_page=100', headers={'Authorization': 'Bearer $TOKEN'}).json()
    for c in r:
        if isinstance(c, dict) and 'id' in c and 'name' in c:
            print(f\"{c['id']}:::{c['name']}\")
except Exception:
    pass
")

echo "========================================="
echo " STEP 2: Running Smart PDF Conversion"
echo "========================================="
# Clean hung background locks
killall -9 soffice.bin 2>/dev/null
find . -type f -name ".~lock.*#" -delete 2>/dev/null

find . -type f \( -name "*.docx" -o -name "*.pptx" -o -name "*.doc" -o -name "*.ppt" \) -print0 | while IFS= read -r -d '' file; do
    pdf_file="${file%.*}.pdf"
    
    if [ -f "$pdf_file" ]; then
        continue
    else
        echo "Converting new document: $file"
        
        abs_file="$(readlink -f "$file")"
        abs_dir="$(dirname "$abs_file")"
        
        # Execute headless conversion using absolute paths and per-process virtual profiles
        timeout 45s libreoffice "-env:UserInstallation=file:///tmp/lo_convert_$$" --headless --convert-to pdf --outdir "$abs_dir" "$abs_file" < /dev/null 2>&1
        
        status=$?
        if [ $status -eq 124 ]; then
            echo " [!] WARNING: Conversion timed out on $file (likely corrupt). Skipping."
            echo "$file" >> "$CONVERSION_ERRORS"
        elif [ ! -f "$pdf_file" ]; then
            echo " [!] WARNING: Conversion failed on $file."
            echo "$file" >> "$CONVERSION_ERRORS"
        fi
        rm -f "$abs_dir/.~lock.$(basename "$abs_file")#" 2>/dev/null
    fi
done
rm -rf /tmp/lo_convert_* 2>/dev/null

echo "========================================="
echo " STEP 3: Throttled Google Drive Cloud Sync"
echo "========================================="
SYNC_ERROR=0

# Sync converted directory trees to Google Drive with API rate throttling
find . -maxdepth 1 -mindepth 1 -type d ! -name ".*" | while IFS= read -r dir; do
    folder_name="$(basename "$dir")"
    
    echo "-> Uploading to Drive: $folder_name"
    
    rclone copy "$dir" "$GDRIVE_PATH/$folder_name" \
        --tpslimit 8 \
        --checkers 4 \
        --transfers 2 \
        --fast-list \
        --low-level-retries 10 \
        --retries 3 \
        --exclude ".*" \
        --exclude "*.tmp" \
        --quiet
        
    if [ $? -ne 0 ]; then
        echo "    [!] Cloud sync warning encountered on: $folder_name"
        SYNC_ERROR=1
    fi
done

echo "========================================="
echo " STEP 4 AUDIT: Building Login Checklist"
echo "========================================="
if [ -f "$RAW_LOG" ]; then
    grep -E "Failed|403|401|Forbidden|Unauthorized" "$RAW_LOG" > "$CHECKLIST_FILE"
fi

if [ -s "$CHECKLIST_FILE" ]; then
    echo "[!] Attention needed: External or locked links flagged."
    echo "    Check '$(pwd)/$CHECKLIST_FILE' for items requiring manual browser actions."
else
    rm -f "$CHECKLIST_FILE" 2>/dev/null
fi

echo "========================================="
echo " SUMMARY REPORT"
echo "========================================="
NEW_FILES=0
if [ -f "$RAW_LOG" ]; then
    NEW_FILES=$(grep -cE "Downloaded:|██████████" "$RAW_LOG" 2>/dev/null || echo "0")
fi
MANUAL_LINKS=0
if [ -f "$CHECKLIST_FILE" ]; then
    MANUAL_LINKS=$(wc -l < "$CHECKLIST_FILE" 2>/dev/null || echo "0")
fi
PDF_COUNT=$(find . -name "*.pdf" -newer .sync_start 2>/dev/null | wc -l || echo "0")
FAILED_CONVERSIONS=0
if [ -f "$CONVERSION_ERRORS" ]; then
    FAILED_CONVERSIONS=$(wc -l < "$CONVERSION_ERRORS" 2>/dev/null || echo "0")
fi

echo " New files downloaded from Canvas : $NEW_FILES"
echo " Office documents built to PDF    : $PDF_COUNT"
echo " Failed document PDF conversions : $FAILED_CONVERSIONS"
echo " Flagged items requiring login   : $MANUAL_LINKS"
echo "-----------------------------------------"

if [ "$SYNC_ERROR" -eq 0 ]; then
    echo "[+] SUCCESS: Cloud infrastructure mirrors are completely current."
else
    echo "[!] WARNING: Cloud sync finished with localized folder exceptions."
fi

# Clean execution lockfile
rm -f .sync_start "$RAW_LOG"
echo "========================================="
echo " WORKFLOW COMPLETE"
echo "========================================="
  1. Save and exit (Ctrl + O, Enter, Ctrl + X).
  2. Grant execute permissions to the script:
    chmod +x canvas_sync.sh

Step 6: Run the Engine

Whenever you need to refresh your local library, generate updated reading packs, and mirror them to the cloud, run:

./canvas_sync.sh

The engine will verify your credentials, skip materials you already have, compile fresh PDFs, and cleanly replicate everything to Google Drive without freezing your machine or tripping cloud rate limits.


Troubleshooting & Core Constraints

  • "Warning: Access denied (403) for course files": Many universities intentionally lock the global Canvas /files API endpoint for student roles to protect licensed course packs. When this occurs, the script bypasses the raw files index and scrapes files, assignment briefs, and module pages directly. Any locked or externally hosted readings are logged into login_links.txt for one-click browser download.
  • Google Drive API 403 / "RATE_LIMIT_EXCEEDED": Google Drive enforces strict per-minute query limits across consumer and institutional projects. If you process dozens of modules simultaneously, rapid API calls can temporarily exhaust your quota. The script prevents this by throttling transactions (--tpslimit 8 --transfers 2) and grouping directory requests with --fast-list.
  • LibreOffice PDF Conversion Failures: Older office documents or slide files with spaces in their path can fail with code 0x11b if handled incorrectly. The updated script forces canonical absolute pathing and uses a clean, isolated temporary profile environment for each document.
  • Expired Tokens & Cloud Reconnection: If your university forces periodic password resets, re-issue your Canvas token in your browser (Step 3) and update ~/.canvas_sync.conf. If your Google Drive token expires, re-authenticate quickly by running rclone config reconnect YOUR_REMOTE_NAME.

Friday, February 3, 2012

My apt sources.list

My /ect/apt/sources.list on Ubuntu 10.04.3 LTS (Lucid Lynx).

Code:
# MAIN
deb http://archive.ubuntu.com/ubuntu/ lucid main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ lucid main restricted universe multiverse

# UPDATES
deb http://archive.ubuntu.com/ubuntu/ lucid-updates main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ lucid-updates main restricted universe multiverse

# BACKPORTS
deb http://archive.ubuntu.com/ubuntu/ lucid-backports main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ lucid-backports main restricted universe multiverse

# SECURITY
deb http://security.ubuntu.com/ubuntu lucid-security main restricted universe multiverse
deb-src http://security.ubuntu.com/ubuntu lucid-security main restricted universe multiverse

# CANONICAL
deb http://archive.canonical.com/ lucid partner
deb http://archive.canonical.com/ubuntu lucid partner
deb-src http://archive.canonical.com/ubuntu lucid partner

# TOR
deb http://deb.torproject.org/torproject.org lucid main
deb-src http://deb.torproject.org/torproject.org lucid main

# MEDIBUNTU ## wget --quiet http://packages.medibuntu.org/medibuntu-key.gpg -O - | sudo apt-key add -
deb http://packages.medibuntu.org/ lucid free non-free
deb-src http://packages.medibuntu.org/ lucid free non-free

# GOOGLE ## wget --quiet http://dl.google.com/linux/linux_signing_key.pub -O - | sudo apt-key add -
deb http://dl.google.com/linux/deb/ stable non-free
Use at your own risk, no responsibility taken, this might break your machine, etcetera, etcetera.

Copy a website using wget

Download a copy of a whole website using wget

Code:
wget --random-wait --limit-rate=20K -r -p -e robots=off -U mozilla http://www.targetsite.com
Use at your own risk, no responsibility taken, this might break your machine &/or get you into trouble & it will probably upset the targetsite you are downloading, etcetera, etcetera.

Fix a broken gnome panel

A simple, one line command to fix a broken gnome panel & restore it to the default settings. I came across it after I accidentally deleted my Gnome panel. Lately I have found it useful for situations where the panel just becomes corrupt (I am always switching monitors) or the panel just won't do what you want it to do (hidden sound, network, time applets etc). Tested on Ubuntu 10.04.3 LTS (Lucid Lynx).

Code:
gconftool-2 -shutdown && gconftool --recursive-unset /apps/panel && rm -rf ~/.gconf/apps/panel && pkill gnome-panel
Use at your own risk, no responsibility taken, this might break your machine, etcetera, etcetera.

Purge unused Ubuntu linux kernels

A simple, one line command to delete (purge) old, unused Ubuntu linux kernels, & update your grub start-up menu. Tested on Ubuntu 10.04.3 LTS (Lucid Lynx).

Code:
sudo dpkg -l linux-* | awk '/^ii/{ print $2}' | grep -v -e `uname -r | cut -f1,2 -d"-"` | grep -e [0-9] | xargs sudo apt-get -y purge
Use at your own risk, no responsibility taken, this might break your machine, etcetera, etcetera.

Saturday, May 15, 2010

HOWTO: delete your Facebook account

Ever tried to delete your Facebook account. They don't make it easy.

Most people look for a delete option on the Account Settings page. Ah, but that would be too obvious & they only give you the option to deactivate your profile. It puts your account on hold.... just in case you want to come back.

If you really want to delete your account, log in to your Facebook profile first and then click this link. It should take you straight to the well hidden Facebook “Delete my account” form.

Friday, May 14, 2010

Ubuntu 10.4 & your Epson C1100

Well 10.4 is finally out & running in the main stream, its quick, its slick & the windows now are all left hand drive (the maximise, minimise & close have all move left AKA mac style).

For me everything just works, it's rock solid & though I miss the old brown Ubuntu theme, I could never go back purple is this years black.

I had the same old issue with getting my Epson C1100 working but by following the simple steps below I was printing in less than 5 minutes.

10.4 like its predecessor seems to be missing libstdc++5_3.3.6-17ubuntu1_i386.deb

In order to install the Epson C1100 under 10.4 on the standard i386 platform please open an terminal via ACCESSORIES > TERMINAL & make a directory as explained below to store your installation files;


Code:
mkdir epson_install
move to the installation directory you just created;

Code:
cd epson_install
get the installation files from our website 000it.com

extract the compressed file;

Code:
tar -zxvf epson_c1100_install.tar.gz
install the deb installation files you have just extracted;

Code:
sudo dpkg -i *.deb
from here you can install your Epson C1100 just as you would normally install any other printer using the SYSTEM > ADMINISTRATION > PRINTERS menu. When/if called for the ppd (PostScript® printer description) file, please point to the "epson_install" directory & select the file Epson-AL-C1100-fm3.ppd