Skip to content

Latest commit

 

History

History
1664 lines (1196 loc) · 23.3 KB

File metadata and controls

1664 lines (1196 loc) · 23.3 KB

CrystalSpace — Fiche de spécifications

1. Présentation

Nom du projet : CrystalSpace
Plateforme : macOS
Langage : Swift
UI : SwiftUI + AppKit lorsque nécessaire
Version minimale : macOS 15
Architecture : application macOS 100 % native
Backend principal : API compatible Wallspace
IA : strictement aucune

CrystalSpace est une application native macOS permettant d’utiliser des vidéos comme fonds d’écran animés, de parcourir une bibliothèque distante compatible avec Wallspace, de télécharger les contenus localement et de gérer indépendamment plusieurs écrans.

L’objectif n’est pas de faire un simple clone graphique de Wallspace mais une implémentation propre, modulaire et open-source-friendly capable de consommer les mêmes ressources/API tout en restant indépendante de Wallspace.


2. Principes du projet

CrystalSpace doit privilégier :

  • performances natives ;
  • consommation CPU minimale ;
  • consommation mémoire contrôlée ;
  • absence totale de compte obligatoire ;
  • absence de télémétrie ;
  • absence de publicité ;
  • fonctionnement hors ligne pour les wallpapers téléchargés ;
  • architecture modulaire ;
  • support multi-écrans ;
  • support des vidéos locales ;
  • compatibilité avec l’API utilisée par Wallspace ;
  • aucun composant d’intelligence artificielle.

Interdictions

CrystalSpace ne doit contenir :

  • aucun LLM ;
  • aucun modèle Core ML servant à la génération ou recherche sémantique ;
  • aucune API OpenAI, Gemini, Claude, etc. ;
  • aucune génération d'image ;
  • aucune recommandation basée sur ML ;
  • aucun moteur de recherche sémantique ;
  • aucun tracking utilisateur ;
  • aucun identifiant publicitaire.

La recherche repose exclusivement sur des informations déterministes :

  • titre ;
  • tags ;
  • catégorie ;
  • auteur ;
  • résolution ;
  • popularité ;
  • date ;
  • identifiant.

3. Fonctionnalités principales

3.1 Home

Écran d'accueil présentant des collections provenant de l'API.

Sections possibles :

  • Featured
  • Popular
  • New
  • Recently Added
  • Trending
  • Categories
  • Continue Watching / Recently Viewed
  • Favorites
  • Downloaded

Le contenu des sections doit être fourni par le backend lorsque disponible.

L'interface ne doit pas dépendre de noms de catégories codés en dur.


3.2 Explore

Vue permettant de parcourir toute la bibliothèque Wallspace.

Fonctionnalités :

  • grille adaptative ;
  • chargement paginé ;
  • infinite scrolling ;
  • aperçu animé au survol ;
  • filtre par catégorie ;
  • filtre par résolution ;
  • filtre par ratio ;
  • filtre vidéo/statique si l'API en expose ;
  • tri ;
  • recherche texte classique.

Tri possible :

Popular
Most liked
Newest
Oldest
Recently updated
Name

3.3 Recherche

Recherche locale/distante classique.

Exemple :

space

peut correspondre à :

title CONTAINS "space"
tags CONTAINS "space"
category == "space"

La recherche doit utiliser l'endpoint Wallspace lorsqu'il existe.

Sinon CrystalSpace peut effectuer un filtrage local des métadonnées déjà récupérées.

Aucune interprétation sémantique du texte.


3.4 Page Wallpaper

Chaque wallpaper dispose d'une fiche complète.

Afficher :

Preview
Title
Description
Author
Category
Tags
Mood
Resolution
Aspect ratio
Duration
FPS
File size
Popularity
Likes
Download status

Actions :

Apply
Download
Remove Download
Favorite
Unfavorite
Preview
Open Fullscreen
Apply to Display...
Apply to All Displays
Add to Playlist
Open Source

Toutes les propriétés doivent être optionnelles au niveau du modèle Swift pour supporter les différences de versions de l'API.


4. Wallpapers vidéo

Formats minimum acceptés :

MP4
MOV
M4V

Décodage :

AVFoundation
AVPlayer
AVPlayerItem
AVPlayerLayer

Priorité au décodage matériel via VideoToolbox lorsque disponible.

CrystalSpace ne doit pas réencoder une vidéo distante si elle peut être lue directement.

Lecture :

loop = true
audio = muted
playback rate = 1.0

Audio toujours désactivé pour un wallpaper.


5. Moteur de rendu Desktop

Créer un moteur indépendant :

protocol WallpaperRenderer {
    func start()
    func pause()
    func resume()
    func stop()

    func load(_ wallpaper: LocalWallpaper) async throws
}

Implémentation principale :

AVPlayerWallpaperRenderer

Chaque écran peut posséder son propre renderer.

Architecture :

WallpaperEngine
 ├── DisplaySession
 │    ├── NSScreen
 │    ├── WallpaperWindow
 │    └── WallpaperRenderer
 │
 ├── DisplaySession
 │    └── ...
 │
 └── PowerController

6. WallpaperWindow

Chaque écran possède une fenêtre spéciale AppKit.

final class WallpaperWindow: NSWindow

Propriétés attendues :

borderless
non-activating
no shadow
transparent
non movable
ignores mouse events

Elle doit rester derrière les fenêtres normales et ne jamais apparaître dans Cmd+Tab.

CrystalSpace doit gérer :

  • changement de résolution ;
  • branchement écran ;
  • débranchement écran ;
  • changement de Spaces ;
  • changement d'écran principal ;
  • sortie de veille.

7. Multi-écrans

Utiliser :

NSScreen.screens

Chaque écran possède un identifiant persistant indépendant de son ordre dans NSScreen.screens.

Modèle :

struct DisplayConfiguration: Codable {
    let displayID: String
    var wallpaperID: String?
    var playlistID: UUID?
    var playbackMode: PlaybackMode
}

Modes :

Independent
Mirror Primary
Same Wallpaper
Playlist
Disabled

CrystalSpace doit continuer de fonctionner avec au moins 7 écrans.


8. Détection des écrans

Ne jamais utiliser uniquement :

NSScreen.localizedName

comme identifiant.

Créer un DisplayManager utilisant les informations CoreGraphics disponibles pour générer une identité persistante.

Exemple :

vendor
product
serial
CGDirectDisplayID

9. Support Spaces macOS

CrystalSpace doit fonctionner sur plusieurs Spaces.

Lorsqu'un utilisateur change de Space :

  • le wallpaper ne doit pas disparaître ;
  • la vidéo ne doit pas redémarrer inutilement ;
  • la lecture doit rester synchronisée.

Prévoir une couche :

SpaceCoordinator

responsable de la relation entre les fenêtres wallpaper et Mission Control.

Éviter autant que possible l'automatisation via System Events/AppleScript.


10. Lock Screen

Fonctionnalité distincte du moteur Desktop.

LockScreenService

Support complet ciblé :

macOS 26+

Sur macOS 15 :

Desktop live wallpaper : oui
Lock Screen live wallpaper : non ou fallback documenté

Ne jamais faire échouer l'application entière lorsque la fonction n'est pas disponible.

API :

protocol LockScreenProvider {
    var isSupported: Bool { get }

    func apply(_ wallpaper: LocalWallpaper) async throws
    func remove() async throws
}

11. Vidéos personnelles

Section :

Library → Local

Import par :

File Picker
Drag & Drop
Open With

CrystalSpace conserve seulement un bookmark de sécurité lorsque possible.

Utiliser :

security-scoped bookmarks

Informations extraites avec AVFoundation :

duration
resolution
fps
codec
bitrate
hasAudio
fileSize

L'audio n'est jamais lu.


12. API Wallspace

Objectif

CrystalSpace doit pouvoir utiliser la bibliothèque fournie par Wallspace à travers son API actuelle.

L'API étant non documentée publiquement, elle doit être considérée comme :

External
Unstable
Versionless until proven otherwise

Toute interaction Wallspace doit passer par un module indépendant.

CrystalSpace
      │
      ▼
WallpaperRepository
      │
      ▼
WallpaperProvider
      │
 ┌────┴────────┐
 │             │
 ▼             ▼
WallSpaceAPI   LocalProvider

13. WallpaperProvider

protocol WallpaperProvider: Sendable {
    func home() async throws -> HomeFeed

    func wallpapers(
        page: Int,
        filters: WallpaperFilters
    ) async throws -> WallpaperPage

    func wallpaper(id: String) async throws -> Wallpaper

    func search(
        query: String,
        page: Int
    ) async throws -> WallpaperPage

    func categories() async throws -> [WallpaperCategory]

    func downloadURL(
        for wallpaper: Wallpaper,
        quality: WallpaperQuality
    ) async throws -> URL
}

Wallspace devient donc simplement :

final actor WallSpaceAPIProvider: WallpaperProvider

14. Compatibilité API Wallspace

Le client HTTP doit pouvoir reproduire les requêtes nécessaires à :

Home feed
Wallpaper listing
Wallpaper metadata
Categories
Search
Popular
Newest
Wallpaper details
Preview URLs
Thumbnail URLs
Video URLs
Download URLs
Pagination

Si l'API expose également :

likes
community
upload
collections

leur prise en charge peut être ajoutée séparément.

Ne jamais disperser les URLs Wallspace dans le projet.

Centraliser :

struct WallSpaceEndpoints

15. API reverse compatibility

Le client doit être tolérant aux changements du backend.

Utiliser des structures Decodable séparées des modèles applicatifs.

Exemple :

WallSpaceWallpaperDTO
          │
          ▼
WallpaperMapper
          │
          ▼
Wallpaper

Jamais :

API JSON
   ↓
UI directement

Ainsi un changement de clé Wallspace n'affecte que le module WallSpaceAPI.


16. Modèle Wallpaper

struct Wallpaper: Identifiable, Hashable, Sendable {
    let id: String

    let title: String
    let description: String?

    let thumbnailURL: URL?
    let previewURL: URL?

    let author: WallpaperAuthor?

    let tags: [String]
    let categories: [WallpaperCategory]

    let mood: String?

    let variants: [WallpaperVariant]

    let createdAt: Date?
    let updatedAt: Date?

    let popularity: Int?
    let likes: Int?
}

17. Variantes

struct WallpaperVariant: Hashable, Sendable {
    let quality: WallpaperQuality

    let width: Int
    let height: Int

    let fps: Double?

    let codec: String?

    let size: Int64?

    let url: URL?
}

Qualités :

enum WallpaperQuality {
    case hd
    case qhd
    case uhd4K
    case original
    case unknown(String)
}

Il faut accepter de nouvelles qualités sans casser le décodage.


18. HTTP Client

Foundation uniquement.

URLSession

Architecture :

HTTPClient
   │
   ├── request()
   ├── download()
   ├── retry()
   └── cancellation

Utiliser :

async/await
actors
Sendable
URLSession
Codable

Éviter les bibliothèques HTTP tierces sauf nécessité réelle.


19. Cache API

Deux niveaux.

Memory

NSCache

Pour :

  • thumbnails ;
  • previews ;
  • réponses API fréquemment utilisées.

Disk

Pour :

  • metadata JSON ;
  • thumbnails ;
  • wallpapers téléchargés.

20. Download Manager

Créer :

actor DownloadManager

Responsabilités :

queue
pause
resume
cancel
retry
progress
deduplication
storage
verification

Support de :

URLSessionDownloadTask

Un téléchargement interrompu doit pouvoir reprendre lorsque le serveur supporte HTTP Range.


21. Stockage local

Structure proposée :

~/Library/Application Support/CrystalSpace/

Database/
    CrystalSpace.sqlite

Cache/
    Metadata/
    Thumbnails/
    Previews/

Wallpapers/
    <wallpaper-id>/
        metadata.json
        wallpaper.mp4
        thumbnail.webp

Local/

Playlists/

22. Base de données

Utiliser SQLite.

Option recommandée :

GRDB.swift

ou wrapper SQLite minimal maison.

Tables :

wallpapers
downloads
favorites
playlists
playlist_items
display_configs
local_wallpapers
history
settings

Ne pas stocker inutilement une copie intégrale de toutes les métadonnées du serveur.


23. Favoris

Favoris locaux par défaut.

FavoriteStore

Aucun compte nécessaire.

Structure :

wallpaperID
provider
createdAt

Cela permet à terme :

provider = wallspace
provider = local
provider = crystalspace

24. Playlists

Création de playlists locales.

struct WallpaperPlaylist {
    let id: UUID
    var name: String
    var wallpapers: [WallpaperReference]
    var configuration: PlaylistConfiguration
}

Modes :

Sequential
Shuffle

Déclencheurs :

Every X minutes
Every X hours
On wake
On login
Manual

Aucune sélection intelligente.


25. Menu Bar

CrystalSpace doit pouvoir fonctionner principalement depuis la barre de menu.

Menu :

CrystalSpace
─────────────

Current Wallpaper

Change Wallpaper
Next
Previous

Pause
Resume

Displays >
    MacBook Display
    External Display

Playlists >

Open CrystalSpace

─────────────

Performance Mode >
    Automatic
    Quality
    Balanced
    Battery Saver

─────────────

Clear Memory Cache
Clear Disk Cache

Settings
Quit CrystalSpace

26. Performance

Objectifs indicatifs :

Idle

CPU : ≈ 0 %

Wallpaper vidéo actif

Objectif :

< 2 % CPU moyen

sur Apple Silicon dans des conditions normales.

Le GPU/VideoToolbox doit faire l'essentiel du décodage.

Mémoire cible :

< 150 MB

hors caches vidéo système importants.


27. Optimisations

Ne jamais décoder une vidéo lorsque :

  • l'écran est éteint ;
  • le Mac est verrouillé et aucun lock-screen animé n'est actif ;
  • un écran n'affiche pas le wallpaper ;
  • le système est en veille.

Option utilisateur :

Pause on battery

Option :

Pause when Low Power Mode is enabled

Option :

Pause while fullscreen application is running

28. PowerManager

Créer :

PowerManager

Observer :

battery state
AC power
Low Power Mode
sleep
wake
display sleep
display wake

Le moteur reçoit ensuite :

.pause(reason:)
.resume(reason:)

29. Performance Modes

Quality

Maximum resolution
Maximum FPS

Balanced

Native screen resolution
Normal FPS

Battery Saver

Lower resolution variant
30 FPS maximum when applicable
pause on battery option

Automatic

Choix déterministe selon :

power source
screen resolution
thermal state

Aucune IA.


30. Thermal Management

Observer :

ProcessInfo.processInfo.thermalState

Réactions :

.nominal
    → normal

.fair
    → normal/balanced

.serious
    → reduce workload

.critical
    → pause animated wallpaper

31. UI générale

Style :

native macOS
modern
glass
dark/light
large thumbnails
minimal chrome

Utiliser autant que possible les matériaux macOS :

Material
VisualEffect
Glass

Ne pas recréer artificiellement l'apparence de macOS avec des composants web.


32. Navigation

Sidebar proposée :

Home

Discover
 ├── Explore
 ├── Popular
 └── New

Library
 ├── Favorites
 ├── Downloads
 ├── Local
 └── Playlists

Settings

33. Preview

Au survol d'une carte :

Image → short silent video preview

Ne pas charger immédiatement les previews de toute la grille.

Pipeline :

thumbnail
    ↓ hover ~300 ms
request preview
    ↓
play
    ↓ mouse exit
stop
release

Limiter le nombre de previews simultanées.


34. Deep Links

CrystalSpace doit posséder son propre scheme :

crystalspace://

Exemples :

crystalspace://wallpaper/123
crystalspace://search?q=space
crystalspace://playlist/UUID

Prévoir également un parseur pouvant reconnaître les liens publics Wallspace.

Exemple conceptuel :

https://wallspace.app/wallpaper/...

Lorsqu'un identifiant Wallspace peut être extrait :

Wallspace URL
    ↓
WallSpaceLinkParser
    ↓
WallpaperReference
    ↓
WallSpaceAPIProvider

35. Réseau

CrystalSpace doit fonctionner sans connexion pour :

wallpapers téléchargés
local wallpapers
favorites locaux
playlists locales
display configuration

Si Wallspace est inaccessible :

No Connection

mais le moteur wallpaper continue de fonctionner.

Une panne API ne doit jamais retirer le wallpaper courant.


36. Sécurité

Pas de :

eval
embedded browser authentication
remote code execution
downloaded executable
dynamic plugin from server

Les fichiers distants sont considérés comme non fiables.

Vérifier :

MIME
extension
container
file size
dimensions
video duration

avant utilisation.


37. Permissions

Minimiser les permissions macOS.

Ne demander une autorisation que lorsqu'une fonction l'exige réellement.

Éviter notamment une dépendance obligatoire à :

System Events
AppleScript
Accessibility
Screen Recording

CrystalSpace doit continuer de fonctionner sans ces permissions.


38. Privacy

CrystalSpace ne génère aucun :

device ID
advertising ID
user fingerprint
analytics ID

Pas de :

Google Analytics
Firebase Analytics
Sentry telemetry
Mixpanel
PostHog
Amplitude

Les requêtes strictement nécessaires au téléchargement de contenu restent évidemment visibles par le serveur Wallspace.


39. Settings

General

Launch at Login
Show Menu Bar Icon
Check for Updates
Default Display

Playback

Performance Mode
Pause on Battery
Pause in Low Power Mode
Pause when Display Sleeps
Pause during Fullscreen Apps

Downloads

Default Quality
Storage Location
Maximum Cache Size

Displays

Configuration indépendante pour chaque écran.

API

Wallspace Provider Enabled
API Base URL
Clear API Cache

L'URL personnalisable est utile pour le développement et pour une future API CrystalSpace compatible.

Privacy

Simple écran rappelant :

No analytics
No account
No AI

40. Architecture projet

CrystalSpace/
│
├── App/
│   ├── CrystalSpaceApp.swift
│   ├── AppDelegate.swift
│   └── AppState.swift
│
├── Core/
│   ├── Models/
│   ├── Networking/
│   ├── Storage/
│   ├── Logging/
│   └── Extensions/
│
├── Providers/
│   ├── WallpaperProvider.swift
│   │
│   ├── WallSpace/
│   │   ├── WallSpaceAPIProvider.swift
│   │   ├── WallSpaceEndpoints.swift
│   │   ├── WallSpaceDTO.swift
│   │   ├── WallSpaceMapper.swift
│   │   └── WallSpaceLinkParser.swift
│   │
│   └── Local/
│       └── LocalWallpaperProvider.swift
│
├── WallpaperEngine/
│   ├── WallpaperEngine.swift
│   ├── WallpaperWindow.swift
│   ├── DisplayManager.swift
│   ├── DisplaySession.swift
│   ├── SpaceCoordinator.swift
│   ├── PowerManager.swift
│   └── Renderers/
│       └── AVPlayerWallpaperRenderer.swift
│
├── LockScreen/
│   └── LockScreenService.swift
│
├── Downloads/
│   └── DownloadManager.swift
│
├── Database/
│   ├── DatabaseManager.swift
│   ├── Migrations/
│   └── Records/
│
├── Features/
│   ├── Home/
│   ├── Explore/
│   ├── Search/
│   ├── WallpaperDetails/
│   ├── Favorites/
│   ├── Downloads/
│   ├── Local/
│   ├── Playlists/
│   └── Settings/
│
└── MenuBar/
    └── MenuBarController.swift

41. Concurrence Swift

Utiliser Swift Concurrency partout où cela a du sens.

async/await
Task
TaskGroup
Actor
@MainActor
Sendable

Exemples d'actors :

WallSpaceAPIProvider
DownloadManager
WallpaperCache
DatabaseManager

L'UI reste :

@MainActor

42. Logging

Utiliser :

import OSLog

Catégories :

API
Downloads
Playback
Displays
Database
Cache
LockScreen
Power

Ne jamais logger :

tokens
signed URLs complets sensibles
identifiants privés
chemins privés inutiles

43. Gestion des erreurs

Créer :

enum CrystalSpaceError: Error {
    case network(NetworkError)
    case provider(ProviderError)
    case playback(PlaybackError)
    case download(DownloadError)
    case storage(StorageError)
    case unsupportedFeature
}

Les erreurs réseau doivent être dissociées des erreurs du moteur wallpaper.


44. Compatibilité future

L'application ne doit jamais supposer que Wallspace sera son unique source.

Grâce à :

WallpaperProvider

on doit pouvoir ajouter ultérieurement :

CrystalSpace API
Local folder
Another public API
Community server

sans modifier le moteur de rendu.


45. Priorités de développement

Phase 1 — Core

SwiftUI shell
AppKit wallpaper window
AVPlayer renderer
single display
local MP4

Phase 2 — Multi-display

DisplayManager
persistent display IDs
independent wallpaper sessions
hotplug handling

Phase 3 — Wallspace API

capture/document actual API
WallSpaceAPIProvider
DTO
pagination
home
explore
search
wallpaper details

Phase 4 — Downloads

download manager
disk cache
offline mode
favorites

Phase 5 — Playlists

playlist storage
scheduler
shuffle
per-display playlists

Phase 6 — System integration

menu bar
launch at login
power management
thermal management
fullscreen detection

Phase 7 — macOS 26

Lock Screen integration
system-specific wallpaper features

46. MVP

Le MVP est considéré comme fonctionnel lorsque CrystalSpace peut :

  1. lancer l'application ;
  2. afficher la bibliothèque Wallspace ;
  3. ouvrir la fiche d'un wallpaper ;
  4. lire son preview ;
  5. télécharger sa vidéo ;
  6. appliquer la vidéo comme wallpaper ;
  7. utiliser une vidéo MP4 locale ;
  8. gérer plusieurs écrans ;
  9. mémoriser les wallpapers configurés ;
  10. restaurer la configuration au démarrage ;
  11. fonctionner hors ligne avec les fichiers déjà téléchargés ;
  12. utiliser moins de ressources qu'une solution basée sur Electron.

47. Non-objectifs

CrystalSpace n'a pas vocation à intégrer :

AI search
AI recommendations
AI generation
image generation
video generation
chatbot
accounts sociaux
ads
tracking
Electron
WebView-based UI

48. Règle fondamentale concernant Wallspace

Aucune partie du moteur CrystalSpace ne doit dépendre directement du format interne de l'API Wallspace.

Toujours respecter :

Wallspace JSON
      ↓
WallSpaceDTO
      ↓
WallSpaceMapper
      ↓
CrystalSpace Model
      ↓
Repository
      ↓
UI / Wallpaper Engine

Cette séparation est obligatoire.

Ainsi, si Wallspace change son API :

CrystalSpace Core → inchangé
UI → inchangée
Wallpaper Engine → inchangé
Database → presque inchangée

WallSpace adapter → mis à jour

49. Philosophie CrystalSpace

CrystalSpace doit suivre cette idée :

Un moteur de wallpapers macOS doit être une couche système légère, pas une plateforme publicitaire ou un moteur de recommandation.

L'application doit rester :

Native.
Fast.
Local-first.
Private.
Deterministic.
No AI.