Conception des Micro-Interactions dans MoviePilot : Optimisation de l'Expérience Utilisateur par des Détails Soignés

Système de Notifications Contextuelles

Le module de notifications de MoviePilot utilise une architecture modulaire pour générer des messages adaptatifs via le moteur de templates Jinja2. La classe GenerateurContexteNotification assemble automatiquement des métadonnées telles que le titre du média, la saison et la note moyenne dans un format cohérent.


def preparer_details_medias(self, infos_media: InfosMedia):
    if not infos_media:
        return
    saison_formatee = f"S{infos_media.saison:02d}" if infos_media.saison else None
    self._contexte.update({
        "titre": infos_media.titre,
        "titre_anglais": infos_media.titre_anglais,
        "annee": infos_media.annee,
        "saison": infos_media.saison,
        "saison_formatee": saison_formatee,
        "note_moyenne": infos_media.note_moyenne,
        "affiche": infos_media.obtenir_image_affiche(),
        "synopsis": infos_media.synopsis
    })

Pour éviter les interruptions intempestives, une file d'attente basée sur des plages horaires est implémentée. La classe GestionnaireFileNotifications vérifie si l'heure actuelle correspond à une fenêtre de diffusion autorisée.


def est_dans_plage_horaire(self, heure_courante: datetime) -> bool:
    if not self.periodes_planifiees:
        return True
    minutes_actuelles = heure_courante.hour * 60 + heure_courante.minute
    for plage in self.periodes_planifiees:
        debut_h, debut_m, fin_h, fin_m = plage
        debut_minutes = debut_h * 60 + debut_m
        fin_minutes = fin_h * 60 + fin_m
        if debut_minutes <= fin_minutes and debut_minutes <= minutes_actuelles <= fin_minutes:
            return True
        if debut_minutes > fin_minutes and (minutes_actuelles >= debut_minutes or minutes_actuelles <= fin_minutes):
            return True
    return False

Mécanisme de Suivi de Progression Visuel

Pour les tâches longues comme le téléchargement ou le transfert de fichiers, la classe AssistantProgression fournit des mises à jour multi-dimensionnelles via une interface publique. Elle permet de combiner un pourcentage d'avancement, un texte descriptif et des données supplémentaires.


def mettre_a_jour(self, valeur: Union[float, int] = None, texte: Optional[str] = None, donnees: dict = None):
    tache_actuelle = self._progression.get(self._cle)
    if not tache_actuelle or not tache_actuelle.get('active'):
        return
    if valeur:
        tache_actuelle['valeur'] = valeur
    if texte:
        tache_actuelle['texte'] = texte
    if donnees:
        tache_actuelle['donnees'].update(donnees)
    self._progression[self._cle] = tache_actuelle

Le cycle de vie d'une tâche est géré automatiquement, avec des transitions d'état claires pour indiquer le démarrage, les phases intermédiaires et la fin.


progression = AssistantProgression(CleProgression.TELECHARGEMENT)
progression.demarrer()
progression.mettre_a_jour(40, "Téléchargement des fichiers sources en cours")
progression.mettre_a_jour(80, "Vérification de l'intégrité des données")
progression.terminer()

Interfaces de Configuration Intelligentes

La classe ParametresSysteme assure une gestion robuste des entrées utilisateur en appliquant des conversions de type automatiques et des validatiosn. Par exemple, les valeurs booléennes sont interprétées à partir de chaînes comme "oui" ou "non".


@staticmethod
def convertisseur_type_generique(valeur: Any, type_attendu: Type, defaut: Any):
    try:
        if type_attendu is bool:
            if isinstance(valeur, str):
                return valeur.lower() in ["vrai", "oui", "1", "actif"]
            return bool(valeur)
        elif type_attendu is int:
            return int(valeur)
    except (ValueError, TypeError):
        logger.avertissement(f"Échec de conversion de la valeur, utilisation de la valeur par défaut : {defaut}")
        return defaut

Lorsqu'une entrée est incorrecte, le système corrige automatiquement et fournit des suggestions. Pour le token d'API, par exemple, un nouveau jeton est généré si la longueur est insuffisante.


@staticmethod
def valider_token_api(valeur: Any, valeur_originale: Any) -> Tuple[Any, bool]:
    valeur = valeur.strip() if isinstance(valeur, str) else None
    if not valeur or len(valeur) < 16:
        nouveau_token = secrets.token_urlsafe(16)
        logger.info(f"Token API manquant ou trop court, génération d'un nouveau jeton : {nouveau_token}")
        return nouveau_token, True
    return valeur, False

Étiquettes: MoviePilot micro-interactions Python jinja2 NAS

Publié le 4 juillet à 22h43