Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions video/gource-captions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@
1584918000|v0.9.2 released (2020-03-23)
1606172400|v0.10.0 released (2020-11-24)
1623794400|v0.11.0 released (2021-06-16)
1664841600|v0.12.0 released (2022-10-04)
1697500800|v0.12.3 released (2023-10-17)
1705190400|awatcher community watcher gains popularity (2024-01-14)
1714953600|aw-watcher-enhanced with OCR/LLM (2024-05-06)
1727740800|v0.13.0 released (2024-10-01)
1735689600|Community repos now included in visualization (2025-01-01)
130 changes: 108 additions & 22 deletions video/gource-output.sh
Original file line number Diff line number Diff line change
@@ -1,33 +1,72 @@
#!/bin/bash
# ActivityWatch Gource Visualization Script
# Updated 2026-01 to include community repos and extend timeline 2014-2025+

set -e

rootdir=../../../
tmpdir=.cache/gource
communitydir=.cache/community # Directory for community repos
rm -rf $tmpdir
mkdir -p $tmpdir
mkdir -p $communitydir

echo "=== ActivityWatch Development Visualization ==="
echo "Building gource visualization with official + community repos"

# Clone/update community repos (external contributors)
echo ""
echo "=== Fetching community repos ==="
community_repos=(
# Popular watchers from the community (awesome-activitywatch)
"2e3s/awatcher" # Popular Rust watcher for X11/Wayland
"kepptic/aw-watcher-enhanced" # Enhanced window watcher with OCR/LLM
"2e3s/aw-watcher-media-player" # Media playback tracking
"brayo-pip/aw-watcher-lastfm" # Last.fm scrobbles
"Otto-AA/aw-watcher-vscode" # VSCode extension
"OlivierMary/aw-watcher-jetbrains" # JetBrains extension
"NicoWeio/activitywatch-plasmoid" # KDE Plasma widget
"phrp720/aw-sync-suite" # Prometheus/Grafana sync
)

echo "Building stuff"
for repo in "${community_repos[@]}"; do
name=$(basename $repo)
if [ -d "$communitydir/$name" ]; then
echo "Updating $name..."
(cd "$communitydir/$name" && git pull --quiet) || echo " (pull failed, using cached)"
else
echo "Cloning $name..."
git clone --quiet "https://github.com/$repo.git" "$communitydir/$name" 2>/dev/null || echo " (clone failed, skipping)"
fi
done

# Bundle repo
# Bundle repo (official ActivityWatch)
echo ""
echo "=== Processing official repos ==="
gource --output-custom-log $tmpdir/activitywatch.txt $rootdir

modules=(
aw-core
# clients
aw-client
other/aw-client-js
other/aw-client-rust
# server
aw-server
aw-server-rust
# ui
aw-server/aw-webui
aw-qt
# watchers
aw-tauri
# watchers (official)
aw-watcher-afk
aw-watcher-window
aw-watcher-input
aw-watcher-spotify
other/aw-watcher-web
other/aw-watcher-window-wayland
# sync
aw-server-rust/aw-sync
# website and docs
other/activitywatch.github.io
docs
Expand All @@ -44,19 +83,35 @@ for path in "${modules[@]}"; do
loc=$(echo $loc | sed -E "s#.+-client.*#clients/$name#g")
loc=$(echo $loc | sed -E "s#.+-server.*#servers/$name#g")
loc=$(echo $loc | sed -E "s#docs|activitywatch.github.io#website/$name#g")
echo $name $loc
gource --output-custom-log $tmpdir/$name.txt $rootdir/$path
sed -i -r "s#(.+)\\|#\\1|/$loc#" $tmpdir/$name.txt
echo " $name -> $loc"
if [ -d "$rootdir/$path" ]; then
gource --output-custom-log $tmpdir/$name.txt $rootdir/$path 2>/dev/null || echo " (skipped)"
sed -i -r "s#(.+)\\|#\\1|/$loc#" $tmpdir/$name.txt 2>/dev/null || true
fi
done

# Process community repos
echo ""
echo "=== Processing community repos ==="
for repo in "${community_repos[@]}"; do
name=$(basename $repo)
if [ -d "$communitydir/$name" ]; then
echo " $name -> community/$name"
gource --output-custom-log $tmpdir/community-$name.txt $communitydir/$name 2>/dev/null || echo " (skipped)"
sed -i -r "s#(.+)\\|#\\1|/community/$name#" $tmpdir/community-$name.txt 2>/dev/null || true
fi
done

# Remove all files in activitywatch-old repo when rewrite began
# TODO: Remove in a logical order (deepest first)
sed -E 's/.+[|](.+)[|].+[|](.+)/1461708000|\1|D|\2/g' $tmpdir/activitywatch-old.txt | uniq > $tmpdir/fixes.txt
if [ -f "$tmpdir/activitywatch-old.txt" ]; then
sed -E 's/.+[|](.+)[|].+[|](.+)/1461708000|\1|D|\2/g' $tmpdir/activitywatch-old.txt | uniq > $tmpdir/fixes.txt
fi

gourcelog=$tmpdir/combined.gource

# Combine all the logs into one log
cat $tmpdir/*.txt | sort -n > $gourcelog
cat $tmpdir/*.txt 2>/dev/null | sort -n > $gourcelog

# Rename activitywatch-old so it behaves like top dir
sed -i 's#activitywatch-old/##g' $gourcelog
Expand All @@ -65,11 +120,14 @@ sed -i 's#activitywatch-old/##g' $gourcelog
sed -i -E 's#.+/.github/##g' $gourcelog

# Color certain file extensions a certain way
sed -i 's/[.]py/\0|4B8BBE/g' $gourcelog
sed -i 's/[.]rs$/\0|FFAA33/g' $gourcelog
sed -i 's/[.]js/\0|F0DB4F/g' $gourcelog
sed -i 's/[.]ts/\0|007ACC/g' $gourcelog
sed -i 's/[.]vue/\0|41B883/g' $gourcelog
sed -i 's/[.]py/\0|4B8BBE/g' $gourcelog # Python - blue
sed -i 's/[.]rs$/\0|FFAA33/g' $gourcelog # Rust - orange
sed -i 's/[.]js/\0|F0DB4F/g' $gourcelog # JavaScript - yellow
sed -i 's/[.]ts/\0|007ACC/g' $gourcelog # TypeScript - blue
sed -i 's/[.]vue/\0|41B883/g' $gourcelog # Vue - green
sed -i 's/[.]go$/\0|00ADD8/g' $gourcelog # Go - cyan (for awatcher)
sed -i 's/[.]kt$/\0|7F52FF/g' $gourcelog # Kotlin - purple (for JetBrains)
sed -i 's/[.]java$/\0|B07219/g' $gourcelog # Java - brown

# Docs
sed -i -E 's/[.](md|rst|txt)|LICENSE$/\0|FF5555/g' $gourcelog
Expand All @@ -86,24 +144,48 @@ sed -i 's/johan-bjareholt/Johan Bjäreholt/g' $gourcelog
sed -i -E 's/Erik Bj.{1,4}reholt/Erik Bjäreholt/g' $gourcelog
sed -i 's/dependabot.+/dependabot/g' $gourcelog
sed -i 's/Bill-linux/Bill Ang Li/g' $gourcelog
sed -i 's/2e3s/Dzmitry/g' $gourcelog # awatcher author

# Remove names which have been spamming commits in CI (accidental bad CI config)
sed -i 's/.*ErikBjare.*//g' $gourcelog

# Prepare avatars
# TODO: Doesn't fetch avatars from all repos (only the ones with most contributors)
# run for contributor-stats repo, initialized .git/avatars folder
if [ -x .git/avatars ]; then
perl fetch-avatars.pl
# TODO: Dynamic avatar evolution - change user avatars over time to reflect
# their profile picture at that point in history. This would require:
# 1. GitHub API to fetch historical avatar URLs (if available) or
# 2. Wayback Machine integration to get historical profile pictures
# 3. Gource custom avatar timeline support (may need patching gource)
# For now, avatars are static (most recent profile picture)
if [ -d ../.git/avatar ]; then
# run for contributor-stats repo
perl fetch-avatars.pl 2>/dev/null || echo "Avatar fetch skipped"
# run for bundle repo, move avatars to local .git/avatars
fetchsrc=$(realpath fetch-avatars.pl)
pushd $rootdir; perl $fetchsrc; popd; mv $rootdir/.git/avatar/* .git/avatar
pushd $rootdir/aw-server/aw-webui; perl $fetchsrc; popd; mv $rootdir/aw-server/aw-webui/.git/avatar/* .git/avatar
pushd $rootdir/docs; perl $fetchsrc; popd; mv $rootdir/docs/.git/avatar/* .git/avatar
if [ -d "$rootdir/.git" ]; then
pushd $rootdir > /dev/null; perl $fetchsrc 2>/dev/null || true; popd > /dev/null
[ -d "$rootdir/.git/avatar" ] && mv $rootdir/.git/avatar/* ../.git/avatar/ 2>/dev/null || true
fi
if [ -d "$rootdir/aw-server/aw-webui/.git" ]; then
pushd $rootdir/aw-server/aw-webui > /dev/null; perl $fetchsrc 2>/dev/null || true; popd > /dev/null
[ -d "$rootdir/aw-server/aw-webui/.git/avatar" ] && mv $rootdir/aw-server/aw-webui/.git/avatar/* ../.git/avatar/ 2>/dev/null || true
fi
if [ -d "$rootdir/docs/.git" ]; then
pushd $rootdir/docs > /dev/null; perl $fetchsrc 2>/dev/null || true; popd > /dev/null
[ -d "$rootdir/docs/.git/avatar" ] && mv $rootdir/docs/.git/avatar/* ../.git/avatar/ 2>/dev/null || true
fi
# Fetch avatars for community contributors
for repo in "${community_repos[@]}"; do
name=$(basename $repo)
if [ -d "$communitydir/$name/.git" ]; then
pushd $communitydir/$name > /dev/null; perl $fetchsrc 2>/dev/null || true; popd > /dev/null
[ -d "$communitydir/$name/.git/avatar" ] && mv $communitydir/$name/.git/avatar/* ../.git/avatar/ 2>/dev/null || true
fi
done
fi

# Rename avatars to suit committer name
cp ../.git/avatar/johan-bjareholt.png '../.git/avatar/Johan Bjäreholt.png'
[ -f "../.git/avatar/johan-bjareholt.png" ] && cp ../.git/avatar/johan-bjareholt.png '../.git/avatar/Johan Bjäreholt.png' 2>/dev/null || true

# Resolutions:
# - 2560x1440 (for upload)
Expand All @@ -117,7 +199,7 @@ res_low=1280x720
# this would be nice, but unfortunately counts directories...
# --file-extension-fallback
gource_options=(
--title 'ActivityWatch (https://activitywatch.net)'
--title 'ActivityWatch Development 2014-2025+ (https://activitywatch.net)'
--caption-file gource-captions.txt
--user-image-dir ../.git/avatar/
--key --file-idle-time 0
Expand All @@ -141,8 +223,12 @@ gource_options=(
$gourcelog
)

echo "Visualizing"
echo ""
echo "=== Visualizing ==="
gource "${gource_options[@]}"

# To render video
echo ""
echo "=== Rendering video ==="
gource "${gource_options[@]}" -o - | ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i - -vcodec libx264 -preset ultrafast -pix_fmt yuv420p -crf 1 -threads 0 -bf 0 gource.mp4
echo "Done! Output: gource.mp4"
Loading