From 8dd513db0d24dec4937f8dd203388dbd192b3872 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 09:21:07 +0200 Subject: [PATCH 01/28] feat!: add ci cd with last version --- .github/workflows/LaMira.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/LaMira.yml b/.github/workflows/LaMira.yml index 4daa19f..d0bf42d 100644 --- a/.github/workflows/LaMira.yml +++ b/.github/workflows/LaMira.yml @@ -16,7 +16,6 @@ env: jobs: check_work: runs-on: ubuntu-latest - if: github.repository != 'Project-Tranquility/cli' name: check_work container: image: python:3.14 @@ -30,7 +29,7 @@ jobs: mira: runs-on: ubuntu-latest needs: check_work - if: github.repository != 'Project-Tranquility/Tranquillity' && github.repository != 'Project-Tranquility/cli' && github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.check_work.result == 'success' + if: github.repository != 'Project-Tranquility/Tranquillity' && github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.check_work.result == 'success' steps: - uses: actions/checkout@v6 with: From 2fe6a29d8510cf3ed64aa0c829283c3ada26170d Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 09:26:40 +0200 Subject: [PATCH 02/28] docs: add new description for the cli project --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fafb666..60d849b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# cli -cli for Tranquility +# cpOS +cpOS ? +This is Central Tranquillity Operating System, the cli for Tranquillity From d9bbef4c90e9fe8485808bfcf9f51e3c7601744e Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 09:41:23 +0200 Subject: [PATCH 03/28] feat!: add file for dev on nixos --- devenv.lock | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ devenv.nix | 52 ++++++++++++++++++++++++++++++++++++++++++ devenv.yaml | 18 +++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..91bb64d --- /dev/null +++ b/devenv.lock @@ -0,0 +1,65 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1782751734, + "narHash": "sha256-shLdS1fYdEQvbp+XzzSDq/pBxKoniU+cqXvDFgmEE70=", + "owner": "cachix", + "repo": "devenv", + "rev": "26c8d030f9398fb8531a2b19f6b3aaa8b3589b47", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "nixpkgs": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1782132010, + "narHash": "sha256-ZnAVHdVrotp80iIMm5CSR1fdxPlw7Uwmwxb+O/wsgZ8=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "12866ae2dddbc0ab8b329915f8072bb9c75bde89", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-src": { + "flake": false, + "locked": { + "lastModified": 1781607440, + "narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3e41b24abd260e8f71dbe2f5737d24122f972158", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} \ No newline at end of file diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..4c14513 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,52 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + # https://devenv.sh/basics/ + env.GREET = "devenv"; + + # https://devenv.sh/packages/ + packages = [ pkgs.git ]; + + # https://devenv.sh/languages/ + # languages.rust.enable = true; + + # https://devenv.sh/processes/ + # processes.dev.exec = "${lib.getExe pkgs.watchexec} -n -- ls -la"; + + # https://devenv.sh/services/ + # services.postgres.enable = true; + + # https://devenv.sh/scripts/ + scripts.hello.exec = '' + echo hello from $GREET + ''; + + # https://devenv.sh/basics/ + enterShell = '' + hello # Run scripts directly + git --version # Use packages + ''; + + # https://devenv.sh/tasks/ + # tasks = { + # "myproj:setup".exec = "mytool build"; + # "devenv:enterShell".after = [ "myproj:setup" ]; + # }; + + languages.python = { + enable = true; + venv.enable = true; + venv.requirements = ./requirement.txt; + }; + + # https://devenv.sh/tests/ + enterTest = '' + echo "Running tests" + git --version | grep --color=auto "${pkgs.git.version}" + ''; + + # https://devenv.sh/git-hooks/ + # git-hooks.hooks.shellcheck.enable = true; + + # See full reference at https://devenv.sh/reference/options/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..3dcab58 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + +# If you're using non-OSS software, you can set allow_unfree to true. +# allow_unfree: true + +# If you're not willing to allow unsupported packages: +# allow_unsupported_system: false + +# If you're willing to use a package that's vulnerable +# permitted_insecure_packages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend From e71313c8150fd03fe7d985ccba2cbfd13933b0c5 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 09:41:38 +0200 Subject: [PATCH 04/28] feat!: add gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c00816b --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Devenv +.devenv* +devenv.local.nix +devenv.local.yaml + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml +.vscode From 256f12ac1ba92e6c3b32ce428dc5d17bc476a649 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 09:42:00 +0200 Subject: [PATCH 05/28] feat!: add requirement for this project --- requirement.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirement.txt diff --git a/requirement.txt b/requirement.txt new file mode 100644 index 0000000..663bd1f --- /dev/null +++ b/requirement.txt @@ -0,0 +1 @@ +requests \ No newline at end of file From c93069f53349806b29969fd6d7bc9aff95c69db7 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 09:42:20 +0200 Subject: [PATCH 06/28] feat!: add first file --- cpOS.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cpOS.py diff --git a/cpOS.py b/cpOS.py new file mode 100644 index 0000000..8198a3f --- /dev/null +++ b/cpOS.py @@ -0,0 +1,6 @@ +import requests + +def main(): + print("Hello from cpOS") + +main() \ No newline at end of file From e05b47804f9ddb3f1d798cd3a1abdc4ca0a64f4b Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 10:06:51 +0200 Subject: [PATCH 07/28] docs: add feature template --- .github/PULL_REQUEST_TEMPLATE/feature.md | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/feature.md diff --git a/.github/PULL_REQUEST_TEMPLATE/feature.md b/.github/PULL_REQUEST_TEMPLATE/feature.md new file mode 100644 index 0000000..751b3ac --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature.md @@ -0,0 +1,31 @@ +# Feature: `` + +## Summary +> What this PR adds and why. + +## Related Issue +Closes # + +## Changes +### What's new +- + +### Implementation notes +> Key design decisions, tradeoffs, or anything a reviewer should know before reading the diff. +- + +## Checklist +- [ ] Conventional Commits used for all commits +- [ ] Tests added/updated for new behavior +- [ ] Tests passing on mGBA +- [ ] `pipx run build` clean locally +- [ ] Docs/README updated if public API changed +- [ ] No unrelated changes bundled in this PR + +## Out of Scope +> Anything deliberately left out (tracked in a separate issue, if applicable). +- + +## Links +- Issue: # +- Docs: \ No newline at end of file From 56d8efabcb9f655ecd5d88264831ee19bdf82844 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 10:07:13 +0200 Subject: [PATCH 08/28] docs: add release template --- .github/PULL_REQUEST_TEMPLATE/release.md | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/release.md diff --git a/.github/PULL_REQUEST_TEMPLATE/release.md b/.github/PULL_REQUEST_TEMPLATE/release.md new file mode 100644 index 0000000..86748fd --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/release.md @@ -0,0 +1,33 @@ +# Release `vX.Y.Z` + +## Summary + +> What changed and why. + +## Changes + +### New features +- + +### Bug fixes +- + +### Breaking changes +- + +## Checklist + +- [ ] Conventional Commits verified on `main` +- [ ] `release-config.json` / `.release-manifest.json` up to date +- [ ] Tests passing on mGBA +- [ ] `pipx run build` clean locally +- [ ] **Cartridge App** workflow green on `stable` + +## Rollback + +> If broken post-merge: publish a patch `vX.Y.Z+1` and yank on PyPI. + +## Links + +- Issue: # +- Docs: \ No newline at end of file From 20bce64f6112623639521d74005cf911cd1267a6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 30 Jun 2026 10:35:36 +0200 Subject: [PATCH 09/28] docs: rm line not use for this project --- .github/PULL_REQUEST_TEMPLATE/feature.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/feature.md b/.github/PULL_REQUEST_TEMPLATE/feature.md index 751b3ac..e5550cf 100644 --- a/.github/PULL_REQUEST_TEMPLATE/feature.md +++ b/.github/PULL_REQUEST_TEMPLATE/feature.md @@ -17,8 +17,6 @@ Closes # ## Checklist - [ ] Conventional Commits used for all commits - [ ] Tests added/updated for new behavior -- [ ] Tests passing on mGBA -- [ ] `pipx run build` clean locally - [ ] Docs/README updated if public API changed - [ ] No unrelated changes bundled in this PR From 293b6b92fb26f247c7704cc82969def5185be448 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 09:41:40 +0200 Subject: [PATCH 10/28] feat!: add more gitgnore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c00816b..69f6770 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ devenv.local.yaml # pre-commit .pre-commit-config.yaml .vscode + +__pycache__ \ No newline at end of file From 18bad4dc5833ad8a62d6d87b7594929a5159b6aa Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 09:47:50 +0200 Subject: [PATCH 11/28] feat!: add commit policy documentation --- .github/policy_commit.md | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/policy_commit.md diff --git a/.github/policy_commit.md b/.github/policy_commit.md new file mode 100644 index 0000000..eb513f5 --- /dev/null +++ b/.github/policy_commit.md @@ -0,0 +1,48 @@ +# Policy de commit + +## Format + +``` +(): +``` + +- **type** : obligatoire, voir liste ci-dessous +- **scope** : optionnel, module/partie concernée (ex: `cli`, `ci`, `bot`) +- **description** : à l'impératif, minuscule, pas de point final + +## Types + +- `feat!` : nouvelle fonctionnalité +- `fix` : correction de bug +- `refactor` : changement de code sans impact fonctionnel +- `docs` : documentation uniquement +- `test` : ajout/modif de tests +- `ci` : pipeline CI/CD + +## Règles + +1. Une ligne de résumé (72 caractères max) +2. Un commit = un changement logique +3. Description claire, pas de "fix stuff" ou "update" +4. Corps du message (optionnel) séparé par une ligne vide, explique le *pourquoi* +5. Pas d'emoji, pas de ponctuation superflue + +## Exemples + +``` +feat(cli): add boot animation with rich progress bar +fix(ci): correct rsync path in sync workflow +refactor(bot): extract command handlers into separate module +docs: add setup instructions for devenv +chore: bump python dependencies +``` + +## Breaking changes + +Ajouter `!` après le type/scope et expliquer dans le corps : + +``` +feat!: change config format from json to toml + +Migration nécessaire, voir CHANGELOG.md +``` \ No newline at end of file From 468967c5910e51123c14255680cfd5bd2c63264c Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 09:48:09 +0200 Subject: [PATCH 12/28] feat!: add rich librrary --- requirement.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirement.txt b/requirement.txt index 663bd1f..195624e 100644 --- a/requirement.txt +++ b/requirement.txt @@ -1 +1,2 @@ -requests \ No newline at end of file +requests +rich \ No newline at end of file From 972a43150a91d4778db396512e497afc7b9e44e3 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 10:17:56 +0200 Subject: [PATCH 13/28] feat!: add first command help --- cpOS.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cpOS.py b/cpOS.py index 8198a3f..c61fcd0 100644 --- a/cpOS.py +++ b/cpOS.py @@ -1,6 +1,22 @@ +#!/usr/bin/env python3 import requests +from test import demo +from src.start.start import start +from src.help import help +import sys def main(): - print("Hello from cpOS") + nb = len(sys.argv) + + if nb <= 1: + help() + return + if sys.argv[1] == "--help" or "-h": + help() + if sys.argv[1] == "hello": + demo() + start() + else: + return 84 main() \ No newline at end of file From b6e37baa769043a8ef39cff9d9807e4750effcf1 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 10:18:28 +0200 Subject: [PATCH 14/28] feat!: add text for help command --- src/help.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/help.py diff --git a/src/help.py b/src/help.py new file mode 100644 index 0000000..adc7f2e --- /dev/null +++ b/src/help.py @@ -0,0 +1,4 @@ +def help(): + print("Welcome to cpOS\n") + print("Usage : cpOS [command]") + print(" cpOS [flags]\n") \ No newline at end of file From 81209c9479d542ef3cdbd63fb35d61b9aa2430ba Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 10:18:56 +0200 Subject: [PATCH 15/28] feat!: add rich intro --- src/start/start.py | 63 +++++++++++++++++++++++++++++++++ test.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 src/start/start.py create mode 100644 test.py diff --git a/src/start/start.py b/src/start/start.py new file mode 100644 index 0000000..3bcf457 --- /dev/null +++ b/src/start/start.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +import requests +import time +import random +from collections import deque +from rich.console import Console +from rich.layout import Layout +from rich.live import Live +from rich.text import Text +from rich.align import Align +from rich.panel import Panel + +from test import demo + +console = Console() +GREEN = "bold spring_green2" +DIM_GREEN = "spring_green4" + +BOOT_LINES = [ + "[INIT] Loading all module...", + "[NET ] Establishing uplink to backbone...", + "[OK ] All systems are onlines.", +] + +SPACE_LINE = [ + " " +] + +WAITING_LINE = [ + "." + ".." + "..." +] + +START_LINE = [ + "Hello from cpOS" +] + +def make_layout() -> Layout: + layout = Layout() + layout.split_column( + Layout(name="main", ratio=2), + ) + return layout + +def render_status(history: deque) -> Panel: + body = Text("\n".join(history), style=DIM_GREEN) + return Panel(body, border_style=DIM_GREEN) + +def start(): + layout = make_layout() + history = deque(maxlen=6) + messages = BOOT_LINES + start = START_LINE + wait = WAITING_LINE + is_finish = False + + with Live(layout, console=console, refresh_per_second=20, screen=True): + while is_finish is False: + history = None + history = deque(maxlen=6) + history.append(start[0]) + layout["main"].update(render_status(history)) \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..55c804b --- /dev/null +++ b/test.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +import time +import random +from collections import deque +from rich.console import Console +from rich.layout import Layout +from rich.live import Live +from rich.text import Text +from rich.align import Align +from rich.panel import Panel + +console = Console() +GREEN = "bold spring_green2" +DIM_GREEN = "spring_green4" + +BOOT_LINES = [ + "[INIT] Loading all module...", + "[NET ] Establishing uplink to backbone...", + "[OK ] All systems are onlines.", +] + +SPACE_LINE = [ + " " +] + +WAITING_LINE = [ + "." + ".." + "..." +] + +START_LINE = [ + "Hello from cpOS" +] + +def make_layout() -> Layout: + layout = Layout() + layout.split_column( + Layout(name="main", ratio=2), + Layout(name="bottom", size=9), + ) + layout["bottom"].split_row( + Layout(name="bottom_left"), + ) + return layout + +def render_center(percent: int, width: int = 40) -> Align: + filled = int(width * percent / 100) + empty = width - filled + bar_str = "█" * filled + "░" * empty + text = Text(f"{bar_str} {percent:3d}%", style=GREEN, justify="center") + return Align.center(text, vertical="middle") + +def render_status(history: deque) -> Panel: + body = Text("\n".join(history), style=DIM_GREEN) + return Panel(body, border_style=DIM_GREEN) + +def demo(): + layout = make_layout() + history = deque(maxlen=6) + messages = BOOT_LINES + start = START_LINE + wait = WAITING_LINE + nb_task = 0 + i = 0 + is_finish = False + + + with Live(layout, console=console, refresh_per_second=20, screen=True): + while is_finish is False: + i = i + 1 + percent = (i * 100 // 59) + layout["main"].update(render_center(percent)) + + if i % 5 == 0: + history.append(random.choice(messages) + f" {random.randint(0,99)}%") + nb_task = nb_task + 1 + layout["bottom_left"].update(render_status(history)) + + if (percent >= 100): + is_finish = True + time.sleep(0.08) + + +if __name__ == "__main__": + demo() \ No newline at end of file From 223d719f6ec48b149407d7022e63117ee3efddb3 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 10:35:20 +0200 Subject: [PATCH 16/28] docs: add docs for command --- src/help.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/help.py b/src/help.py index adc7f2e..12c9aeb 100644 --- a/src/help.py +++ b/src/help.py @@ -1,4 +1,7 @@ def help(): print("Welcome to cpOS\n") print("Usage : cpOS [command]") - print(" cpOS [flags]\n") \ No newline at end of file + print(" cpOS [flags]\n") + print("[command] :\n") + print(" --light toggle (toggle connected light)") + print(" --music (use command for music)") \ No newline at end of file From 197290c7c512defebee901a767faf122e0785bcb Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 13:44:21 +0200 Subject: [PATCH 17/28] feat!: add dotenv on the requirement --- requirement.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirement.txt b/requirement.txt index 195624e..1e58b25 100644 --- a/requirement.txt +++ b/requirement.txt @@ -1,2 +1,3 @@ requests -rich \ No newline at end of file +rich +python-dotenv \ No newline at end of file From 87ff0d89d5409c1a07d82de758e15050e1a33b33 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 13:44:48 +0200 Subject: [PATCH 18/28] feat!: add all important files in gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 69f6770..bd7d347 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,7 @@ devenv.local.yaml .pre-commit-config.yaml .vscode -__pycache__ \ No newline at end of file +__pycache__ +.env + +.spotify_token.json \ No newline at end of file From 02a44d80bfd5e182b7d9c64a942b6dd3ca1d9bb3 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 15:15:02 +0200 Subject: [PATCH 19/28] feat!: add music section command --- cpOS.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cpOS.py b/cpOS.py index c61fcd0..b41f4f4 100644 --- a/cpOS.py +++ b/cpOS.py @@ -1,9 +1,11 @@ -#!/usr/bin/env python3 -import requests from test import demo from src.start.start import start from src.help import help import sys +from dotenv import load_dotenv +from src.music.music import music_command + +load_dotenv() def main(): nb = len(sys.argv) @@ -11,6 +13,9 @@ def main(): if nb <= 1: help() return + if sys.argv[1] == "--music": + music_command() + return if sys.argv[1] == "--help" or "-h": help() if sys.argv[1] == "hello": @@ -18,5 +23,4 @@ def main(): start() else: return 84 - main() \ No newline at end of file From cb3bf34fd1e7302e1a564bebf8d6e4ccb1d1b7b1 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 15:15:21 +0200 Subject: [PATCH 20/28] feat!: add get device id --- src/music/get_id_device.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/music/get_id_device.py diff --git a/src/music/get_id_device.py b/src/music/get_id_device.py new file mode 100644 index 0000000..d7a974a --- /dev/null +++ b/src/music/get_id_device.py @@ -0,0 +1,17 @@ +import requests +import json +import sys +from src.music.get_token_tmp import get_token +import requests + +def get_id_device(): + access_token = get_token() + url = "https://api.spotify.com/v1/me/player/devices" + header = {"Authorization": "Bearer " + access_token} + + result = requests.get(url, headers=header) + try: + body = result.json() + except Exception: + body = result.text + print(body) From 9b0b6166ecdb3a55339040dcb2457e5eaf3145c5 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 15:15:52 +0200 Subject: [PATCH 21/28] feat!: add get token for identification --- src/music/get_token_tmp.py | 115 +++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/music/get_token_tmp.py diff --git a/src/music/get_token_tmp.py b/src/music/get_token_tmp.py new file mode 100644 index 0000000..193fc4f --- /dev/null +++ b/src/music/get_token_tmp.py @@ -0,0 +1,115 @@ +import os +import json +import base64 +import webbrowser +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import urlencode, urlparse, parse_qs +from requests import post +from dotenv import load_dotenv + +load_dotenv() + +CLIENT_ID = os.getenv("TOKEN_PUBLIC") +CLIENT_SECRET = os.getenv("TOKEN_PRIVATE") +REDIRECT_URI = "http://127.0.0.1:8888/callback" +SCOPES = "user-read-playback-state user-modify-playback-state" +TOKEN_FILE = os.path.join(os.path.dirname(__file__), ".spotify_token.json") + + +def _basic_auth_header(): + auth_string = f"{CLIENT_ID}:{CLIENT_SECRET}".encode("utf-8") + return "Basic " + base64.b64encode(auth_string).decode("utf-8") + + +class _CallbackHandler(BaseHTTPRequestHandler): + code = None + + def do_GET(self): + query = parse_qs(urlparse(self.path).query) + _CallbackHandler.code = query.get("code", [None])[0] + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(b"

You can close this.

") + + def log_message(self, format, *args): + pass + + +def _request_authorization_code(): + params = { + "client_id": CLIENT_ID, + "response_type": "code", + "redirect_uri": REDIRECT_URI, + "scope": SCOPES, + } + auth_url = "https://accounts.spotify.com/authorize?" + urlencode(params) + webbrowser.open(auth_url) + + server = HTTPServer(("127.0.0.1", 8888), _CallbackHandler) + server.handle_request() + return _CallbackHandler.code + + +def _exchange_code_for_tokens(code): + url = "https://accounts.spotify.com/api/token" + headers = { + "Authorization": _basic_auth_header(), + "Content-Type": "application/x-www-form-urlencoded", + } + data = { + "grant_type": "authorization_code", + "code": code, + "redirect_uri": REDIRECT_URI, + } + result = post(url, headers=headers, data=data) + result.raise_for_status() + return result.json() + + +def _refresh_access_token(refresh_token): + url = "https://accounts.spotify.com/api/token" + headers = { + "Authorization": _basic_auth_header(), + "Content-Type": "application/x-www-form-urlencoded", + } + data = { + "grant_type": "refresh_token", + "refresh_token": refresh_token, + } + result = post(url, headers=headers, data=data) + result.raise_for_status() + return result.json() + + +def _save_tokens(tokens): + with open(TOKEN_FILE, "w") as f: + json.dump(tokens, f) + + +def _load_tokens(): + if not os.path.exists(TOKEN_FILE): + return None + with open(TOKEN_FILE) as f: + return json.load(f) + + +def get_token(): + if not CLIENT_ID or not CLIENT_SECRET: + raise RuntimeError("TOKEN_PUBLIC et TOKEN_PRIVATE doivent etre definis dans l'environnement") + + tokens = _load_tokens() + + if tokens and "refresh_token" in tokens: + new_tokens = _refresh_access_token(tokens["refresh_token"]) + new_tokens.setdefault("refresh_token", tokens["refresh_token"]) + _save_tokens(new_tokens) + return new_tokens["access_token"] + + code = _request_authorization_code() + if not code: + raise RuntimeError("Aucun code recu, autorisation echouee") + + tokens = _exchange_code_for_tokens(code) + _save_tokens(tokens) + return tokens["access_token"] \ No newline at end of file From 0370d71e50152f09357ebba2cf3aa7c8d93f4ab5 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 15:16:10 +0200 Subject: [PATCH 22/28] feat!: add music section --- src/music/music.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/music/music.py diff --git a/src/music/music.py b/src/music/music.py new file mode 100644 index 0000000..f4705bb --- /dev/null +++ b/src/music/music.py @@ -0,0 +1,26 @@ +import requests +import json +import sys +from src.music.get_token_tmp import get_token +import requests +from src.music.get_id_device import get_id_device +from src.music.pause import pause +from src.music.resume import resume + +def music_command(): + nb = len(sys.argv) + + if nb <= 2: + print("no argument give. Please give word") + return + if sys.argv[2] == "device": + get_id_device() + return + if sys.argv[2] == "pause": + pause() + return + if sys.argv[2] == "resume": + resume() + return + else: + print("no argument give. Please give word") From 4f8310da912ecefef17f82bda21b35a8d5d4f683 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 15:16:35 +0200 Subject: [PATCH 23/28] feat!: add resume --- src/music/resume.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/music/resume.py diff --git a/src/music/resume.py b/src/music/resume.py new file mode 100644 index 0000000..d09bcf8 --- /dev/null +++ b/src/music/resume.py @@ -0,0 +1,15 @@ +import requests +from src.music.get_token_tmp import get_token + +def resume(): + access_token = get_token() + url = "https://api.spotify.com/v1/me/player/play" + header = {"Authorization": "Bearer " + access_token, "Content-Type": "application/json"} + data = {} + result = requests.put(url, headers=header, json=data) + try: + body = result.json() + except Exception: + body = result.text + print(body) + print(result.status_code) \ No newline at end of file From 2cc6cd7ba420649167af746a07cd44d22f82ea1b Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 15:16:52 +0200 Subject: [PATCH 24/28] feat!: add pause --- src/music/pause.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/music/pause.py diff --git a/src/music/pause.py b/src/music/pause.py new file mode 100644 index 0000000..abd6c88 --- /dev/null +++ b/src/music/pause.py @@ -0,0 +1,15 @@ +import requests +from src.music.get_token_tmp import get_token + +def pause(): + access_token = get_token() + url = "https://api.spotify.com/v1/me/player/pause" + header = {"Authorization": "Bearer " + access_token} + + result = requests.put(url, headers=header) + try: + body = result.json() + except Exception: + body = result.text + print(body) + print(result.status_code) \ No newline at end of file From 5a0822a1c05c4f82eae278b0efcd3fcb4c2baf3f Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 23:07:23 +0200 Subject: [PATCH 25/28] feat!: add light control with command --- src/light/light.py | 15 +++++++++++++++ src/light/toggle_light.py | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/light/light.py create mode 100644 src/light/toggle_light.py diff --git a/src/light/light.py b/src/light/light.py new file mode 100644 index 0000000..e9fbb57 --- /dev/null +++ b/src/light/light.py @@ -0,0 +1,15 @@ +import os +import sys +from src.light.toggle_light import toggle_light + +def light_command(): + nb = len(sys.argv) + + if nb <= 2: + print("no argument give. Please give word") + return + if sys.argv[2] == "toggle": + toggle_light() + return + else: + print("no argument give. Please give word") \ No newline at end of file diff --git a/src/light/toggle_light.py b/src/light/toggle_light.py new file mode 100644 index 0000000..75e45c0 --- /dev/null +++ b/src/light/toggle_light.py @@ -0,0 +1,18 @@ +import os +import subprocess + +FIFO_PATH = "/tmp/kitty_shell_input" +CMD_LIGHT = "./ecc-api --toggle" + +def toggle_light(): + if not os.path.exists(FIFO_PATH): + print(f"Sub-Agent: FIFO introuvable: {FIFO_PATH}") + return + with open(FIFO_PATH, "w") as fifo: + fifo.write(CMD_LIGHT + "\n") + subprocess.run(CMD_LIGHT) + return + +def toggle_light(): + subprocess.run(CMD_LIGHT) + return \ No newline at end of file From 052bf97701456efd40e2249b1da70ab92fb10c89 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 23:09:59 +0200 Subject: [PATCH 26/28] docs: fix wrong name of this project --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 60d849b..0719210 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# cpOS -cpOS ? +# ctOS +ctOS ? This is Central Tranquillity Operating System, the cli for Tranquillity From e52cb50fd60db4b9e9209466e76a0081a0ecc2a1 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 23:12:47 +0200 Subject: [PATCH 27/28] fix: rename file for real name --- cpOS.py => ctOS.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cpOS.py => ctOS.py (100%) diff --git a/cpOS.py b/ctOS.py similarity index 100% rename from cpOS.py rename to ctOS.py From 29b86c7ca2fb404994f7941f7be7f69780521441 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 2 Jul 2026 23:15:35 +0200 Subject: [PATCH 28/28] docs: fix description with good name of this project --- src/help.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/help.py b/src/help.py index 12c9aeb..7754ea6 100644 --- a/src/help.py +++ b/src/help.py @@ -1,7 +1,7 @@ def help(): - print("Welcome to cpOS\n") - print("Usage : cpOS [command]") - print(" cpOS [flags]\n") + print("Welcome to ctOS, the cli for Tranquillity\n") + print("Usage : ctOS [command]") + print(" ctOS [flags]\n") print("[command] :\n") print(" --light toggle (toggle connected light)") print(" --music (use command for music)") \ No newline at end of file