resetting repo

This commit is contained in:
2025-08-13 22:56:59 +02:00
parent 6cac00c6a6
commit 06416eed22
4440 changed files with 12270 additions and 2083 deletions

46
.gitattributes vendored
View File

@@ -1,15 +1,33 @@
*.db filter=lfs diff=lfs merge=lfs -text *.db filter=lfs diff=lfs merge=lfs -text
*.exe filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text *.JPG filter=lfs diff=lfs merge=lfs -text
*.PNG filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text *.PNG filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text *.pnG filter=lfs diff=lfs merge=lfs -text
*.osk filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text
*.osr filter=lfs diff=lfs merge=lfs -text *.webp filter=lfs diff=lfs merge=lfs -text
*.osz filter=lfs diff=lfs merge=lfs -text *.psd filter=lfs diff=lfs merge=lfs -text
*.xcf filter=lfs diff=lfs merge=lfs -text
*.pxr filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.WAV filter=lfs diff=lfs merge=lfs -text
*.wav123321321 filter=lfs diff=lfs merge=lfs -text
*.sfk filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.lnk filter=lfs diff=lfs merge=lfs -text
*.pk filter=lfs diff=lfs merge=lfs -text
*.fig filter=lfs diff=lfs merge=lfs -text
*.fds filter=lfs diff=lfs merge=lfs -text
*.pdn filter=lfs diff=lfs merge=lfs -text
*.osk filter=lfs diff=lfs merge=lfs -text
*.osr filter=lfs diff=lfs merge=lfs -text
*.osz filter=lfs diff=lfs merge=lfs -text
*.pack filter=lfs diff=lfs merge=lfs -text *.pack filter=lfs diff=lfs merge=lfs -text
*.pdn filter=lfs diff=lfs merge=lfs -text *.mp4 filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.WAV filter=lfs diff=lfs merge=lfs -text

View File

@@ -1,9 +1,7 @@
name: CI/CD Pipeline name: Generate Skin previews, OSK files and per skin documentation
on: on:
push: push:
branches:
- main
paths: paths:
- '.gitea/workflows/*' - '.gitea/workflows/*'
- 'Skins/**/*' - 'Skins/**/*'
@@ -25,22 +23,26 @@ env:
DANSER_SCREENSHOT_DIR: "/app/danser/screenshots" DANSER_SCREENSHOT_DIR: "/app/danser/screenshots"
SKINS_DIR: "${{ github.workspace }}/Skins" SKINS_DIR: "${{ github.workspace }}/Skins"
DANSER_SKINS_DIR: "/app/danser/skins" DANSER_SKINS_DIR: "/app/danser/skins"
DEFAULT_SKIN_DIR: "${{ github.workspace }}/src/default-skin" DEFAULT_SKIN_DIR: "/app/danser/skins/default-skin"
REPO_SCREENSHOT_DIR: "${{ github.workspace }}/media/gameplay" REPO_SCREENSHOT_DIR: "${{ github.workspace }}/media/gameplay"
REPO_MOD_ICONS_DIR: "${{ github.workspace }}/media/icons" REPO_MOD_ICONS_DIR: "${{ github.workspace }}/media/icons"
REPO_RANKING_PANEL_DIR: "${{ github.workspace }}/media/panel" REPO_RANKING_PANEL_DIR: "${{ github.workspace }}/media/panel"
REPO_THUMBNAIL_DIR: "${{ github.workspace }}/media/thumbnail"
SETTINGS_JSON_PATH: "/app/danser/settings/default.json" SETTINGS_JSON_PATH: "/app/danser/settings/default.json"
README_PATH: "${{ github.workspace }}/README.md" README_PATH: "${{ github.workspace }}/README.md"
REPLAY_PATH: "${{ github.workspace }}/src/replay.osr" GAMEPLAY_REPLAY_PATH: "/app/danser/custom-replays/yomi_yori.osr"
THUMBNAIL_REPLAY_PATH: "/app/danser/custom-replays/combo_colors.osr"
PANEL_REPLAY_PATH: "/app/danser/custom-replays/2000_gekis.osr"
OSK_PATH: "${{ github.workspace }}/export" OSK_PATH: "${{ github.workspace }}/export"
IMAGE_NAME: osc/skins-image IMAGE_NAME: osc/skins-image
REGISTRY_URL: "https://${{ vars.CONTAINER_REGISTRY }}" REGISTRY_URL: "https://${{ vars.CONTAINER_REGISTRY }}"
OSU_ID: ${{ vars.OSUID }} OSU_ID: ${{ vars.OSUID }}
DOC_DIR: "${{ github.workspace }}/docs"
jobs: jobs:
generate_everything: generate_everything:
name: Full CI/CD Pipeline name: Full CI/CD Pipeline
runs-on: ubuntu-latest runs-on: danser
container: container:
image: ${{ vars.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest image: ${{ vars.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
options: >- options: >-
@@ -101,8 +103,8 @@ jobs:
echo "→ Target skins specified. Using target_skins input…" echo "→ Target skins specified. Using target_skins input…"
IFS=',' read -r -a input_skins <<< "$target_skins" IFS=',' read -r -a input_skins <<< "$target_skins"
for s in "${input_skins[@]}"; do for s in "${input_skins[@]}"; do
s="${s#"${s%%[![:space:]]*}"}" # trim leading whitespace s="${s#"${s%%[![:space:]]*}"}"
s="${s%"${s##*[![:space:]]}"}" # trim trailing whitespace s="${s%"${s##*[![:space:]]}"}"
[[ -n "$s" ]] && skins+=("$s") [[ -n "$s" ]] && skins+=("$s")
done done
echo " ✓ Found ${#skins[@]} skin(s) from target_skins input" echo " ✓ Found ${#skins[@]} skin(s) from target_skins input"
@@ -119,7 +121,7 @@ jobs:
git diff --name-only -z --diff-filter=AM "$latest_tag" HEAD \ git diff --name-only -z --diff-filter=AM "$latest_tag" HEAD \
| while IFS= read -r -d '' file; do | while IFS= read -r -d '' file; do
[[ $file == Skins/* ]] && echo "${file#Skins/}" | cut -d/ -f1 [[ $file == Skins/* ]] && echo "${file#Skins/}" | cut -d/ -f1
done done | sort -u
) )
echo " ✓ Found ${#skins[@]} added/modified skins" echo " ✓ Found ${#skins[@]} added/modified skins"
@@ -128,7 +130,7 @@ jobs:
git diff --name-only -z --diff-filter=D "$latest_tag" HEAD \ git diff --name-only -z --diff-filter=D "$latest_tag" HEAD \
| while IFS= read -r -d '' file; do | while IFS= read -r -d '' file; do
[[ $file == Skins/* ]] && echo "${file#Skins/}" | cut -d/ -f1 [[ $file == Skins/* ]] && echo "${file#Skins/}" | cut -d/ -f1
done done | sort -u
) )
if [ "${#deleted_skins[@]}" -gt 0 ]; then if [ "${#deleted_skins[@]}" -gt 0 ]; then
for d in "${deleted_skins[@]}"; do for d in "${deleted_skins[@]}"; do
@@ -217,9 +219,8 @@ jobs:
shell: bash shell: bash
run: | run: |
echo "Creating base directories for assets..." echo "Creating base directories for assets..."
mkdir -p "$REPO_SCREENSHOT_DIR" "$REPO_MOD_ICONS_DIR" "$REPO_RANKING_PANEL_DIR" "$OSK_PATH" mkdir -p "$REPO_SCREENSHOT_DIR" "$REPO_MOD_ICONS_DIR" "$REPO_RANKING_PANEL_DIR" "$OSK_PATH" "$REPO_THUMBNAIL_DIR"
# Read the newline-delimited list back into an array
readarray -t skins <<< "$ALL_SKINS_DIR" readarray -t skins <<< "$ALL_SKINS_DIR"
for skin in "${skins[@]}"; do for skin in "${skins[@]}"; do
@@ -228,7 +229,8 @@ jobs:
"$REPO_SCREENSHOT_DIR/$skin" \ "$REPO_SCREENSHOT_DIR/$skin" \
"$REPO_MOD_ICONS_DIR/$skin" \ "$REPO_MOD_ICONS_DIR/$skin" \
"$REPO_RANKING_PANEL_DIR/$skin" \ "$REPO_RANKING_PANEL_DIR/$skin" \
"$OSK_PATH/$skin" "$OSK_PATH/$skin" \
"$REPO_THUMBNAIL_DIR/$skin"
done done
echo "All asset directories created for ${#skins[@]} skins." echo "All asset directories created for ${#skins[@]} skins."
@@ -279,50 +281,73 @@ jobs:
[ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin: $skin_path"; continue; } [ ! -d "$SKIN_DIR" ] && { echo "Skipping missing skin: $skin_path"; continue; }
SKIN_NAME="$skin_path" SKIN_NAME="$skin_path"
OUT_GIF_DIR="$REPO_SCREENSHOT_DIR/$SKIN_NAME" OUT_VIDEO_DIR="$REPO_SCREENSHOT_DIR/$SKIN_NAME"
OUT_PNG_DIR="$REPO_RANKING_PANEL_DIR/$SKIN_NAME" OUT_PNG_DIR="$REPO_RANKING_PANEL_DIR/$SKIN_NAME"
OUT_THUMBNAIL_DIR="$REPO_THUMBNAIL_DIR/$SKIN_NAME"
echo "" echo ""
echo "[$INDEX/$SKIN_COUNT] Generating for skin: $SKIN_NAME" echo "[$INDEX/$SKIN_COUNT] Generating for skin: $SKIN_NAME"
LOGFILE="/tmp/danser_log_$INDEX.txt" LOGFILE="/tmp/danser_log_$INDEX.txt"
FFMPEG_LOG="/tmp/ffmpeg_log_$INDEX.txt"
echo " → Generating video..." echo " → Generating video..."
if ! xvfb-run -a "$DANSER_DIR/danser-cli" \ if ! xvfb-run -a "$DANSER_DIR/danser-cli" \
-replay "$REPLAY_PATH" -record -skip -start=215 -end=230 -noupdatecheck \ -replay "$GAMEPLAY_REPLAY_PATH" -record -skip -start=300 -end=307 -noupdatecheck \
-out="$SKIN_NAME" -skin="$SKIN_NAME" >"$LOGFILE" 2>&1; then -out="$SKIN_NAME" -skin="$SKIN_NAME" >"$LOGFILE" 2>&1; then
echo " ✖ Video failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue echo " ✖ Video failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue
fi fi
echo " → Taking screenshot..."
if ! xvfb-run -a "$DANSER_DIR/danser-cli" \
-replay "$REPLAY_PATH" -skip -noupdatecheck -ss 243 \
-out="$SKIN_NAME" -skin="$SKIN_NAME" >>"$LOGFILE" 2>&1; then
echo " ✖ Screenshot failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue
fi
if [ -f "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" ]; then if [ -f "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" ]; then
echo " → Converting to GIF..." echo " → Trimming MP4 with ffmpeg..."
if ffmpeg -y -hwaccel cuda -ss 4 -t 10 -i "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" \ ffmpeg -hide_banner -loglevel error \
-filter_complex "[0:v] fps=24,scale=720:-1:flags=lanczos,palettegen [p]; [0:v] fps=24,scale=720:-1:flags=lanczos [x]; [x][p] paletteuse" \ -ss 5 -t 6.5 \
-c:v gif "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" >"$FFMPEG_LOG" 2>&1; then -i "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" \
mv "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" "$OUT_GIF_DIR/$SKIN_NAME.gif" -c:v h264_nvenc -preset fast \
echo " ✓ GIF moved to $OUT_GIF_DIR/" -c:a aac -b:a 128k \
"$DANSER_VIDEO_DIR/${SKIN_NAME}_trimmed.mp4"
if [ -f "$DANSER_VIDEO_DIR/${SKIN_NAME}_trimmed.mp4" ]; then
mv "$DANSER_VIDEO_DIR/${SKIN_NAME}_trimmed.mp4" "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4"
mkdir -p "$OUT_VIDEO_DIR"
mv "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" "$OUT_VIDEO_DIR/$SKIN_NAME.mp4"
echo " ✓ Trimmed MP4 moved to $OUT_VIDEO_DIR/"
else else
echo " ✖ FFmpeg conversion failed for $SKIN_NAME"; cat "$FFMPEG_LOG" echo " ✖ ffmpeg trimming failed for $SKIN_NAME"
fi fi
else else
echo " ✖ No MP4 found for $SKIN_NAME" echo " ✖ No MP4 found for $SKIN_NAME"
fi fi
echo " → Taking screenshot..."
if ! xvfb-run -a "$DANSER_DIR/danser-cli" \
-replay "$PANEL_REPLAY_PATH" -skip -noupdatecheck -ss 28 \
-out="$SKIN_NAME" -skin="$SKIN_NAME" >>"$LOGFILE" 2>&1; then
echo " ✖ Screenshot failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue
fi
if [ -f "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" ]; then if [ -f "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" ]; then
mkdir -p "$OUT_PNG_DIR"
mv "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" "$OUT_PNG_DIR/$SKIN_NAME.png" mv "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" "$OUT_PNG_DIR/$SKIN_NAME.png"
echo " ✓ PNG moved to $OUT_PNG_DIR/" echo " ✓ PNG moved to $OUT_PNG_DIR/"
else else
echo " ✖ No PNG found for $SKIN_NAME" echo " ✖ No PNG found for $SKIN_NAME"
fi fi
echo " → Taking thumbnail screenshot..."
if ! xvfb-run -a "$DANSER_DIR/danser-cli" \
-replay "$THUMBNAIL_REPLAY_PATH" -skip -noupdatecheck -ss 1.3 \
-out="${SKIN_NAME}_thumb" -skin="$SKIN_NAME" >>"$LOGFILE" 2>&1; then
echo " ✖ Thumbnail screenshot failed for $SKIN_NAME"; cat "$LOGFILE"; INDEX=$((INDEX+1)); continue
fi
if [ -f "$DANSER_SCREENSHOT_DIR/${SKIN_NAME}_thumb.png" ]; then
mkdir -p "$OUT_THUMBNAIL_DIR"
mv "$DANSER_SCREENSHOT_DIR/${SKIN_NAME}_thumb.png" "$OUT_THUMBNAIL_DIR/$SKIN_NAME.png"
echo " ✓ Thumbnail PNG moved to $OUT_THUMBNAIL_DIR/"
else
echo " ✖ No thumbnail PNG found for $SKIN_NAME"
fi
INDEX=$((INDEX + 1)) INDEX=$((INDEX + 1))
done done
@@ -370,18 +395,21 @@ jobs:
if [ -n "$name_line" ]; then if [ -n "$name_line" ]; then
val="${name_line#*:}" val="${name_line#*:}"
val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
sanitized="$(sanitize_filename "$val")" if [ -n "$val" ]; then
[ -n "$sanitized" ] && skin_header="$sanitized" sanitized="$(sanitize_filename "$val")"
[ -n "$sanitized" ] && skin_header="$sanitized"
fi
fi fi
fi fi
GIF_DIR="$REPO_SCREENSHOT_DIR/$SKIN_DIR_NAME" VIDEO_DIR="$REPO_SCREENSHOT_DIR/$SKIN_DIR_NAME"
PNG_DIR="$REPO_RANKING_PANEL_DIR/$SKIN_DIR_NAME" PNG_DIR="$REPO_RANKING_PANEL_DIR/$SKIN_DIR_NAME"
THUMBNAIL_DIR="$REPO_THUMBNAIL_DIR/$SKIN_DIR_NAME"
if [ -f "$GIF_DIR/$SKIN_DIR_NAME.gif" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then if [ -f "$VIDEO_DIR/$SKIN_DIR_NAME.mp4" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then
mv -f "$GIF_DIR/$SKIN_DIR_NAME.gif" \ mv -f "$VIDEO_DIR/$SKIN_DIR_NAME.mp4" \
"$GIF_DIR/$skin_header.gif" || true "$VIDEO_DIR/$skin_header.mp4" || true
echo " ✓ Renamed GIF to $GIF_DIR/$skin_header.gif" echo " ✓ Renamed MP4 to $VIDEO_DIR/$skin_header.mp4"
fi fi
if [ -f "$PNG_DIR/$SKIN_DIR_NAME.png" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then if [ -f "$PNG_DIR/$SKIN_DIR_NAME.png" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then
@@ -390,13 +418,19 @@ jobs:
echo " ✓ Renamed PNG to $PNG_DIR/$skin_header.png" echo " ✓ Renamed PNG to $PNG_DIR/$skin_header.png"
fi fi
if [ -f "$THUMBNAIL_DIR/$SKIN_DIR_NAME.png" ] && [ "$SKIN_DIR_NAME" != "$skin_header" ]; then
mv -f "$THUMBNAIL_DIR/$SKIN_DIR_NAME.png" \
"$THUMBNAIL_DIR/$skin_header.png" || true
echo " ✓ Renamed thumbnail to $THUMBNAIL_DIR/$skin_header.png"
fi
INDEX=$((INDEX + 1)) INDEX=$((INDEX + 1))
done done
echo "" echo ""
echo "[Asset Renaming Complete — processed $SKIN_COUNT skins]" echo "[Asset Renaming Complete — processed $SKIN_COUNT skins]"
- name: Generate Mod Icons - name: Generate Mod Icons (WEBP)
shell: bash shell: bash
run: | run: |
echo "[Mod Icon Generation Job Started]" echo "[Mod Icon Generation Job Started]"
@@ -438,8 +472,10 @@ jobs:
if [ -n "$name_line" ]; then if [ -n "$name_line" ]; then
val="${name_line#*:}" val="${name_line#*:}"
val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
sanitized="$(sanitize_filename "$val")" if [ -n "$val" ]; then
[ -n "$sanitized" ] && skin_header="$sanitized" sanitized="$(sanitize_filename "$val")"
[ -n "$sanitized" ] && skin_header="$sanitized"
fi
fi fi
fi fi
@@ -449,7 +485,7 @@ jobs:
ICON_FOLDER="$SKIN_DIR" ICON_FOLDER="$SKIN_DIR"
OUTPUT_DIR="$REPO_MOD_ICONS_DIR/$skin_path" OUTPUT_DIR="$REPO_MOD_ICONS_DIR/$skin_path"
mkdir -p "$OUTPUT_DIR" mkdir -p "$OUTPUT_DIR"
OUTPUT="$OUTPUT_DIR/${skin_header}-mod-icons.png" OUTPUT="$OUTPUT_DIR/${skin_header}-mod-icons.webp"
row_images=() row_images=()
row_index=1 row_index=1
@@ -457,15 +493,22 @@ jobs:
for group_list in "$group1_icons" "$group2_icons" "$group3_icons"; do for group_list in "$group1_icons" "$group2_icons" "$group3_icons"; do
montage_files=() montage_files=()
for icon in $group_list; do for icon in $group_list; do
file=""
if [ -f "${ICON_FOLDER}/selection-mod-${icon}@2x.png" ]; then if [ -f "${ICON_FOLDER}/selection-mod-${icon}@2x.png" ]; then
montage_files+=("${ICON_FOLDER}/selection-mod-${icon}@2x.png") file="${ICON_FOLDER}/selection-mod-${icon}@2x.png"
elif [ -f "${ICON_FOLDER}/selection-mod-${icon}.png" ]; then
file="${ICON_FOLDER}/selection-mod-${icon}.png"
elif [ -f "${DEFAULT_SKIN_DIR}/selection-mod-${icon}@2x.png" ]; then elif [ -f "${DEFAULT_SKIN_DIR}/selection-mod-${icon}@2x.png" ]; then
montage_files+=("${DEFAULT_SKIN_DIR}/selection-mod-${icon}@2x.png") file="${DEFAULT_SKIN_DIR}/selection-mod-${icon}@2x.png"
fi fi
[ -n "$file" ] && montage_files+=("$file")
done done
while [ "${#montage_files[@]}" -lt 7 ]; do while [ "${#montage_files[@]}" -lt 7 ]; do
montage_files+=("$BLANK_IMAGE") montage_files+=("$BLANK_IMAGE")
done done
magick montage "${montage_files[@]}" \ magick montage "${montage_files[@]}" \
-tile "7x1" -geometry "160x160+10+10" -background none \ -tile "7x1" -geometry "160x160+10+10" -background none \
"row_${row_index}.png" "row_${row_index}.png"
@@ -475,8 +518,10 @@ jobs:
magick montage "${row_images[@]}" \ magick montage "${row_images[@]}" \
-tile "1x${#row_images[@]}" -geometry "+10+10" -background none \ -tile "1x${#row_images[@]}" -geometry "+10+10" -background none \
"$OUTPUT" "temp_combined.png"
rm row_*.png
magick "temp_combined.png" -define webp:lossless=true "$OUTPUT"
rm temp_combined.png row_*.png
echo " ✓ Mod Icons Generated at $OUTPUT" echo " ✓ Mod Icons Generated at $OUTPUT"
INDEX=$((INDEX + 1)) INDEX=$((INDEX + 1))
@@ -487,6 +532,40 @@ jobs:
echo "" echo ""
echo "[Mod Icon Generation Finished — processed $SKIN_COUNT skins]" echo "[Mod Icon Generation Finished — processed $SKIN_COUNT skins]"
- name: Convert PNGs to WEBPs
shell: bash
run: |
echo "[Convert PNG → WEBP Started]"
if [ -z "${CHANGED_SKINS_FILE:-}" ] || [ ! -s "$CHANGED_SKINS_FILE" ]; then
echo "No skins changed. Skipping conversion."
exit 0
fi
mapfile -t skins < "$CHANGED_SKINS_FILE"
[ "${#skins[@]}" -eq 0 ] && { echo "No skins to process. Exiting."; exit 0; }
convert_pngs_to_webp() {
local base_dir="$1"
local skin_path="$2"
local dir="$base_dir/$skin_path"
echo " → Processing: $dir"
[ ! -d "$dir" ] && { echo " ✖ Directory does not exist: $dir"; return; }
find "$dir" -type f -iname "*.png" | while read -r png; do
webp="${png%.png}.webp"
echo " ↳ Converting: $png → $webp"
magick "$png" -define webp:lossless=false -quality 90 "$webp" && rm -f "$png"
done
}
for skin_path in "${skins[@]}"; do
[ -z "$skin_path" ] && continue
convert_pngs_to_webp "$REPO_RANKING_PANEL_DIR" "$skin_path"
convert_pngs_to_webp "$REPO_THUMBNAIL_DIR" "$skin_path"
done
echo "[Convert PNG → WEBP Finished]"
- name: Create OSK Files - name: Create OSK Files
shell: bash shell: bash
run: | run: |
@@ -525,8 +604,10 @@ jobs:
if [ -n "$name_line" ]; then if [ -n "$name_line" ]; then
val="${name_line#*:}" val="${name_line#*:}"
val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
sanitized="$(sanitize_filename "$val")" if [ -n "$val" ]; then
[ -n "$sanitized" ] && skin_header="$sanitized" sanitized="$(sanitize_filename "$val")"
[ -n "$sanitized" ] && skin_header="$sanitized"
fi
fi fi
fi fi
@@ -549,27 +630,28 @@ jobs:
- name: Generate README - name: Generate README
shell: bash shell: bash
run: | run: |
echo "Starting README generation..." echo "Generating README index…"
sanitize_filename() { sanitize_filename() {
echo "$1" | \ echo "$1" | \
tr -d '\000-\037' | \ tr -d '\000-\037' | \
sed -e 's#[\\/:\*\?"<>|]#-#g' | \ sed -e 's#[\\/:\*\?"<>|]#-#g' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
} }
url_encode_path() { url_encode_path() {
local raw_path="$1" local IFS='/'
echo "$raw_path" | tr -d '\r\n' | jq -sRr @uri local parts=($1)
local encoded=""
for part in "${parts[@]}"; do
[ -n "$encoded" ] && encoded+="/"
encoded+=$(printf '%s' "$part" | jq -sRr @uri)
done
echo "$encoded"
} }
SKINS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/skins.json" SKINS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/skins.json"
DESC_FILE=$(mktemp) DESC_FILE=$(mktemp)
echo "Step 1: Extracting descriptions from skins.json..."
jq -r '.descriptions | to_entries[] | "\(.key)=\(.value)"' "$SKINS_JSON_FILE" > "$DESC_FILE"
echo "Step 2: Starting to build README..."
echo "---" > "$README_PATH" echo "---" > "$README_PATH"
echo "gitea: none" >> "$README_PATH" echo "gitea: none" >> "$README_PATH"
echo "include_toc: true" >> "$README_PATH" echo "include_toc: true" >> "$README_PATH"
@@ -583,33 +665,42 @@ jobs:
echo "" >> "$README_PATH" echo "" >> "$README_PATH"
echo "**Go back to [osc/skins]($REGISTRY_URL/osc/skins)**" >> "$README_PATH" echo "**Go back to [osc/skins]($REGISTRY_URL/osc/skins)**" >> "$README_PATH"
echo "" >> "$README_PATH" echo "" >> "$README_PATH"
echo "**Click on the Skin name to download it, or click on the thumbnail to see more about the skin, including a video preview, screenshots, and mod icons.**" >> "$README_PATH"
echo "" >> "$README_PATH"
jq -r '.descriptions | to_entries[] | "\(.key)=\(.value)"' "$SKINS_JSON_FILE" > "$DESC_FILE"
jq -r '.order[]?' "$SKINS_JSON_FILE" > order.txt
get_desc() { get_desc() {
grep -F -m1 -- "$1=" "$DESC_FILE" 2>/dev/null | cut -d '=' -f2- grep -F -m1 -- "$1=" "$DESC_FILE" 2>/dev/null | cut -d '=' -f2- || true
} }
ORDER_FILE=$(mktemp) declare -A ordered
echo "Step 3: Extracting order from skins.json..."
jq -r '.order[]?' "$SKINS_JSON_FILE" > "$ORDER_FILE"
echo "Step 4: Processing ordered skins..."
while IFS= read -r skin; do while IFS= read -r skin; do
echo " Processing skin (order): $skin" [ "$skin" = "default-skin" ] && continue
ordered["$skin"]=1
dir="$DANSER_SKINS_DIR/$skin" dir="$DANSER_SKINS_DIR/$skin"
[ ! -d "$dir" ] && { echo " Skipping missing directory: $dir"; continue; } [ ! -d "$dir" ] && continue
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1) ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1 || true)
skin_header="$skin" skin_header="$skin"
if [ -f "$ini_file" ]; then if [ -f "$ini_file" ]; then
line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 2>/dev/null || true) name_line=$(grep -a -i -m1 'Name[[:space:]]*:' "$ini_file" || true)
[ -n "$line" ] && { if [ -n "$name_line" ]; then
val="${line#*:}" val="${name_line#*:}"
skin_header=$(sanitize_filename "$val") val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
} [ -n "$val" ] && skin_header=$(sanitize_filename "$val")
else
skin_header=$(sanitize_filename "$skin")
fi
else
continue
fi fi
raw_path="$(printf "%s/%s" "$skin" "$skin_header" | sed 's/^ *//;s/ *$//')" raw_path="$(printf "%s/%s" "$skin" "$skin_header" | sed 's/^ *//;s/ *$//')"
base_path=$(url_encode_path "$raw_path") base_path=$(url_encode_path "$raw_path")
echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk)" >> "$README_PATH" echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk)" >> "$README_PATH"
echo "" >> "$README_PATH" echo "" >> "$README_PATH"
@@ -617,71 +708,57 @@ jobs:
[ -n "$desc" ] && { echo "$desc" >> "$README_PATH"; echo "" >> "$README_PATH"; } [ -n "$desc" ] && { echo "$desc" >> "$README_PATH"; echo "" >> "$README_PATH"; }
if [ -f "$ini_file" ]; then if [ -f "$ini_file" ]; then
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 2>/dev/null || true) author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 || true)
if [ -n "$author_line" ]; then if [ -n "$author_line" ]; then
author="${author_line#*:}" author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
author="$(sanitize_filename "$author")"
[ -n "$author" ] && { echo "**Author:** $author" >> "$README_PATH"; echo "" >> "$README_PATH"; } [ -n "$author" ] && { echo "**Author:** $author" >> "$README_PATH"; echo "" >> "$README_PATH"; }
fi fi
fi fi
echo "![$skin_header Gameplay](media/gameplay/${base_path}.gif)" >> "$README_PATH" echo "[![$skin_header Thumbnail](media/thumbnail/${base_path}.webp)](/docs/${base_path}.md)" >> "$README_PATH"
echo "" >> "$README_PATH" echo "" >> "$README_PATH"
echo "![$skin_header Ranking Panel](media/panel/${base_path}.png)" >> "$README_PATH" done < order.txt
echo "" >> "$README_PATH"
echo "![$skin_header Mods](media/icons/${base_path}-mod-icons.png)" >> "$README_PATH"
echo "" >> "$README_PATH"
done < "$ORDER_FILE"
declare -A ordered for dir in "$DANSER_SKINS_DIR"/*; do
while IFS= read -r skin; do [ -d "$dir" ] || continue
ordered["$skin"]=1 skin="$(basename "$dir")"
done < "$ORDER_FILE" [ "$skin" = "default-skin" ] && continue
[[ -n "${ordered[$skin]}" ]] && continue
echo "Step 5: Processing extra skins..." ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1 || true)
if [ ! -s "$ORDER_FILE" ]; then skin_header="$skin"
echo "→ No \`order\` defined in skins.json; skipping extra-skins loop."
else
for dir in "$DANSER_SKINS_DIR"/*; do
[ -d "$dir" ] || continue
skin=$(basename "$dir")
[[ -n "${ordered[$skin]}" ]] && continue
echo " Writing extra skin: $skin" if [ -f "$ini_file" ]; then
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1) name_line=$(grep -a -i -m1 'Name[[:space:]]*:' "$ini_file" || true)
skin_header="$skin" if [ -n "$name_line" ]; then
if [ -f "$ini_file" ]; then val="${name_line#*:}"
line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 2>/dev/null || true) val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
[ -n "$line" ] && { [ -n "$val" ] && skin_header=$(sanitize_filename "$val")
val="${line#*:}" else
skin_header=$(sanitize_filename "$val") skin_header=$(sanitize_filename "$skin")
}
fi fi
else
continue
fi
raw_path="$(printf "%s/%s" "$skin" "$skin_header" | sed 's/^ *//;s/ *$//')" raw_path="$(printf "%s/%s" "$skin" "$skin_header" | sed 's/^ *//;s/ *$//')"
base_path=$(url_encode_path "$raw_path") base_path=$(url_encode_path "$raw_path")
echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "$ini_file" ]; then echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk)" >> "$README_PATH"
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 2>/dev/null || true) echo "" >> "$README_PATH"
if [ -n "$author_line" ]; then
author="${author_line#*:}" if [ -f "$ini_file" ]; then
author="$(sanitize_filename "$author")" author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 || true)
[ -n "$author" ] && { echo "**Author:** $author" >> "$README_PATH"; echo "" >> "$README_PATH"; } if [ -n "$author_line" ]; then
fi author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -n "$author" ] && { echo "**Author:** $author" >> "$README_PATH"; echo "" >> "$README_PATH"; }
fi fi
fi
echo "![$skin_header Gameplay](media/gameplay/${base_path}.gif)" >> "$README_PATH" echo "[![$skin_header Thumbnail](media/thumbnail/${base_path}.webp)](/docs/${base_path}.md)" >> "$README_PATH"
echo "" >> "$README_PATH" echo "" >> "$README_PATH"
echo "![$skin_header Ranking Panel](media/panel/${base_path}.png)" >> "$README_PATH" done
echo "" >> "$README_PATH"
echo "![$skin_header Mods](media/icons/${base_path}-mod-icons.png)" >> "$README_PATH"
echo "" >> "$README_PATH"
done
fi
echo "Step 7: Writing Build History section..."
echo "# Build History" >> "$README_PATH" echo "# Build History" >> "$README_PATH"
echo "" >> "$README_PATH" echo "" >> "$README_PATH"
echo "| Version | Date |" >> "$README_PATH" echo "| Version | Date |" >> "$README_PATH"
@@ -699,7 +776,114 @@ jobs:
done done
fi fi
echo "README generation completed successfully." echo "README index generated successfully."
- name: Generate Per-Skin Pages
shell: bash
run: |
echo "Generating detailed per-skin markdown pages…"
sanitize_filename() {
echo "$1" | \
tr -d '\000-\037' | \
sed -e 's#[\\/:\*\?"<>|]#-#g' | \
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}
url_encode_path() {
local IFS='/'
local parts=($1)
local encoded=""
for part in "${parts[@]}"; do
[ -n "$encoded" ] && encoded+="/"
encoded+=$(printf '%s' "$part" | jq -sRr @uri)
done
echo "$encoded"
}
mkdir -p "$DOC_DIR"
for dir in "$DANSER_SKINS_DIR"/*; do
[ -d "$dir" ] || continue
skin=$(basename "$dir")
[ "$skin" = "default-skin" ] && continue
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n1 || true)
skin_header="$skin"
if [ -f "$ini_file" ]; then
line=$(grep -i '^[[:space:]]*Name:' "$ini_file" | head -n1 || true)
if [ -n "$line" ]; then
val="${line#*:}"
val="$(echo "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -n "$val" ]; then
skin_header=$(sanitize_filename "$val")
fi
fi
fi
raw_path="${skin}/${skin_header}"
base_path=$(url_encode_path "$raw_path")
osk_url="$REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/${base_path}.osk"
md_file_path="${DOC_DIR}/${raw_path}.md"
mkdir -p "$(dirname "$md_file_path")"
video_url="$REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/media/gameplay/${base_path}.mp4"
author=""
if [ -f "$ini_file" ]; then
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n1 || true)
if [ -n "$author_line" ]; then
author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
fi
fi
{
echo "# [$skin_header]($osk_url)"
echo ""
[ -n "$author" ] && echo "**Author:** $author"
[ -n "$author" ] && echo ""
echo "## Hitsounds"
echo "<video controls autoplay loop muted playsinline src=\"$video_url\" type=\"video/mp4\">"
echo "</video>"
echo ""
echo "## Ranking Panel"
echo "![](/media/panel/${base_path}.webp)"
echo ""
echo "## Mod Icons"
echo "![](/media/icons/${base_path}-mod-icons.webp)"
echo ""
echo "## Build History"
echo ""
echo "| Version | Date |"
echo "| ------- | ---- |"
current_commit_date=$(TZ="Europe/Zurich" date -d "$(git log -1 --format=%cI)" "+%d.%m.%Y %H:%M:%S")
echo "| [\`$new_tag (Current)\`]($REGISTRY_URL/$USER_REPOSITORY/src/tag/$new_tag/docs/${base_path}.md) | $current_commit_date |"
old_tags=$(git tag --sort=-v:refname | grep -v "^$new_tag$" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true)
if [ -n "$old_tags" ]; then
echo "$old_tags" | while read -r tag; do
raw_osk_path="export/${skin}/${skin_header}.osk"
if git ls-tree -r --name-only "$tag" | grep -Fx -- "$raw_osk_path" >/dev/null; then
tag_date=$(git log -1 --format=%ci "$tag")
formatted_date=$(TZ="Europe/Zurich" date -d "$tag_date" "+%d.%m.%Y %H:%M:%S")
echo "| [\`$tag\`]($REGISTRY_URL/$USER_REPOSITORY/src/tag/$tag/docs/${base_path}.md) | $formatted_date |"
fi
done
fi
} > "$md_file_path"
echo " → Wrote $md_file_path"
done
echo "Per-skin markdown pages complete."
- name: Cleanup Extra Files - name: Cleanup Extra Files
shell: bash shell: bash
@@ -707,8 +891,9 @@ jobs:
set -euo pipefail set -euo pipefail
echo "[Cleanup Extra Files Started]" echo "[Cleanup Extra Files Started]"
[ -d src/docs ] && rm -rf src/docs
[ -f how-to-use.md ] && rm -f how-to-use.md [ -f how-to-use.md ] && rm -f how-to-use.md
[ -f src/replay.osr ] && rm -f src/replay.osr
[ -d src/default-skin ] && rm -rf src/default-skin
readarray -t skins <<< "$ALL_SKINS_DIR" readarray -t skins <<< "$ALL_SKINS_DIR"
@@ -719,7 +904,34 @@ jobs:
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
} }
for root in "$REPO_SCREENSHOT_DIR" "$REPO_RANKING_PANEL_DIR" "$REPO_MOD_ICONS_DIR" "$OSK_PATH"; do prune_dir() {
local root="$1"
local skin="$2"
local expected="$3"
for f in "$root"/*; do
[ -f "$f" ] || continue
name="$(basename "$f")"
if printf '%s\n' "${skins[@]}" | grep -Fxq -- "$name"; then
continue
fi
echo " → Removing unexpected root file: $f"
rm -f "$f"
done
dir="$root/$skin"
[ -d "$dir" ] || return
for f in "$dir"/*; do
[ -e "$f" ] || continue
if [[ "$(basename "$f")" != "$expected" ]]; then
echo " → Removing unexpected file: $f"
rm -f "$f"
fi
done
}
for root in "$REPO_SCREENSHOT_DIR" "$REPO_RANKING_PANEL_DIR" "$REPO_MOD_ICONS_DIR" "$REPO_THUMBNAIL_DIR" "$OSK_PATH" "$DOC_DIR"; do
[ -d "$root" ] || continue
for dir in "$root"/*; do for dir in "$root"/*; do
[ -d "$dir" ] || continue [ -d "$dir" ] || continue
name="$(basename "$dir")" name="$(basename "$dir")"
@@ -736,34 +948,17 @@ jobs:
if [[ -f "$ini" ]]; then if [[ -f "$ini" ]]; then
raw=$(grep -i '^[[:space:]]*Name:' "$ini" | head -n1 || true) raw=$(grep -i '^[[:space:]]*Name:' "$ini" | head -n1 || true)
raw="${raw#*:}" raw="${raw#*:}"
raw="$(echo "$raw" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
tmp_header=$(sanitize_filename "$raw") tmp_header=$(sanitize_filename "$raw")
if [[ -n "$tmp_header" ]]; then [[ -n "$tmp_header" ]] && header="$tmp_header"
header="$tmp_header"
fi
fi fi
expect_gif="$header.gif" prune_dir "$REPO_SCREENSHOT_DIR" "$skin" "$header.mp4"
expect_png="$header.png" prune_dir "$REPO_RANKING_PANEL_DIR" "$skin" "$header.webp"
expect_icon="$header-mod-icons.png" prune_dir "$REPO_MOD_ICONS_DIR" "$skin" "$header-mod-icons.webp"
expect_osk="$header.osk" prune_dir "$REPO_THUMBNAIL_DIR" "$skin" "$header.webp"
prune_dir "$OSK_PATH" "$skin" "$header.osk"
prune_dir() { prune_dir "$DOC_DIR" "$skin" "$header.md"
root=$1; expected=$2
dir="$root/$skin"
[ ! -d "$dir" ] && return
for f in "$dir"/*; do
[ -e "$f" ] || continue
if [[ "$(basename "$f")" != "$expected" ]]; then
echo " → Removing unexpected file: $f"
rm -f "$f"
fi
done
}
prune_dir "$REPO_SCREENSHOT_DIR" "$expect_gif"
prune_dir "$REPO_RANKING_PANEL_DIR" "$expect_png"
prune_dir "$REPO_MOD_ICONS_DIR" "$expect_icon"
prune_dir "$OSK_PATH" "$expect_osk"
done done
echo "[Cleanup Extra Files Complete]" echo "[Cleanup Extra Files Complete]"
@@ -779,7 +974,7 @@ jobs:
run: | run: |
git config advice.addIgnoredFile false git config advice.addIgnoredFile false
for p in media/gameplay media/panel media/icons export README.md how-to-use.md src; do for p in docs/ media/gameplay media/thumbnail media/panel media/icons export README.md how-to-use.md src; do
if [ -e "$p" ]; then if [ -e "$p" ]; then
git add -A "$p" git add -A "$p"
fi fi

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/count1.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/count2.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/count3.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/cursor.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/go.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/hit0.png LFS Normal file

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/hit100.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/hit300.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Skins/#WhatTheDOGdoin#/hit50.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More