Initial commit

This commit is contained in:
Skellers
2025-06-06 11:52:45 +02:00
commit 0c81440017
656 changed files with 2864 additions and 0 deletions

15
.gitattributes vendored Normal file
View File

@@ -0,0 +1,15 @@
*.db filter=lfs diff=lfs merge=lfs -text
*.exe filter=lfs diff=lfs merge=lfs -text
*.gif 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
*.ogg 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
*.pdn filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.WAV filter=lfs diff=lfs merge=lfs -text

557
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,557 @@
name: CI/CD Pipeline
on:
push:
branches:
- main
paths:
- '.gitea/workflows/ci.yml'
- 'Skins/**/*'
workflow_dispatch:
env:
DANSER_PATH: "/app/danser/danser-cli"
DANSER_DIR: "/app/danser"
DANSER_VIDEO_DIR: "/app/danser/videos"
DANSER_SCREENSHOT_DIR: "/app/danser/screenshots"
SKINS_DIR: "${{ github.workspace }}/Skins"
DANSER_SKINS_DIR: "/app/danser/skins"
DEFAULT_SKIN_DIR: "${{ github.workspace }}/src/default-skin"
REPO_SCREENSHOT_DIR: "${{ github.workspace }}/media/gameplay"
REPO_MOD_ICONS_DIR: "${{ github.workspace }}/media/icons"
REPO_RANKING_PANEL_DIR: "${{ github.workspace }}/media/panel"
SETTINGS_JSON_PATH: "/app/danser/settings/default.json"
README_PATH: "${{ github.workspace }}/README.md"
REPLAY_PATH: "${{ github.workspace }}/src/replay.osr"
OSK_PATH: "${{ github.workspace }}/export"
IMAGE_NAME: arlind/skins
REGISTRY_URL: "https://${{ secrets.CONTAINER_REGISTRY }}"
jobs:
generate_everything:
name: Full CI/CD Pipeline
runs-on: ubuntu-latest
container:
image: ${{ secrets.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
options: >-
--gpus all
--privileged
--security-opt seccomp=unconfined
--security-opt apparmor=unconfined
--cap-add=ALL
--env NVIDIA_DRIVER_CAPABILITIES=all
--env NVIDIA_VISIBLE_DEVICES=all
--user 0:0
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}
- name: Git LFS Pull
run: |
echo "Pulling Git LFS files..."
git lfs pull
echo "LFS files pulled."
- name: Extract Repository path
run: |
FULL_WORKSPACE_PATH="${{ github.workspace }}"
USER_REPOSITORY="${FULL_WORKSPACE_PATH#/workspace/}"
USER_REPOSITORY="${USER_REPOSITORY%/}"
echo "USER_REPOSITORY=$USER_REPOSITORY" >> $GITHUB_ENV
- name: Set XDG_RUNTIME_DIR
run: |
echo "Setting XDG_RUNTIME_DIR..."
mkdir -p /tmp/xdg_runtime_dir
chmod 0700 /tmp/xdg_runtime_dir
echo "XDG_RUNTIME_DIR=/tmp/xdg_runtime_dir" >> "$GITHUB_ENV"
echo "XDG_RUNTIME_DIR set."
- name: Create directories for assets
run: |
echo "Creating asset directories..."
mkdir -p "$REPO_SCREENSHOT_DIR"
mkdir -p "$REPO_MOD_ICONS_DIR"
mkdir -p "$REPO_RANKING_PANEL_DIR"
mkdir -p "$OSK_PATH"
echo "Asset directories created successfully."
- name: Create New Tag
run: |
echo "Computing new tag..."
git fetch --tags >/dev/null 2>&1
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1) 2>/dev/null || echo "")
if [ -z "$latest_tag" ]; then
new_tag="v1.0.0"
else
version=${latest_tag#v}
major=$(echo "$version" | cut -d. -f1)
minor=$(echo "$version" | cut -d. -f2)
patch=$(echo "$version" | cut -d. -f3)
minor=$((minor + 1))
patch=0
new_tag="v${major}.${minor}.${patch}"
fi
echo "new_tag=$new_tag" >> $GITHUB_ENV
echo "Computed new tag: $new_tag"
- name: Move Skin files to Danser Skins directory
run: |
echo "Moving Skin files to Danser Skins directory..."
mkdir -p "$DANSER_SKINS_DIR"
mv "$SKINS_DIR"/* "$DANSER_SKINS_DIR"
echo "Skin files moved."
- name: Generate Danser videos and screenshots
run: |
echo "[Danser Job Started]"
SKIN_COUNT=$(find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
INDEX=1
for skin in "$DANSER_SKINS_DIR"/*/; do
if [ -d "$skin" ]; then
SKIN_NAME=$(basename "$skin")
echo ""
echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME"
LOGFILE="/tmp/danser_log_$SKIN_NAME.txt"
FFMPEG_LOG="/tmp/ffmpeg_log_$SKIN_NAME.txt"
echo " → Generating video..."
if ! xvfb-run -a "$DANSER_DIR/danser-cli" -replay "$REPLAY_PATH" -record -skip -start=215 -end=230 -noupdatecheck -out="$SKIN_NAME" -skin "$SKIN_NAME" >"$LOGFILE" 2>&1; then
echo " ✖ Video generation failed for $SKIN_NAME. Log output:"
cat "$LOGFILE"
exit 1
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 generation failed for $SKIN_NAME. Log output:"
cat "$LOGFILE"
exit 1
fi
if [ -f "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" ]; then
echo " → Converting to GIF..."
if ! ffmpeg -y -hwaccel cuda -ss 4 -t 10 -i "$DANSER_VIDEO_DIR/$SKIN_NAME.mp4" \
-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" \
-c:v gif "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" >"$FFMPEG_LOG" 2>&1; then
echo " ✖ FFmpeg conversion failed for $SKIN_NAME. Log output:"
cat "$FFMPEG_LOG"
exit 1
fi
mv "$DANSER_VIDEO_DIR/$SKIN_NAME.gif" "$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif"
fi
if [ -f "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" ]; then
mv "$DANSER_SCREENSHOT_DIR/$SKIN_NAME.png" "$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png"
fi
echo " ✓ Completed"
INDEX=$((INDEX + 1))
fi
done
echo ""
echo "[Danser Job Finished — $SKIN_COUNT skins processed]"
- name: Rename Generated Assets Based on skin.ini
run: |
echo "[Asset Renaming Started]"
INDEX=1
SKIN_COUNT=$(find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
for skin_path in "$DANSER_SKINS_DIR"/*/; do
if [ -d "$skin_path" ]; then
SKIN_NAME=$(basename "$skin_path")
echo ""
echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME"
ini_file=$(find "$skin_path" -maxdepth 1 -iname "skin.ini" | head -n1)
skin_header="$SKIN_NAME"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1)
if [ -n "$name_line" ]; then
new_name=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -n "$new_name" ] && skin_header="$new_name"
fi
fi
original_gif="$REPO_SCREENSHOT_DIR/$SKIN_NAME.gif"
renamed_gif="$REPO_SCREENSHOT_DIR/$skin_header.gif"
if [ -f "$original_gif" ] && [ "$original_gif" != "$renamed_gif" ]; then
mv -f "$original_gif" "$renamed_gif"
echo " ✓ Renamed GIF"
else
echo " → No GIF to rename or already named correctly"
fi
original_png="$REPO_RANKING_PANEL_DIR/$SKIN_NAME.png"
renamed_png="$REPO_RANKING_PANEL_DIR/$skin_header.png"
if [ -f "$original_png" ] && [ "$original_png" != "$renamed_png" ]; then
mv -f "$original_png" "$renamed_png"
echo " ✓ Renamed PNG"
else
echo " → No PNG to rename or already named correctly"
fi
echo " ✓ Completed"
INDEX=$((INDEX + 1))
fi
done
echo ""
echo "[Asset Renaming Complete — $SKIN_COUNT skins processed]"
- name: Generate Mod Icons
run: |
echo "[Mod Icon Generation Started]"
ICONS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/icons.json"
group1_icons=$(jq -r '.group1 | join(" ")' "$ICONS_JSON_FILE")
group2_icons=$(jq -r '.group2 | join(" ")' "$ICONS_JSON_FILE")
group3_icons=$(jq -r '.group3 | join(" ")' "$ICONS_JSON_FILE")
SKIN_COUNT=$(find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
INDEX=1
for skin_path in "$DANSER_SKINS_DIR"/*/; do
if [ -d "$skin_path" ]; then
SKIN_NAME=$(basename "$skin_path")
echo ""
echo "[$INDEX/$SKIN_COUNT] Skin: $SKIN_NAME"
ini_file=$(find "$skin_path" -maxdepth 1 -iname "skin.ini" | head -n1)
skin_header="$SKIN_NAME"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1)
if [ -n "$name_line" ]; then
new_name=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$new_name" ]; then
skin_header="$new_name"
fi
fi
fi
ICON_FOLDER="$skin_path"
OUTPUT="${REPO_MOD_ICONS_DIR}/${skin_header}-mod-icons.png"
TILE_SIZE=160
PADDING=10
MAX_ICONS=7
BLANK_IMAGE="blank.png"
magick -size "${TILE_SIZE}x${TILE_SIZE}" xc:none "$BLANK_IMAGE"
row_images=""
row_index=1
for group_list in "$group1_icons" "$group2_icons" "$group3_icons"; do
montage_files=""
count=0
for icon in $group_list; do
icon_path="${ICON_FOLDER}/selection-mod-${icon}@2x.png"
if [ -f "$icon_path" ]; then
montage_files="$montage_files \"$icon_path\""
count=$((count + 1))
elif [ -f "$DEFAULT_SKIN_DIR/selection-mod-${icon}@2x.png" ]; then
montage_files="$montage_files \"$DEFAULT_SKIN_DIR/selection-mod-${icon}@2x.png\""
count=$((count + 1))
fi
done
missing=$(( MAX_ICONS - count ))
if [ "$missing" -lt 0 ]; then
missing=0
fi
i=0
while [ "$i" -lt "$missing" ]; do
montage_files="$montage_files \"$BLANK_IMAGE\""
i=$((i + 1))
done
row_file="row_${row_index}.png"
eval "magick montage $montage_files -tile \"${MAX_ICONS}x1\" -geometry \"${TILE_SIZE}x${TILE_SIZE}+${PADDING}+${PADDING}\" -background none \"$row_file\""
row_images="$row_images \"$row_file\""
row_index=$((row_index + 1))
done
num_rows=0
for _ in $row_images; do
num_rows=$((num_rows + 1))
done
eval "magick montage $row_images -tile \"1x${num_rows}\" -geometry \"+${PADDING}+${PADDING}\" -background none \"$OUTPUT\""
rm "$BLANK_IMAGE"
rm row_*.png
echo " ✓ Completed"
INDEX=$((INDEX + 1))
fi
done
echo ""
echo "[Mod Icon Generation Finished — $SKIN_COUNT skins processed]"
- name: Create OSK files
run: |
echo "[OSK Creation Job Started]"
SKIN_COUNT=$(find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
INDEX=1
FIXED_TIMESTAMP="2025-01-01 00:00:00"
find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skin; do
SKIN_FOLDER=$(basename "$skin")
echo ""
echo "[$INDEX/$SKIN_COUNT] Processing skin folder: $SKIN_FOLDER"
ini_file=$(find "$skin" -maxdepth 1 -iname "skin.ini" | head -n1)
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n1)
if [ -n "$name_line" ]; then
new_name=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$new_name" ]; then
SKIN_FOLDER="$new_name"
fi
fi
else
echo " → No skin.ini found, using folder name."
fi
osk_file="${OSK_PATH}/${SKIN_FOLDER}.osk"
if ! (cd "$skin" && find . -type f -exec touch -d "$FIXED_TIMESTAMP" {} +); then
echo " ✖ Failed to normalize timestamps in $skin"
exit 1
fi
if (
cd "$skin" && \
find . -type f | sort | zip -rq -D -X -9 --compression-method deflate "$osk_file" -@
); then
echo " ✓ OSK file created successfully."
else
echo " ✖ Failed to create OSK file: $osk_file"
exit 1
fi
INDEX=$((INDEX + 1))
done
echo ""
echo "[OSK Creation Job Finished — $SKIN_COUNT skins processed]"
- name: Generate README
run: |
echo "Starting README generation..."
SKINS_JSON_FILE="${{ github.workspace }}/.gitea/workflows/skins.json"
DESC_FILE=$(mktemp)
jq -r '.descriptions | to_entries[] | "\(.key)=\(.value)"' "$SKINS_JSON_FILE" > "$DESC_FILE"
echo "---" > "$README_PATH"
echo "gitea: none" >> "$README_PATH"
echo "include_toc: true" >> "$README_PATH"
echo "---" >> "$README_PATH"
echo "" >> "$README_PATH"
echo "# Skins" >> "$README_PATH"
echo "" >> "$README_PATH"
echo "This README is automatically generated using CI/CD pipelines, Danser, ImageMagick, and FFmpeg. The workflow generates gameplay previews, creates mod icons, packages .osk files, and updates the README with media and descriptions, with the Skins included in [Skins](./Skins)." >> "$README_PATH"
echo "" >> "$README_PATH"
get_desc() {
key=$1
escaped_key=$(printf '%s\n' "$key" | sed 's/[\/&]/\\&/g')
grep "^${escaped_key}=" "$DESC_FILE" | cut -d '=' -f2-
}
ORDER_FILE=$(mktemp)
JSON_SKINS_TMP=$(mktemp)
SEEN_HEADERS_FILE=$(mktemp)
jq -r '.order[]' "$SKINS_JSON_FILE" > "$ORDER_FILE"
cp "$ORDER_FILE" "$JSON_SKINS_TMP"
while IFS= read -r skin; do
dir="$DANSER_SKINS_DIR/$skin"
if [ ! -d "$dir" ]; then
echo "Skipping missing skin directory: $skin"
continue
fi
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n 1)
skin_header="$skin"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n 1)
if [ -n "$name_line" ]; then
skin_header=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
fi
fi
if grep -Fxq "$skin_header" "$SEEN_HEADERS_FILE"; then
echo "Skipping duplicate skin header from JSON order: $skin_header"
continue
fi
echo "$skin_header" >> "$SEEN_HEADERS_FILE"
escaped_img=$(echo "$skin_header.gif" | sed 's/ /%20/g')
escaped_osk=$(echo "$skin_header.osk" | sed 's/ /%20/g')
echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/$escaped_osk)" >> "$README_PATH"
echo "" >> "$README_PATH"
skin_desc=$(get_desc "$skin")
if [ -n "$skin_desc" ]; then
echo "$skin_desc" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
if [ -f "$ini_file" ]; then
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n 1 || true)
if [ -n "$author_line" ]; then
author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$author" ]; then
echo "**Author:** $author" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
fi
fi
echo "![$skin_header Gameplay](media/gameplay/$escaped_img)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "media/panel/${skin_header}.png" ]; then
escaped_panel=$(echo "${skin_header}.png" | sed 's/ /%20/g')
echo "![$skin_header Ranking Panel](media/panel/$escaped_panel)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
mod_icon_file="${skin_header}-mod-icons.png"
if [ -f "media/icons/$mod_icon_file" ]; then
escaped_mod=$(echo "$mod_icon_file" | sed 's/ /%20/g')
echo "![$skin_header Mods](media/icons/$escaped_mod)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
done < "$ORDER_FILE"
find "$DANSER_SKINS_DIR" -mindepth 1 -maxdepth 1 -type d | while IFS= read -r dir; do
skin=$(basename "$dir")
ini_file=$(find "$dir" -maxdepth 1 -iname "skin.ini" | head -n 1)
skin_header="$skin"
if [ -f "$ini_file" ]; then
name_line=$(grep -i '^[[:space:]]*name:' "$ini_file" | head -n 1)
if [ -n "$name_line" ]; then
skin_header=$(echo "$name_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
fi
fi
if grep -Fxq "$skin_header" "$SEEN_HEADERS_FILE"; then
continue
fi
if grep -Fxq "$skin" "$JSON_SKINS_TMP"; then
continue
fi
echo "$skin_header" >> "$SEEN_HEADERS_FILE"
escaped_img=$(echo "$skin_header.gif" | sed 's/ /%20/g')
escaped_osk=$(echo "$skin_header.osk" | sed 's/ /%20/g')
echo "## [$skin_header]($REGISTRY_URL/$USER_REPOSITORY/media/tag/$new_tag/export/$escaped_osk)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "$ini_file" ]; then
author_line=$(grep -i '^[[:space:]]*Author:' "$ini_file" | head -n 1 || true)
if [ -n "$author_line" ]; then
author=$(echo "$author_line" | cut -d ':' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$author" ]; then
echo "**Author:** $author" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
fi
fi
echo "![$skin_header Gameplay](media/gameplay/$escaped_img)" >> "$README_PATH"
echo "" >> "$README_PATH"
if [ -f "media/panel/${skin_header}.png" ]; then
escaped_panel=$(echo "${skin_header}.png" | sed 's/ /%20/g')
echo "![$skin_header Ranking Panel](media/panel/$escaped_panel)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
mod_icon_file="${skin_header}-mod-icons.png"
if [ -f "media/icons/$mod_icon_file" ]; then
escaped_mod=$(echo "$mod_icon_file" | sed 's/ /%20/g')
echo "![$skin_header Mods](media/icons/$escaped_mod)" >> "$README_PATH"
echo "" >> "$README_PATH"
fi
done
rm "$DESC_FILE" "$ORDER_FILE" "$JSON_SKINS_TMP" "$SEEN_HEADERS_FILE"
echo "# Build History" >> "$README_PATH"
echo "" >> "$README_PATH"
echo "| Version | Date |" >> "$README_PATH"
echo "| ------- | ---- |" >> "$README_PATH"
current_commit_date=$(TZ="Europe/Zurich" date -d "$(git log -1 --format=%cI)" "+%d.%m.%Y %H:%M:%S")
echo "| [\`$new_tag (Current)\`](https://git.sulejmani.xyz/arlind/skins/src/tag/$new_tag/README.md) | $current_commit_date |" >> "$README_PATH"
git tag --sort=-v:refname | grep -v "^$new_tag$" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | while read -r tag; do
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\`](https://git.sulejmani.xyz/arlind/skins/src/tag/$tag/README.md) | $formatted_date |" >> "$README_PATH"
done
echo "README generation completed."
- name: Configure Git
run: |
echo "Configuring git user and LFS..."
git config user.email "arlind@sulej.ch"
git config user.name "ci-bot"
git config lfs.https://${{ secrets.CONTAINER_REGISTRY }}/arlind/skins.git/info/lfs.locksverify true
echo "Git configured."
- name: Add and Commit changes
run: |
git config advice.addIgnoredFile false
echo "Staging files for commit..."
git add README.md media/gameplay/* media/panel/* media/icons/* export/*
echo "Committing changes..."
git commit -m "[ci skip] push back from pipeline" -q || echo "No changes to commit"
echo "Commit step completed."
- name: Push changes and create tag
run: |
echo "Checking branch and pushing changes..."
if [ "${GITHUB_REF}" = "refs/heads/main" ]; then
echo "On main branch: pushing to origin main..."
git push origin HEAD:main || echo "No changes to push"
echo "Creating and pushing tag $new_tag..."
git tag "$new_tag"
git push origin "$new_tag"
else
echo "On branch ${GITHUB_REF_NAME}: pushing to origin ${GITHUB_REF_NAME}..."
git push origin HEAD:"${GITHUB_REF_NAME}" || echo "No changes to push"
fi
echo "Push step completed."

View File

@@ -0,0 +1,25 @@
{
"group1": [
"easy",
"nofail",
"halftime"
],
"group2": [
"hardrock",
"suddendeath",
"perfect",
"doubletime",
"nightcore",
"hidden",
"flashlight"
],
"group3": [
"relax",
"relax2",
"autoplay",
"target",
"spunout",
"cinema",
"scorev2"
]
}

View File

@@ -0,0 +1,10 @@
{
"order": [
"example1",
"example2"
],
"descriptions": {
"example1": "Description of example1",
"example2": "Description of example2"
}
}

0
.gitignore vendored Normal file
View File

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Arlind-dev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

15
hardlink-songs-folder.bat Normal file
View File

@@ -0,0 +1,15 @@
@echo off
net session >nul 2>&1
if %errorLevel% neq 0 (
echo Requesting admin privileges...
powershell -Command "Start-Process cmd -ArgumentList '/c \"%~f0\"' -Verb RunAs"
exit /b
)
echo Running robocopy sync as Administrator...
echo.
robocopy "E:\osu!\skins" "D:\git\skins\Skins" /MIR /COPYALL /SEC /B /XJ /DCOPY:T /J
echo Robocopy sync completed.
pause

208
how-to-use.md Normal file
View File

@@ -0,0 +1,208 @@
---
gitea: none
include_toc: true
---
# How to Use This Repository
If anything is unclear, just shoot me a message on Discord.
## 1. Create Your Account on git.sulejmani.xyz
(If you don't want to share your mail with me DM me on Discord)
Sign up here:
[https://git.sulejmani.xyz/user/sign\_up](https://git.sulejmani.xyz/user/sign_up)
![Register](/src/docs/register.png)
Check your email and click the confirmation link to activate your account.
![Check Mail](/src/docs/check_mail.png)
![Activation Email](/src/docs/activation_mail.png)
Then confirm your password to complete the process.
![Activate Account](/src/docs/activate_account.png)
---
## 2. Generate an Access Token
Go to [https://git.sulejmani.xyz/](https://git.sulejmani.xyz/) and open your **Settings**.
![Navigate Settings](/src/docs/navigate_settings.png)
Under the **Applications** section, create a new token.
* Check all permissions (as shown in the image)
* Give it a clear and recognizable name
![Generate Token](/src/docs/applications_generate_token.png)
Once the token is generated, **copy and save it securely**, you wont be able to see it again.
![Copy Token](/src/docs/copy_token.png)
---
## 3. Create a New Repository
Still on [https://git.sulejmani.xyz/](https://git.sulejmani.xyz/), click the **+** icon in the top right corner to create a new repo.
![Start Creating New Repo](/src/docs/start_create_new_repo.png)
* Name it however you like
* Use `skins-template` as the template
* Enable all template items
You now have your own copy of the template repo.
![Template Repo](/src/docs/template_repo.png)
---
## 4. Set Up Secrets for Deployment
Go to your repository's **Settings**.
![Navigate Repo Settings](/src/docs/navigate_repository_settings.png)
Then under **Actions** open the **Secrets** tab and add these two:
| Secret Name | Secret Value |
| -------------------- | -------------------------- |
| `TOKEN` | The token you just created |
| `CONTAINER_REGISTRY` | `git.sulejmani.xyz` |
![Add Secret 1](/src/docs/secret1.png)
![Add Secret 2](/src/docs/secret2.png)
---
## 5. Clone the Repository
If you dont have Git installed, download it here:
👉 [https://git-scm.com/downloads/win](https://git-scm.com/downloads/win)
Choose where to clone the repo (e.g. `D:/git/`) and run:
```bash
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git lfs install
git clone https://git.sulejmani.xyz/{yourusername}/{yourrepositoryname}.git
```
---
## 6. Prepare Your Skins
In your cloned repo, open `hardlink-songs-folder.bat`.
Edit the source and target paths. For example, if:
* Your osu! skins are in `E:\osu!\skins`
* Your repo is in `D:\git\skins`
Then update the line on the script like this:
```bat
robocopy "E:\osu!\skins" "D:\git\skins\Skins" /MIR /COPYALL /SEC /B /XJ /DCOPY:T /J
```
Run the batch file by double-clicking it.
---
## 7. Change Skin Order, add descriptions, and ignore Skins.
### Skin Order
This is optional, but recommended. In the `workflows` folder, open `skins.json`.
Edit the order and descriptions as you like.
here's an example:
```json
{
"order": [
"- Barely still Jace",
"- Jace"
],
"descriptions": {
"- Barely still Jace": "My main skin.",
"- Jace": "My secondary skin, only really used for EZ."
}
}
```
Make sure the order matches the skins folder name in the `Skins` folder.
### Ignore Skins
There is a .gitignore file in the root of the repo. You can add any skins you don't want to be uploaded.
Here's an example:
```
*WhiteCat*
*Vaxei*
```
So in this example if a Skin has WhiteCat or Vaxei in the name, it will be ignored.
---
## 8. Upload Your Skins
Open a terminal (cmd or PowerShell) in your repo directory:
```cmd
cd D:\git\skins # update this to your actual path
```
Then run:
```bash
git add .
git commit -m "Adding skins"
git config --global credential.helper cache
git push
```
Your skins will now be uploaded.
---
## 9. Monitor Upload Progress
Go to your repo's **Actions** tab.
![navigate-actions](/src/docs/navigate_actions.png)
Youll see the workflow progress there.
![progress](/src/docs/progress.png)
If something goes wrong, check the logs—or just ping me on Discord.
---
## 10. Update Skins in the Future
To update your skins later:
1. Re-run `hardlink-songs-folder.bat`
2. Run the same upload commands:
```bash
git pull
git add .
git commit -m "Updating skins"
git push
```

6
rsync.sh Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
rsync -av --delete /mnt/e/osu\!/Skins/ /home/nixos/git/skins/Skins/
find ./Skins/ -type f ! -perm 644 -exec chmod 644 {} +
find ./Skins/ -type d ! -perm 755 -exec chmod 755 {} +

View File

@@ -0,0 +1,7 @@
All osu! graphic and other game assets are copyright ppy Pty Ltd.
You are granted permission to use the elements contained within this archive as a template for creating your own skins. You can modify these in any way you see fit. Please do not include the resources if you haven't modified them; the defaults will automatically be used in this case.
You are NOT permitted to use these graphics outside of skins and/or beatmaps. This includes using them on other websites, games, products etc.
If you would like to use the resources outside of the scope provided above, please contact me at pe@ppy.sh

View File

@@ -0,0 +1,27 @@
About this skin :
=======================
Template by Corne2Plum3 (https://osu.ppy.sh/users/15646039).
This is the just the default skin from the stable version (no target practice sprites sorry...), but 2021 version (from the 20210821 version to be exact). I tried to make the default skin but updated (the last I found was from 2014...), and complete (I added almost missing files) where skinners use it as a template to create their skin.
Most of the sprites and gameplay sounds are from ppy/osu-ressources (https://github.com/ppy/osu-resources). Sometimes I used an AI to generates the @2x versions of some files. For the sounds, I tried to pick them directly from the game, so sorry if it's not accurate...
Please don't kill me for making this, peppy!
A few notes :
=============
BACK BUTTON:
It's impossible to recreate the back button. In this skin, there is just 1 frame of the back button. Unlike the default, he's isn't animated, and doesn't change according to the game language.
COMBO SPRITES:
Same than score sprites.
SLIDERBALL:
It's IMPOSSIBLE to recreate exactly the default sliderball with skinning, because sliderb files disable sliderb-spec (overlay) and sliderb-nd (background). So you will see only sliderb files in game with this skin.
The only way to have the same sliderball than the default skin is deleting ALL slider ball files (sliderb, sliderb-spec and sliderb-nd).
SPINNER APPROACH CIRCLE:
With the default skin which uses new spinner, spinner-approachcircle is invisible. In the folder 'new spinner/visible approach circle' you have the default spinner-approachcircle if this file is needed.

BIN
src/default-skin/applause.wav 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.

BIN
src/default-skin/button-left.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/button-right.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/check-off.wav LFS Normal file

Binary file not shown.

BIN
src/default-skin/check-on.wav LFS Normal file

Binary file not shown.

BIN
src/default-skin/click-close.wav LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/click-short.wav LFS Normal file

Binary file not shown.

BIN
src/default-skin/combobreak.wav LFS Normal file

Binary file not shown.

BIN
src/default-skin/comboburst.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/count.wav LFS Normal file

Binary file not shown.

BIN
src/default-skin/count1.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/count1@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/count2.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/count2@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/count3.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/count3@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/cursor-smoke.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/cursor.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/cursor@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/cursormiddle.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/cursortrail.png LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/default-0.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-0@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-1.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-1@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-2.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-2@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-3.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-3@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-4.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-4@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-5.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-5@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-6.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-6@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-7.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-7@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-8.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-8@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-9.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/default-9@2x.png LFS Normal file

Binary file not shown.

BIN
src/default-skin/drum-hitclap.wav 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.

BIN
src/default-skin/failsound.mp3 LFS Normal file

Binary file not shown.

BIN
src/default-skin/followpoint.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/fruit-apple.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.

BIN
src/default-skin/fruit-drop.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/default-skin/fruit-grapes.png LFS Normal file

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