Compare commits
	
		
			3 Commits
		
	
	
		
			a356f4c003
			...
			ef4c0f8747
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ef4c0f8747 | ||
|  | 4ab5b3a2f1 | ||
|  | 323af82393 | 
							
								
								
									
										6
									
								
								.bashrc
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								.bashrc
									
									
									
									
									
								
							| @@ -1,6 +0,0 @@ | ||||
| alias dlmusic='MUSIC_PATH="/mnt/media/music/evilspins/$(date +"%Y")" && mkdir -p $MUSIC_PATH && NOW=$(date +"%Y%m%d%H") && pip install -U yt-dlp && yt-dlp -f bestaudio -x --audio-format mp3 --audio-quality 0 -o $MUSIC_PATH"/"$NOW"__%(title)s__%(artist)s__%(id)s.%(ext)s" $1' | ||||
| alias dlvideo='VIDEO_PATH="/mnt/media/video/autre" && cd "$VIDEO_PATH" && pip install -U yt-dlp && yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4" --merge-output-format mp4 "$@"' | ||||
| alias dlfilm='VIDEO_PATH="/mnt/media/video/film" && yt-dlp -o $VIDEO_PATH $1' | ||||
| alias dlimage='IMAGE_PATH="/mnt/media/image/screenshit" && wget -P $IMAGE_PATH $1'  | ||||
| alias search='find ./ -iname "*$1*"' | ||||
| alias searchinside='grep -rwl "*$1*" ./' | ||||
							
								
								
									
										15
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| { | ||||
|   // Use IntelliSense to learn about possible attributes. | ||||
|   // Hover to view descriptions of existing attributes. | ||||
|   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|   "version": "0.2.0", | ||||
|   "configurations": [ | ||||
|     { | ||||
|       "type": "chrome", | ||||
|       "request": "launch", | ||||
|       "name": "Launch Chrome against localhost", | ||||
|       "url": "http://localhost:8080", | ||||
|       "webRoot": "${workspaceFolder}" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										246
									
								
								adb-music-sync/adb-music-sync.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								adb-music-sync/adb-music-sync.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| import shlex | ||||
| import subprocess | ||||
| import tempfile | ||||
| from pathlib import Path | ||||
| from typing import List, Tuple, Optional | ||||
|  | ||||
| # --- Configuration --- | ||||
| HOME = Path.home() | ||||
| SOURCES = [ | ||||
|     HOME / "Music", | ||||
|     HOME / "Downloads", | ||||
| ] | ||||
| DEST_ON_PHONE = "/sdcard/Music" | ||||
| # Model tag as seen in `adb devices -l` (e.g., ... model:AGM_M7 ...) | ||||
| ADB_MODEL_MATCH = ["AGM_M7", "AGM", "M7"] | ||||
| POLL_INTERVAL_SEC = 60 | ||||
| SIZE_LIMIT_BYTES = 7 * 1024 ** 3  # 7 GiB | ||||
| MUSIC_EXTS = {".mp3", ".m4a", ".ogg", ".aac", ".opus", ".alac", ".flac"} | ||||
|  | ||||
| # If you have a static Wi-Fi ADB endpoint, set it here (e.g., "192.168.1.50:5555"). | ||||
| ADB_IP_PORT: Optional[str] = None | ||||
|  | ||||
| # --- Helpers --- | ||||
| def run(cmd: List[str], check: bool = False, text: bool = True, timeout: Optional[int] = None) -> subprocess.CompletedProcess: | ||||
|     try: | ||||
|         return subprocess.run(cmd, check=check, text=text, capture_output=True, timeout=timeout) | ||||
|     except Exception as e: | ||||
|         return subprocess.CompletedProcess(cmd, returncode=1, stdout="", stderr=str(e)) | ||||
|  | ||||
|  | ||||
| def ensure_adb_server() -> None: | ||||
|     run(["adb", "start-server"])  # best-effort | ||||
|  | ||||
|  | ||||
| def list_adb_devices() -> List[str]: | ||||
|     proc = run(["adb", "devices", "-l"])  # do not check, we parse regardless | ||||
|     if proc.returncode != 0: | ||||
|         return [] | ||||
|     lines = [ln.strip() for ln in proc.stdout.splitlines()] | ||||
|     # skip header line: "List of devices attached" | ||||
|     if lines and lines[0].lower().startswith("list of devices"): | ||||
|         lines = lines[1:] | ||||
|     return [ln for ln in lines if ln] | ||||
|  | ||||
|  | ||||
| def adb_connect_if_needed(): | ||||
|     if not ADB_IP_PORT: | ||||
|         return | ||||
|     devices = list_adb_devices() | ||||
|     if any(ADB_IP_PORT in ln for ln in devices): | ||||
|         return  # already connected | ||||
|     run(["adb", "connect", ADB_IP_PORT]) | ||||
|  | ||||
|  | ||||
| def is_agm_connected() -> bool: | ||||
|     devices = list_adb_devices() | ||||
|     for ln in devices: | ||||
|         if "device" in ln and not ln.endswith("offline"): | ||||
|             if any(tag in ln for tag in ADB_MODEL_MATCH): | ||||
|                 return True | ||||
|     return False | ||||
|  | ||||
|  | ||||
| def gather_music_files() -> List[Tuple[Path, Path, float, int]]: | ||||
|     # Returns list of tuples: (root_dir, file_path, mtime, size) | ||||
|     entries: List[Tuple[Path, Path, float, int]] = [] | ||||
|     for root in SOURCES: | ||||
|         try: | ||||
|             if not root.exists(): | ||||
|                 continue | ||||
|             for dirpath, _, filenames in os.walk(root): | ||||
|                 dpath = Path(dirpath) | ||||
|                 for fn in filenames: | ||||
|                     p = dpath / fn | ||||
|                     if p.suffix.lower() not in MUSIC_EXTS: | ||||
|                         continue | ||||
|                     try: | ||||
|                         st = p.stat() | ||||
|                         entries.append((root, p, st.st_mtime, st.st_size)) | ||||
|                     except Exception: | ||||
|                         continue | ||||
|         except Exception: | ||||
|             continue | ||||
|     # newest first by mtime | ||||
|     entries.sort(key=lambda t: t[2], reverse=True) | ||||
|     return entries | ||||
|  | ||||
|  | ||||
| def select_up_to_size(entries: List[Tuple[Path, Path, float, int]], limit_bytes: int) -> List[Tuple[Path, Path, int]]: | ||||
|     selected: List[Tuple[Path, Path, int]] = [] | ||||
|     total = 0 | ||||
|     for root, p, _mt, sz in entries: | ||||
|         if total >= limit_bytes: | ||||
|             break | ||||
|         selected.append((root, p, sz)) | ||||
|         total += sz | ||||
|     return selected | ||||
|  | ||||
|  | ||||
| def adb_shell(cmd: str) -> subprocess.CompletedProcess: | ||||
|     # Single string executed via adb shell | ||||
|     return run(["adb", "shell", cmd]) | ||||
|  | ||||
|  | ||||
| def remote_size(adb_path: str) -> Optional[int]: | ||||
|     # Uses stat if available; returns None if missing | ||||
|     # Escape path for shell | ||||
|     qpath = shlex.quote(adb_path) | ||||
|     proc = adb_shell(f"stat -c %s {qpath}") | ||||
|     if proc.returncode != 0 or not proc.stdout.strip(): | ||||
|         return None | ||||
|     out = proc.stdout.strip() | ||||
|     if "No such file" in out or "not found" in out: | ||||
|         return None | ||||
|     try: | ||||
|         return int(out.splitlines()[-1].strip()) | ||||
|     except Exception: | ||||
|         return None | ||||
|  | ||||
|  | ||||
| def ensure_remote_dir(adb_path: str) -> None: | ||||
|     # mkdir -p dirname | ||||
|     dirname = os.path.dirname(adb_path) | ||||
|     if not dirname: | ||||
|         return | ||||
|     adb_shell(f"mkdir -p {shlex.quote(dirname)}") | ||||
|  | ||||
|  | ||||
| def have_ffmpeg() -> bool: | ||||
|     proc = run(["ffmpeg", "-version"]) | ||||
|     return proc.returncode == 0 | ||||
|  | ||||
|  | ||||
| def transcode_flac_to_mp3(src: Path) -> Optional[Path]: | ||||
|     try: | ||||
|         tmp = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) | ||||
|         tmp_path = Path(tmp.name) | ||||
|         tmp.close() | ||||
|     except Exception: | ||||
|         return None | ||||
|     cmd = [ | ||||
|         "ffmpeg", | ||||
|         "-y", | ||||
|         "-i", | ||||
|         str(src), | ||||
|         "-vn", | ||||
|         "-c:a", | ||||
|         "libmp3lame", | ||||
|         "-q:a", | ||||
|         "2", | ||||
|         str(tmp_path), | ||||
|     ] | ||||
|     proc = run(cmd) | ||||
|     if proc.returncode != 0: | ||||
|         try: | ||||
|             tmp_path.unlink(missing_ok=True)  # type: ignore[arg-type] | ||||
|         except Exception: | ||||
|             pass | ||||
|         return None | ||||
|     return tmp_path | ||||
|  | ||||
|  | ||||
| def push_file(local_path: Path, remote_path: str, size: int) -> bool: | ||||
|     # Skip if same size exists | ||||
|     rsz = remote_size(remote_path) | ||||
|     if rsz is not None and rsz == size: | ||||
|         print(f"[=] Skip (same size): {local_path} -> {remote_path}") | ||||
|         return True | ||||
|     ensure_remote_dir(remote_path) | ||||
|     proc = run(["adb", "push", "-p", str(local_path), remote_path]) | ||||
|     if proc.returncode == 0: | ||||
|         print(f"[+] Pushed: {local_path} -> {remote_path}") | ||||
|         return True | ||||
|     else: | ||||
|         print(f"[!] Push failed: {local_path}\n{proc.stderr}") | ||||
|         return False | ||||
|  | ||||
|  | ||||
| def sync_latest_batch(): | ||||
|     entries = gather_music_files() | ||||
|     if not entries: | ||||
|         print("[i] No music files found.") | ||||
|         return | ||||
|     batch = select_up_to_size(entries, SIZE_LIMIT_BYTES) | ||||
|     print(f"[i] Selected {len(batch)} files up to {SIZE_LIMIT_BYTES/(1024**3):.2f} GiB") | ||||
|     for root, p, sz in batch: | ||||
|         try: | ||||
|             rel = p.relative_to(root) | ||||
|         except Exception: | ||||
|             rel = p.name  # fallback | ||||
|         if p.suffix.lower() == ".flac": | ||||
|             if not have_ffmpeg(): | ||||
|                 print(f"[!] ffmpeg not available, skipping: {p}") | ||||
|                 continue | ||||
|             mp3_tmp = transcode_flac_to_mp3(p) | ||||
|             if not mp3_tmp: | ||||
|                 print(f"[!] Transcode failed, skipping: {p}") | ||||
|                 continue | ||||
|             try: | ||||
|                 if isinstance(rel, Path): | ||||
|                     rel_mp3 = rel.with_suffix(".mp3") | ||||
|                     remote = f"{DEST_ON_PHONE.rstrip('/')}/{rel_mp3.as_posix()}" | ||||
|                 else: | ||||
|                     remote = f"{DEST_ON_PHONE.rstrip('/')}/{Path(rel).with_suffix('.mp3').as_posix()}" | ||||
|                 msz = mp3_tmp.stat().st_size | ||||
|                 push_file(mp3_tmp, remote, msz) | ||||
|             finally: | ||||
|                 try: | ||||
|                     mp3_tmp.unlink() | ||||
|                 except Exception: | ||||
|                     pass | ||||
|         else: | ||||
|             remote = f"{DEST_ON_PHONE.rstrip('/')}/{rel.as_posix()}" | ||||
|             push_file(p, remote, sz) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     print("[i] adb-music-sync daemon starting...") | ||||
|     ensure_adb_server() | ||||
|     connected_prev = False | ||||
|     while True: | ||||
|         try: | ||||
|             ensure_adb_server() | ||||
|             adb_connect_if_needed() | ||||
|             connected = is_agm_connected() | ||||
|             if connected and not connected_prev: | ||||
|                 print("[+] AGM M7 connected. Starting sync of latest ~7 GiB...") | ||||
|                 sync_latest_batch() | ||||
|             elif not connected and connected_prev: | ||||
|                 print("[-] AGM M7 disconnected.") | ||||
|             connected_prev = connected | ||||
|             time.sleep(POLL_INTERVAL_SEC) | ||||
|         except KeyboardInterrupt: | ||||
|             print("[i] Exiting on user interrupt.") | ||||
|             sys.exit(0) | ||||
|         except Exception as e: | ||||
|             print(f"[!] Error in main loop: {e}") | ||||
|             time.sleep(POLL_INTERVAL_SEC) | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										78
									
								
								adb-music-sync/install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								adb-music-sync/install.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -euo pipefail | ||||
|  | ||||
| SILENT=false | ||||
| LOG_PREFIX="[adb-music-sync]" | ||||
|  | ||||
| PYTHON_SCRIPT="$HOME/.linux-env/adb-music-sync/adb-music-sync.py" | ||||
| INSTALL_TARGET="$HOME/.local/bin/adb-music-sync.py" | ||||
| AUTOSTART_DIR="$HOME/.config/autostart" | ||||
| AUTOSTART_FILE="$AUTOSTART_DIR/adb-music-sync.desktop" | ||||
|  | ||||
| log() { | ||||
|   if [ "$SILENT" = true ]; then return; fi | ||||
|   echo "$LOG_PREFIX $*" | ||||
| } | ||||
|  | ||||
| check_and_install_deps() { | ||||
|   # Minimal dependency: adb | ||||
|   if ! command -v adb >/dev/null 2>&1; then | ||||
|     if command -v apt >/dev/null 2>&1; then | ||||
|       log "[+] Installation d'adb (apt) ..." | ||||
|       sudo apt update -y && sudo apt install -y adb | ||||
|     elif command -v dnf >/dev/null 2>&1; then | ||||
|       log "[+] Installation d'adb (dnf) ..." | ||||
|       sudo dnf install -y android-tools | ||||
|     elif command -v pacman >/dev/null 2>&1; then | ||||
|       log "[+] Installation d'adb (pacman) ..." | ||||
|       sudo pacman -Sy --noconfirm android-tools | ||||
|     else | ||||
|       log "[!] adb introuvable et gestionnaire de paquets inconnu. Installez adb manuellement." | ||||
|     fi | ||||
|   fi | ||||
| } | ||||
|  | ||||
| install_script() { | ||||
|   check_and_install_deps | ||||
|  | ||||
|   log "[+] Copie du script Python..." | ||||
|   mkdir -p "$(dirname "$INSTALL_TARGET")" | ||||
|   cp "$PYTHON_SCRIPT" "$INSTALL_TARGET" | ||||
|   chmod +x "$INSTALL_TARGET" | ||||
|  | ||||
|   log "[+] Création du fichier autostart..." | ||||
|   mkdir -p "$AUTOSTART_DIR" | ||||
|   cat > "$AUTOSTART_FILE" <<EOF | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| Exec=$INSTALL_TARGET | ||||
| Hidden=false | ||||
| NoDisplay=false | ||||
| X-GNOME-Autostart-enabled=true | ||||
| Name=ADB Music Sync | ||||
| Comment=Lance la synchronisation de ~5GiB de musique vers l'AGM M7 à la connexion ADB | ||||
| EOF | ||||
|  | ||||
|   log "[✓] Installation terminée. Le script se lancera automatiquement à l'ouverture de session." | ||||
| } | ||||
|  | ||||
| uninstall_script() { | ||||
|   log "[+] Suppression du script Python et autostart..." | ||||
|   rm -f "$INSTALL_TARGET" | ||||
|   rm -f "$AUTOSTART_FILE" | ||||
|   log "[✓] Désinstallation terminée." | ||||
| } | ||||
|  | ||||
| case "${1:-}" in | ||||
|   uninstall) | ||||
|     uninstall_script | ||||
|     ;; | ||||
|   silent) | ||||
|     SILENT=true | ||||
|     uninstall_script &>/dev/null || true | ||||
|     install_script &>/dev/null | ||||
|     ;; | ||||
|   *) | ||||
|     install_script | ||||
|     ;; | ||||
| esac | ||||
							
								
								
									
										11
									
								
								applications/install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/install.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #!/bin/bash | ||||
| # desktop apps | ||||
| cp ~/.linux-env/applications/*/*.desktop ~/.local/share/applications/ | ||||
| cp ~/.linux-env/applications/*/*.sh ~/.local/bin/ | ||||
| cp ~/.linux-env/applications/*/*.svg ~/.local/share/icons/ | ||||
|  | ||||
| chmod +x ~/.local/share/applications/*.desktop | ||||
| chmod +x ~/.local/bin/*.sh | ||||
|  | ||||
| update-desktop-database ~/.local/share/applications/ | ||||
| gtk-update-icon-cache ~/.local/share/icons/  # inutile sauf si tu fais un vrai thème d'icônes | ||||
							
								
								
									
										103
									
								
								bashrc/.bashrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								bashrc/.bashrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| # .linux-env | ||||
| dlvideo() { | ||||
|   VIDEO_PATH="/mnt/media/video/autre" | ||||
|   cd "$VIDEO_PATH" || return | ||||
|   pip install -U yt-dlp | ||||
|   yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4" --merge-output-format mp4 "$@" | ||||
| } | ||||
|  | ||||
| dlfilm() { | ||||
|   VIDEO_PATH="/mnt/media/video/film" | ||||
|   yt-dlp -o "$VIDEO_PATH/%(title)s.%(ext)s" "$1" | ||||
| } | ||||
|  | ||||
| dlimage() { | ||||
|   IMAGE_PATH="/mnt/media/image/screenshit" | ||||
|   wget -P "$IMAGE_PATH" "$1" | ||||
| } | ||||
|  | ||||
| dlcover() { | ||||
|   query_or_file="$1" | ||||
|   manual_url="$2" | ||||
|   MUSIC_DIR="$HOME/media/files/music" | ||||
|  | ||||
|   if [ -f "$query_or_file" ]; then | ||||
|     file="$query_or_file" | ||||
|   elif echo "$query_or_file" | grep -q '%(ext)'; then | ||||
|     file="$query_or_file" | ||||
|   else | ||||
|     file=$(find "$MUSIC_DIR" -type f \ | ||||
|       \( -iname "*${query_or_file}*.mp3" -o -iname "*${query_or_file}*.m4a" -o -iname "*${query_or_file}*.flac" -o -iname "*${query_or_file}*.wav" -o -iname "*${query_or_file}*.ogg" \) \ | ||||
|       | head -n 1) | ||||
|   fi | ||||
|  | ||||
|   if [ -z "$file" ]; then | ||||
|     echo "❌ Aucun fichier trouvé pour: $query_or_file" | ||||
|     return 1 | ||||
|   fi | ||||
|  | ||||
|   base="${file%.*}" | ||||
|  | ||||
|   if [ -n "$manual_url" ]; then | ||||
|     curl -s "$manual_url" -o "${base}.jpg" | ||||
|     echo "✅ ${base}.jpg saved from manual URL (fichier: $file)" | ||||
|     return | ||||
|   fi | ||||
|  | ||||
|   artist_raw=$(echo "$base" | awk -F'__' '{print $2}') | ||||
|   title_raw=$(echo "$base" | awk -F'__' '{for(i=3;i<=NF;i++){printf (i>3?" ":""); printf $i}}') | ||||
|   artist=$(echo "$artist_raw" | tr '_' ' ') | ||||
|   title=$(echo "$title_raw" | tr '_' ' ') | ||||
|   query_enc=$(jq -rn --arg s "$artist $title" '$s|@uri') | ||||
|  | ||||
|   it_url="https://itunes.apple.com/search?term=${query_enc}&entity=song&limit=1" | ||||
|   art=$(curl -s "$it_url" | jq -r '.results[0].artworkUrl100') | ||||
|   if [ -n "$art" ] && [ "$art" != "null" ]; then | ||||
|     art_hi=$(echo "$art" | sed 's/100x100bb/1000x1000bb/') | ||||
|     curl -s "$art_hi" -o "${base}.jpg" | ||||
|     echo "✅ ${base}.jpg saved from iTunes (fichier: $file)" | ||||
|     return | ||||
|   fi | ||||
|  | ||||
|   api_key="5ae5d1212599f96ffa799e21da1b2a7a38274c94bb5ae8ad26ce8d5b08528aaa" | ||||
|   url=$(curl -s "https://serpapi.com/search.json?engine=google&q=${query_enc}&tbm=isch&api_key=${api_key}" \ | ||||
|         | jq -r '.images_results[0].original') | ||||
|  | ||||
|   if [ -n "$url" ] && [ "$url" != "null" ]; then | ||||
|     curl -s "$url" -o "${base}.jpg" | ||||
|     echo "✅ ${base}.jpg saved from Google Images (fichier: $file)" | ||||
|   else | ||||
|     echo "❌ No cover found for $artist - $title (fichier: $file)" | ||||
|   fi | ||||
| } | ||||
|  | ||||
| dlmusic() { | ||||
|   MUSIC_PATH="/home/root/media/files/music" | ||||
|   mkdir -p "$MUSIC_PATH" | ||||
|  | ||||
|   NOW=$(date +"%Y%m%d%H") | ||||
|   URL="$1" | ||||
|  | ||||
|   # Récupère le titre | ||||
|   TITLE=$(yt-dlp --get-title "$URL") | ||||
|  | ||||
|   # Transforme le titre pour le nom de fichier | ||||
|   SAFE_TITLE="${TITLE// - /__}" | ||||
|   SAFE_TITLE="${SAFE_TITLE// /_}" | ||||
|  | ||||
|   TRACK_NAME="$MUSIC_PATH/${NOW}__${SAFE_TITLE}.%(ext)s" | ||||
|  | ||||
|   # Téléchargement | ||||
|   yt-dlp -x --audio-format mp3 -o $TRACK_NAME "$URL" | ||||
|   dlcover $TRACK_NAME | ||||
| } | ||||
|  | ||||
| search() { | ||||
|   find ./ -iname "*$1*" | ||||
| } | ||||
|  | ||||
| searchinside() { | ||||
|   grep -rwl "$1" ./ | ||||
| } | ||||
|  | ||||
| # .linux-env | ||||
							
								
								
									
										4
									
								
								bashrc/install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								bashrc/install.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| sed -i '/# .linux-env/,/# .linux-env/d' ~/.bashrc | ||||
| cat ~/.linux-env/bashrc/.bashrc >> ~/.bashrc | ||||
| @@ -1,13 +0,0 @@ | ||||
| [Unit] | ||||
| Description=Pad Dolphin Watcher Service (User) | ||||
| After=graphical.target | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| ExecStart=/usr/bin/python3 /home/%u/.local/bin/gamecube-pad.py | ||||
| Restart=always | ||||
| RestartSec=3 | ||||
| WorkingDirectory=/home/%u | ||||
|  | ||||
| [Install] | ||||
| WantedBy=default.target | ||||
| @@ -1,46 +1,77 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
|  | ||||
| SERVICE_NAME="gamecube-pad.service" | ||||
| SERVICE_PATH="$HOME/.config/systemd/user/gamecube-pad.service" | ||||
| PYTHON_SCRIPT="/home/$USER/.local/bin/gamecube-pad.py" | ||||
| PYTHON_SCRIPT="$HOME/.local/bin/gamecube-pad.py" | ||||
| AUTOSTART_DIR="$HOME/.config/autostart" | ||||
| AUTOSTART_FILE="$AUTOSTART_DIR/gamecube-pad.desktop" | ||||
|  | ||||
| install_service() { | ||||
|   echo "[+] Installation des dépendances..." | ||||
|   sudo apt install -y python3-evdev python3-uinput xdotool wmctrl | ||||
| SILENT=false | ||||
| if [[ "$1" == "silent" ]]; then | ||||
|   SILENT=true | ||||
| fi | ||||
|  | ||||
|   echo "[+] Copie du script Python..." | ||||
|   mkdir -p "$(dirname "$PYTHON_SCRIPT")" | ||||
|   cp gamecube-pad.py "$PYTHON_SCRIPT" | ||||
|   chmod +x "$PYTHON_SCRIPT" | ||||
|  | ||||
|   echo "[+] Copie du fichier systemd utilisateur..." | ||||
|   mkdir -p "$HOME/.config/systemd/user" | ||||
|   cp gamecube-pad.service "$SERVICE_PATH" | ||||
|  | ||||
|   echo "[+] Activation du service..." | ||||
|   systemctl --user daemon-reload | ||||
|   systemctl --user enable gamecube-pad.service | ||||
|   systemctl --user restart gamecube-pad.service | ||||
|  | ||||
|   echo "[✓] Installation terminée. Consultez les logs avec : journalctl --user -u gamecube-pad.service -f" | ||||
| log() { | ||||
|   if [ "$SILENT" = false ]; then | ||||
|     echo "$@" | ||||
|   fi | ||||
| } | ||||
|  | ||||
| uninstall_service() { | ||||
|   echo "[+] Arrêt et suppression du service..." | ||||
|   systemctl --user stop gamecube-pad.service || true | ||||
|   systemctl --user disable gamecube-pad.service || true | ||||
|   rm -f "$SERVICE_PATH" | ||||
| check_and_install_deps() { | ||||
|   for pkg in evdev uinput; do | ||||
|     if ! python3 -c "import $pkg" &>/dev/null; then | ||||
|       log "[!] Dépendance manquante : $pkg. Installation avec pip..." | ||||
|       python3 -m pip install --user "$pkg" &>/dev/null | ||||
|     fi | ||||
|   done | ||||
|  | ||||
|   for cmd in xdotool wmctrl; do | ||||
|     if ! command -v "$cmd" &>/dev/null; then | ||||
|       log "[!] Commande manquante : $cmd. Merci de l’installer via votre gestionnaire de paquets." | ||||
|     fi | ||||
|   done | ||||
| } | ||||
|  | ||||
| install_script() { | ||||
|   check_and_install_deps | ||||
|  | ||||
|   log "[+] Copie du script Python..." | ||||
|   mkdir -p "$(dirname "$PYTHON_SCRIPT")" | ||||
|   cp "$HOME/.linux-env/gamecube-pad/gamecube-pad.py" "$PYTHON_SCRIPT" | ||||
|   chmod +x "$PYTHON_SCRIPT" | ||||
|  | ||||
|   log "[+] Création du fichier autostart..." | ||||
|   mkdir -p "$AUTOSTART_DIR" | ||||
|   cat > "$AUTOSTART_FILE" <<EOF | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| Exec=$PYTHON_SCRIPT | ||||
| Hidden=false | ||||
| NoDisplay=false | ||||
| X-GNOME-Autostart-enabled=true | ||||
| Name=GameCube Pad | ||||
| Comment=Lance le script GameCube Pad au démarrage | ||||
| EOF | ||||
|  | ||||
|   log "[✓] Installation terminée. Le script se lancera automatiquement à l'ouverture de session." | ||||
| } | ||||
|  | ||||
| uninstall_script() { | ||||
|   log "[+] Suppression du script Python et autostart..." | ||||
|   rm -f "$PYTHON_SCRIPT" | ||||
|   systemctl --user daemon-reload | ||||
|   echo "[✓] Désinstallation terminée." | ||||
|   rm -f "$AUTOSTART_FILE" | ||||
|   log "[✓] Désinstallation terminée." | ||||
| } | ||||
|  | ||||
| case "$1" in | ||||
|   uninstall) | ||||
|     uninstall_service | ||||
|     uninstall_script | ||||
|     ;; | ||||
|   silent) | ||||
|     SILENT=true | ||||
|     uninstall_script &>/dev/null | ||||
|     install_script &>/dev/null | ||||
|     ;; | ||||
|   *) | ||||
|     install_service | ||||
|     install_script | ||||
|     ;; | ||||
| esac | ||||
|   | ||||
							
								
								
									
										22
									
								
								install.sh
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								install.sh
									
									
									
									
									
								
							| @@ -1,10 +1,16 @@ | ||||
| # desktop apps | ||||
| cp ./applications/*/*.desktop ~/.local/share/applications/ | ||||
| cp ./applications/*/*.sh ~/.local/bin/ | ||||
| cp ./applications/*/*.svg ~/.local/share/icons/ | ||||
| #!/bin/bash | ||||
| set -e | ||||
|  | ||||
| chmod +x ~/.local/share/applications/*.desktop | ||||
| chmod +x ~/.local/bin/*.sh | ||||
| # Chemin de base : le dossier où se trouve ce script | ||||
| BASE_DIR="$(dirname "$(realpath "$0")")" | ||||
|  | ||||
| update-desktop-database ~/.local/share/applications/ | ||||
| # gtk-update-icon-cache ~/.local/share/icons/  # inutile sauf si tu fais un vrai thème d'icônes | ||||
| echo "[+] Recherche et exécution des install.sh..." | ||||
|  | ||||
| # Boucle récursive sur tous les fichiers install.sh | ||||
| find "$BASE_DIR" -type f -name "install.sh" ! -path "$BASE_DIR/install.sh" | while read -r script; do | ||||
|     echo "[+] Lancement : $script" | ||||
|     chmod +x "$script" | ||||
|     "$script" | ||||
| done | ||||
|  | ||||
| echo "[✓] Tous les install.sh ont été exécutés." | ||||
|   | ||||
		Reference in New Issue
	
	Block a user