{"id":6151,"date":"2026-02-16T17:08:48","date_gmt":"2026-02-16T16:08:48","guid":{"rendered":"https:\/\/gpmfactory.com\/?p=6151"},"modified":"2026-02-16T19:35:59","modified_gmt":"2026-02-16T18:35:59","slug":"transformer-un-pc-windows-en-box-tv-controlable-a-distance","status":"publish","type":"post","link":"https:\/\/gpmfactory.com\/index.php\/2026\/02\/16\/transformer-un-pc-windows-en-box-tv-controlable-a-distance\/","title":{"rendered":"Transformer un PC Windows en Box TV contr\u00f4lable \u00e0 distance"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Introduction<\/h1>\n\n\n\n<p>Dans le cadre d&rsquo;un projet personnel, j&rsquo;ai d\u00e9velopp\u00e9 une application web permettant de transformer n&rsquo;importe quel PC Windows en box TV compl\u00e8te pour un environnement <strong>Free Box<\/strong>, contr\u00f4lable depuis un smartphone. Le syst\u00e8me offre un acc\u00e8s \u00e0 28 cha\u00eenes de t\u00e9l\u00e9vision gratuites et propose une exp\u00e9rience utilisateur fluide comparable aux solutions commerciales.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Contexte du projet<\/h2>\n\n\n\n<p>L&rsquo;objectif initial \u00e9tait simple : pouvoir regarder la t\u00e9l\u00e9vision sur mon PC de travail pendant les pauses, avec un contr\u00f4le \u00e0 distance depuis mon smartphone pour \u00e9viter les allers-retours constants entre le bureau et l&rsquo;\u00e9cran. Plut\u00f4t que d&rsquo;investir dans une box TV ou un abonnement suppl\u00e9mentaire, j&rsquo;ai choisi de d\u00e9velopper une solution sur mesure exploitant les ressources d\u00e9j\u00e0 disponibles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Architecture technique<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Stack technologique<\/h3>\n\n\n\n<p>L&rsquo;application repose sur une architecture Node.js classique :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Backend<\/strong> : Express.js pour l&rsquo;API REST<\/li>\n\n\n\n<li><strong>Frontend<\/strong> : HTML\/CSS\/JavaScript vanilla (pas de framework)<\/li>\n\n\n\n<li><strong>Automation<\/strong> : Puppeteer-core pour le contr\u00f4le du navigateur<\/li>\n\n\n\n<li><strong>System Control<\/strong> : NirCmd pour les interactions syst\u00e8me Windows<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Fonctionnement<\/h3>\n\n\n\n<p>Le serveur Node.js expose une API REST accessible depuis le r\u00e9seau local. L&rsquo;interface web, optimis\u00e9e pour mobile, communique avec le serveur pour contr\u00f4ler une instance Chrome en mode kiosk qui diffuse les flux vid\u00e9o.<\/p>\n\n\n\n<p>Le choix de Puppeteer-core plut\u00f4t qu&rsquo;un simple spawn de processus permet d&rsquo;obtenir des transitions instantan\u00e9es entre cha\u00eenes (environ 500ms) gr\u00e2ce \u00e0 la navigation programm\u00e9e dans la m\u00eame instance de navigateur.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Exemple simplifi\u00e9 de changement de cha\u00eene\nasync function navigateToChannel(url) {\n    await page.goto(url, { \n        waitUntil: 'networkidle2', \n        timeout: 30000 \n    });\n}\n\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>ARCHITECTURE DU SYST\u00c8ME\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502                    R\u00c9SEAU WI-FI                          \u2502\n\u2502                                                          \u2502\n\u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510                   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502\n\u2502  \u2502  SMARTPHONE  \u2502                   \u2502   PC WINDOWS 11 \u2502  \u2502\n\u2502  \u2502              \u2502   HTTP Request    \u2502                 \u2502  \u2502\n\u2502  \u2502 &#91;Interface]  \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25b6 \u2502  &#91;Listener]    \u2502  \u2502\n\u2502  \u2502 T\u00e9l\u00e9commande \u2502   Port 3000       \u2502  Node.js        \u2502  \u2502\n\u2502  \u2502              \u2502                   \u2502  Express        \u2502  \u2502\n\u2502  \u2502              \u2502\u25c0\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500  \u2502                \u2502  \u2502 \n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   R\u00e9ponse JSON    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502\n\u2502                                              \u2502           \u2502\n\u2502                                              \u2502           \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                                               \u2502\n                                               \u2502 Commandes\n                                               \u25bc\n                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502       SYST\u00c8ME D'EXPLOITATION         \u2502\n                    \u2502                                      \u2502\n                    \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510                  \u2502\n                    \u2502  \u2502  Chrome.exe    \u2502  (Instance TV)   \u2502\n                    \u2502  \u2502  --kiosk\/      \u2502                  \u2502\n                    \u2502  \u2502  --window      \u2502                  \u2502\n                    \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                  \u2502\n                    \u2502                                      \u2502\n                    \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510                  \u2502\n                    \u2502  \u2502 DisplaySwitch  \u2502  (si config.)    \u2502\n                    \u2502  \u2502 Mode Duplicate \u2502                  \u2502\n                    \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                  \u2502\n                    \u2502                                      \u2502\n                    \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510                  \u2502\n                    \u2502  \u2502 PowerShell API \u2502  (si config.)    \u2502\n                    \u2502  \u2502 Prevent Sleep  \u2502                  \u2502\n                    \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                  \u2502\n                    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                                  \u2502\n                                  \u2502 Affichage\n                                  \u25bc\n                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502                                      \u2502\n                    \u2502    &#91;\u00c9cran PC]    &#91;\u00c9cran HDMI]        \u2502\n                    \u2502        \u2551               \u2551             \u2502\n                    \u2502        \u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d             \u2502\n                    \u2502          (Mode Duplicate)            \u2502\n                    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Fonctionnalit\u00e9s principales<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Contr\u00f4le \u00e0 distance<\/h3>\n\n\n\n<p>L&rsquo;interface mobile propose :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>S\u00e9lection parmi 28 cha\u00eenes TNT gratuites<\/li>\n\n\n\n<li>Contr\u00f4le du volume syst\u00e8me (4 niveaux : 0%, 30%, 50%, 75%)<\/li>\n\n\n\n<li>D\u00e9marrage et arr\u00eat du mode TV<\/li>\n\n\n\n<li>\u00c9tat en temps r\u00e9el de l&rsquo;application<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Optimisations syst\u00e8me<\/h3>\n\n\n\n<p>Plusieurs m\u00e9canismes ont \u00e9t\u00e9 mis en place pour une exp\u00e9rience optimale :<\/p>\n\n\n\n<p><strong>Gestion d&rsquo;affichage<\/strong> : Basculement automatique en mode \u00ab\u00a0duplicate\u00a0\u00bb via DisplaySwitch.exe pour projeter sur un second \u00e9cran si n\u00e9cessaire.<\/p>\n\n\n\n<p><strong>Audio<\/strong> : S\u00e9lection automatique du p\u00e9riph\u00e9rique audio configur\u00e9 et activation du son en cas de d\u00e9sactivation accidentelle.<\/p>\n\n\n\n<p><strong>Fen\u00eatrage<\/strong> : La fen\u00eatre Chrome est d\u00e9finie en \u00ab\u00a0always on top\u00a0\u00bb (HWND_TOPMOST) via l&rsquo;API Windows pour rester visible en permanence.<\/p>\n\n\n\n<p><strong>\u00c9nergie<\/strong> : D\u00e9sactivation de la mise en veille pendant la diffusion via SetThreadExecutionState.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Performance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Changement de cha\u00eene : environ 500ms (navigation simple)<\/li>\n\n\n\n<li>Pas d&rsquo;interruption visuelle (pas de flash noir)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">D\u00e9fis techniques rencontr\u00e9s<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Gestion du focus fen\u00eatre<\/h3>\n\n\n\n<p>Le principal d\u00e9fi a \u00e9t\u00e9 de maintenir la fen\u00eatre Chrome au premier plan de mani\u00e8re fiable. Plusieurs approches ont \u00e9t\u00e9 test\u00e9es :<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>SetForegroundWindow<\/strong> : Insuffisant en mode kiosk, le focus \u00e9tait parfois perdu<\/li>\n\n\n\n<li><strong>Minimisation des autres fen\u00eatres<\/strong> : Fonctionnel mais perturbant pour le workflow<\/li>\n\n\n\n<li><strong>HWND_TOPMOST<\/strong> : Solution finale retenue, combin\u00e9e au mode kiosk<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u00c9limination des popups ind\u00e9sirables<\/h3>\n\n\n\n<p>Chrome affiche par d\u00e9faut plusieurs \u00e9l\u00e9ments g\u00eanants en automatisation :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Barre \u00ab\u00a0Chrome est contr\u00f4l\u00e9 par un logiciel de test automatis\u00e9\u00a0\u00bb<\/li>\n\n\n\n<li>Popups de traduction automatique<\/li>\n\n\n\n<li>D\u00e9tection du flag navigator.webdriver<\/li>\n<\/ul>\n\n\n\n<p>Ces \u00e9l\u00e9ments ont \u00e9t\u00e9 d\u00e9sactiv\u00e9s via une combinaison d&rsquo;arguments Chrome (<code>--exclude-switches=enable-automation<\/code>), de pr\u00e9f\u00e9rences de profil, et d&rsquo;injection JavaScript (<code>navigator.webdriver = undefined<\/code>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gestion de l&rsquo;\u00e9tat<\/h3>\n\n\n\n<p>Le syst\u00e8me maintient la coh\u00e9rence entre l&rsquo;\u00e9tat du serveur et celui de l&rsquo;interface :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>V\u00e9rification p\u00e9riodique du statut (polling toutes les 5 secondes)<\/li>\n\n\n\n<li>Synchronisation automatique en cas de d\u00e9connexion\/reconnexion<\/li>\n\n\n\n<li>Nettoyage proper des ressources \u00e0 l&rsquo;arr\u00eat<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Progressive Web App<\/h2>\n\n\n\n<p>L&rsquo;application est configur\u00e9e comme PWA (Progressive Web App) avec :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Manifest.json pour l&rsquo;installation sur l&rsquo;\u00e9cran d&rsquo;accueil<\/li>\n\n\n\n<li>Ic\u00f4nes adaptatives (192&#215;192 et 512&#215;512)<\/li>\n\n\n\n<li>Mode standalone (plein \u00e9cran sans barre d&rsquo;adresse)<\/li>\n\n\n\n<li>M\u00e9ta-tags pour iOS et Android<\/li>\n<\/ul>\n\n\n\n<p>Cela permet une exp\u00e9rience proche d&rsquo;une application native lorsqu&rsquo;elle est ajout\u00e9e \u00e0 l&rsquo;\u00e9cran d&rsquo;accueil du smartphone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation et d\u00e9ploiement<\/h2>\n\n\n\n<p>Le d\u00e9ploiement est volontairement simple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Installation des d\u00e9pendances\nnpm install puppeteer-core\n\n# Configuration (fichier JSON)\n# - Liste des cha\u00eenes\n# - Param\u00e8tres syst\u00e8me (audio, display, etc.)\n\n# Lancement\nnode tv-remote-server.js<\/code><\/pre>\n\n\n\n<p>L&rsquo;application \u00e9coute sur le port 3000 et est accessible depuis n&rsquo;importe quel appareil du r\u00e9seau local. Pour une utilisation r\u00e9guli\u00e8re, un service Windows ou un script de d\u00e9marrage automatique peut \u00eatre configur\u00e9.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Limitations et am\u00e9liorations futures<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Limitations actuelles<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fonctionne uniquement sur Windows (d\u00e9pendances syst\u00e8me sp\u00e9cifiques)<\/li>\n\n\n\n<li>N\u00e9cessite Chrome install\u00e9 sur le syst\u00e8me<\/li>\n\n\n\n<li>Limit\u00e9 aux cha\u00eenes gratuites disponibles sur tv.free.fr<\/li>\n\n\n\n<li>Pas de gestion multi-utilisateurs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Pistes d&rsquo;am\u00e9lioration<\/h3>\n\n\n\n<p>Plusieurs \u00e9volutions pourraient \u00eatre envisag\u00e9es :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Support multi-plateforme (Linux, macOS)<\/li>\n\n\n\n<li>Enregistrement de programmes<\/li>\n\n\n\n<li>Programmation d&rsquo;enregistrements<\/li>\n\n\n\n<li>Picture-in-Picture<\/li>\n\n\n\n<li>Contr\u00f4le vocal<\/li>\n\n\n\n<li>Historique de visionnage<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Ce projet illustre comment une combinaison de technologies web standards et d&rsquo;APIs syst\u00e8me peut produire une solution fonctionnelle et performante pour un besoin sp\u00e9cifique. L&rsquo;approche modulaire et l&rsquo;utilisation de Puppeteer offrent une flexibilit\u00e9 importante pour des \u00e9volutions futures.<\/p>\n\n\n\n<p>Le code source complet et la documentation technique sont disponibles sur demande pour les personnes int\u00e9ress\u00e9es par les aspects d&rsquo;impl\u00e9mentation d\u00e9taill\u00e9s.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>D\u00e9velopp\u00e9 en f\u00e9vrier 202<\/em>6<br><em>Stack : Node.js, Express, Puppeteer-core, Vanilla JavaScript<\/em><br><em>Plateforme : Windows 10\/11<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Dans le cadre d&rsquo;un projet personnel, j&rsquo;ai d\u00e9velopp\u00e9 une application web permettant de transformer n&rsquo;importe quel PC Windows en box TV compl\u00e8te pour&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"ppma_author":[150],"class_list":["post-6151","post","type-post","status-publish","format-standard","hentry","category-non-classe"],"authors":[{"term_id":150,"user_id":1,"is_guest":0,"slug":"admin8700","display_name":"Patrick","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/209d5ed69b74d288390621ab4c1d3773?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/6151","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/comments?post=6151"}],"version-history":[{"count":3,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/6151\/revisions"}],"predecessor-version":[{"id":6155,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/6151\/revisions\/6155"}],"wp:attachment":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/media?parent=6151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/categories?post=6151"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/tags?post=6151"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/ppma_author?post=6151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}