tvg-chno : les numéros de chaîne dans les playlists IPTV, et ce que chaque lecteur en fait
Aucun standard ne définit cet attribut. Une playlist mesurée en porte 1 355 sur 263 valeurs distinctes, et il n’existe pas deux applications qui le lisent et qui s’accordent.
tvg-chno="1" 107 entries
tvg-chno="2" 92
tvg-chno="3" 80
tvg-chno="4" 72
tvg-chno="5" 68
tvg-chno="#" 4
tvg-chno="22.1" 1
Voilà la tête de l’histogramme des numéros de chaîne d’une vraie playlist, avec deux de ses valeurs les plus étranges épinglées en dessous. Le fichier compte 1 895 entrées. 1 355 d’entre elles portent l’attribut, et ces 1 355 numéros couvrent 263 valeurs distinctes, une moyenne de 5.15 entrées par valeur. Le numéro le plus populaire du fichier est revendiqué par 107 chaînes sans rapport entre elles.
Aucun standard ne définit ce que tout cela signifie. Il n’existe pas de spécification pour tvg-chno, pas de registre, pas de propriétaire. L’implémentation de référence le documente en une ligne d’un README, et les applications qui le lisent ne s’accordent ni sur l’orthographe, ni sur le type, ni sur le repli, ni sur l’opportunité d’afficher la valeur.
D’où vient l’attribut, pour autant qu’on puisse le montrer
La RFC 8216 est une soumission de statut Informational plutôt qu’un document de la voie des standards. Elle définit EXTM3U en section 4.3.1.1, EXTINF en section 4.3.2.1 et la famille #EXT-X-, et la chaîne tvg n’y apparaît nulle part. Sa grammaire de liste d’attributs est séparée par des virgules avec une forme de nom fixe, que la convention tvg- séparée par des espaces ne suit pas, et la même section dit aux clients d’ignorer les noms qu’ils ne reconnaissent pas. C’est cette instruction qui permet à la convention de vivre à côté de la RFC : tout ce qui suit est un accord privé entre un programme et les playlists qu’on lui a montrées.
La plus ancienne apparition de tvg-chno que je puisse pointer est la pull request #92 contre l’add-on IPTV Simple de Kodi, ouverte le 2016-11-24 et fusionnée le 2016-12-17. Je ne peux pas prouver qu’elle est la première, seulement que rien de plus ancien n’est public. Elle ajoute une constante de marqueur, et la motivation déclarée de son auteur était de synchroniser les numéros de chaîne avec un backend. La preuve négative qui l’entoure est plus forte que le commit : un ticket du même suivi, daté du 2015-07-20, dit clairement que le format M3U n’a pas de balise pour un numéro de chaîne et propose d’empaqueter le numéro dans le nom de la chaîne à la place. L’auteur de la #92 a trouvé ce ticket cinq minutes après avoir ouvert sa pull request, et le mainteneur l’a plus tard fermé comme corrigé par son patch. À la mi-2015, la propre communauté de l’add-on ne connaissait aucun attribut de ce genre.
Le préfixe tvg- a presque quatre ans de plus que le numéro. tvg-name, tvg-logo et group-title figurent dans le commit initial du projet de l’add-on, le 2013-01-17, tvg-id arrive le même jour, et tvg-shift quatre jours plus tard. Ce que tvg abrège relève du folklore : tout le monde le lit comme « Guide TV » et les attributs frères qui portent l’adresse du guide soutiennent cette lecture, mais aucune source de première main n’énonce le développement, et les récits qui font remonter le jeu d’attributs à un écosystème de lecteurs Windows antérieur sont de seconde main. Le négatif est plus solide. XMLTV est ce vers quoi ces attributs pointent, pas d’où ils viennent.
L’adoption après Kodi enchaîne Tvheadend qui l’émet en mars 2018, Emby qui le documente courant 2018, Jellyfin en février 2019. Le détail le plus irritant de la récolte est que le message du commit Tvheadend dit ajouter un attribut tvg-chnum alors que le code qu’il introduit écrit tvg-chno=, et qu’au retour il lit tvh-chnum en premier, avec repli sur tvg-chno. Au moins cinq orthographes portent un numéro de chaîne dans de vrais fichiers : tvg-chno, channel-number, ch-number, tvh-chnum, et un channel-id que certains lecteurs traitent comme un numéro.
1 176 des 1 355 numéros sont partagés avec une autre ligne
Nous avons mesuré trois playlists d’exemple que nous détenons localement, 2 209 entrées au total. Deux des trois ne numérotent rien du tout : zéro de leurs 314 entrées porte l’attribut. Tout ce qui suit concerne le troisième fichier.
Les valeurs sont déséquilibrées. 179 des 263 valeurs distinctes sont utilisées exactement une fois, donc l’essentiel de la masse tient dans une courte tête : 107 entrées sur le numéro 1, puis 92, 80, 72 et 68. La plage va de 1 à 4 999, donc le plafond de 99 999 de l’analyseur ne s’est jamais déclenché. 1 176 des 1 355 lignes, 86.8 pour cent, partagent leur numéro avec au moins une autre ligne, et une clé unique posée sur le fichier en supprimerait 1 092. En ne comptant que les valeurs que notre analyseur accepte, le taux de partage est de 1 172 sur 1 299, soit 90.2 pour cent.
Pourquoi rien dans l’un de ces fichiers n’est une clé stable est réglé dans l’article sur le M3U étendu, et je ne vais pas le réargumenter ici. Ce que cet article ne fait pas, c’est tuer la réparation évidente : si les numéros se dupliquent à travers un fichier, on les limite au groupe où ils se trouvent et chaque groupe obtient une séquence propre.
Cela ne fonctionne pas.
33 des 85 groupes qui portent un numéro quelconque contiennent au moins un numéro dupliqué à l’intérieur du groupe. 69 de ces 85 contiennent une chaîne numérotée 1. Un groupe compte 7 entrées toutes numérotées 1, et dans le plus grand groupe du fichier, 79 entrées numérotées couvrent 74 numéros distincts. Le regroupement réduit le nombre de collisions sans le supprimer, et un correctif qui marche pour deux tiers de vos groupes échoue silencieusement sur le tiers restant.
Le taux d’absence compte autant que la duplication. 596 des 1 895 entrées, 31.5 pour cent, finissent sans numéro exploitable : 540 n’ont jamais porté l’attribut, et 56 portaient quelque chose que l’analyseur refuse. En croisant avec l’attribut d’identifiant, 398 lignes ne portent ni numéro ni id, la population que rien dans le fichier ne peut adresser. Une ligne de guide sans rien à quoi s’attacher se manifeste comme une grille fausse sur certaines chaînes et juste sur d’autres, jamais comme une plainte de numérotation.
Tous les décimaux du fichier sont argentins
25 entrées utilisent une numérotation major.minor, sur 24 valeurs distinctes allant de 22.1 à 36.2, et seul 32.1 se répète. Chacune des 25 porte le pays AR, un id se terminant par .ar et le groupe Argentina, qui compte 26 entrées et numérote les 26. L’Argentine diffuse en ISDB-Tb. ATSC et ISDB-Tb utilisent tous deux la forme major.minor, et la forme est tout ce qu’ils partagent ici. Notre propre commentaire de code appelle ces valeurs de style ATSC, ce qui est vrai de la forme et faux quant à l’origine.
Ces 25 lignes sont la raison pour laquelle le numéro est une valeur en virgule flottante plutôt qu’un entier. Le type ne coûtait rien. La migration, si. La colonne ne peut pas être remplie a posteriori, parce que le numéro vit dans la charge utile de la source et que nous ne conservons pas la playlist brute. Chaque installation existante aurait porté un null sur chaque ligne, et une liste triée par ce numéro aurait envoyé toute une bibliothèque dans la queue des non numérotés. La migration supprime l’empreinte de l’instantané et le marqueur de matérialisation, forçant la synchronisation suivante à reconstruire le catalogue. Chaque installation existante a payé une rematérialisation complète pour afficher un badge.
Cinquante-six valeurs ne sont pas des nombres du tout
56 des 1 355 valeurs, sur 53 orthographes distinctes, sont refusées d’emblée. Elles se répartissent en quatre formes :
- Un
#nu sur 4 entrées, un espace réservé que l’émetteur n’a jamais rempli. - 49 codes de système de diffusion : une série
D011àD081, une familleBS101àBS4K101, une longue sérieCS055àCS363, et un uniqueJCOM307. C’est un vrai schéma de numérotation national, et l’attribut n’a pas de place pour lui. CH 106etCH 108, le même numéro avec un préfixe et une espace.- Une valeur qui est un nom de plateforme écrit en japonais avec
527à la fin.
Un refus coûte à la ligne son numéro et jamais la ligne, donc les 56 gardent leur place et se trient dans la queue des non numérotés.
Plusieurs refus sont des règles sans témoin : un signe en tête place +5 et -5 hors des styles acceptés, et zéro est refusé parce qu’un numéro absent n’est pas la chaîne zéro. Aucune des deux formes n’apparaît une seule fois dans 1 355 valeurs. Les zéros en tête sont le cas indulgent : 007 s’analyse en 7.
Cette répartition appelle une réserve, parce que le script de mesure émule notre analyseur en Python et que float() est plus laxiste que le jeu de styles que nous passons. J’ai vérifié le corpus pour chaque forme où les deux divergent, un plus ou un moins en tête, les exposants, les underscores, le remplissage par des espaces et les zéros en tête, et j’en ai trouvé zéro de chaque. La répartition 1 299 contre 56 est celle de l’analyseur livré.
Aucun de ces lecteurs ne s’accorde avec un autre
Rien de ce qui suit ne vient d’une spécification, parce qu’il n’y en a pas. Chaque cellule est lue dans le code source de ce projet ou dans sa propre documentation.
| Consommateur | Le lit | Accepte aussi | Décimal | Deux lignes, un numéro | Attribut absent |
|---|---|---|---|---|---|
| Kodi IPTV Simple | Oui, depuis 2016-12 | ch-number, les valeurs sans guillemets, et toute orthographe préfixée, parce que la correspondance est une recherche de sous-chaîne | Découpé au point en un numéro majeur et un numéro de sous-canal | Aucune détection ; les deux survivent, le cœur trie par numéro et départage sur le nom | Le compteur courant continue depuis la chaîne précédente plus un |
| Jellyfin | Oui, depuis 2019-02 | Un tvg-id numérique ; un channel-id numérique, mais seulement quand tvg-id est présent et non numérique ; un nombre en tête du nom ; le nom de fichier de l’URL | Analysé en double, chaîne d’origine conservée | Rien dans l’analyseur | Descend la chaîne de replis ; -1 et 0 rejetés comme invalides |
| Emby | Oui, courant 2018 | Rien de documenté | Non documenté | Non documenté | Documenté comme une détection automatique à partir des balises disponibles |
| xTeVe | Non, stocké et ignoré | Rien ; la numérotation est interne | Seulement depuis sa propre interface | Impossible ; l’allocateur prend le premier numéro libre | Comme si présent : premier numéro libre à partir d’un début configurable, 1000 par défaut |
| Threadfin | Oui | tvg-chno uniquement | Stocké comme chaîne, analysé en float pour trier | Copié tel quel, donc les collisions persistent | Premier numéro libre à partir du numéro de départ du groupe |
| Channels DVR | channel-number, selon sa documentation | channel-id, dont sa documentation fait le champ obligatoire | Non documenté | Fusionné entre sources, la chaîne prioritaire l’emporte | Non documenté |
| Plex | Aucune ingestion M3U | Sans objet | Ce que le proxy placé devant envoie | Ce que le proxy envoie | Les numéros viennent d’une grille de tuner |
| VLC | Non | Lit tvg-logo, tvg-name, tvg-language, tvg-id, group-title et abandonne le reste | Sans objet | Sans objet | Aucun concept de numéro de chaîne, seulement la position dans la playlist |
Le lecteur de Kodi est le plus permissif du lot et le plus surprenant dans ses effets. Chaque entrée est initialisée depuis un compteur courant qui se remet au numéro qu’une chaîne revendique, donc un seul tvg-chno="500" aux deux tiers d’un fichier renumérote toutes les chaînes non numérotées suivantes à partir de 501. Un attribut sur une ligne déplace des centaines d’autres. Et après tout cet effort d’analyse, le réglage du cœur de Kodi pour utiliser les numéros de chaîne du backend est désactivé par défaut : le groupe toutes-chaînes se renumérote de 1 à n, les autres groupes en héritent, et le numéro de la playlist reste sur la chaîne sans être affiché.
Celui de Jellyfin est le plus strict : son expression régulière exige une valeur entre guillemets doubles, donc tvg-chno=101 sans guillemets lui est invisible alors que Kodi le lit sans broncher, et notre propre lecteur a la même restriction. Sa chaîne de replis se termine au nom de fichier de l’URL du flux, le point où un numéro de chaîne cesse d’être une donnée et devient une supposition sur un chemin.
xTeVe est le refus intéressant. Il analyse l’attribut en métadonnée générique et ne l’utilise jamais, allouant ses propres numéros depuis le premier emplacement libre à partir de 1000, et une demande d’honorer tvg-chno a été fermée comme non planifiée. C’est la seule conception ici où deux lignes revendiquant un même numéro ne peuvent pas entrer en collision, parce que l’avis de la playlist n’est jamais consulté, et le prix en est qu’une source soigneusement numérotée arrive renumérotée. Threadfin, son fork, a pris l’autre direction et copie la valeur telle quelle, donc les collisions passent intactes ; son dédoublonnage hache l’URL, le tvg-id et l’id de source, jamais le numéro.
Plex est la ligne à part, parce qu’il n’a aucune ingestion M3U de première main et que rien en son sein n’analyse l’attribut ; sa page sur les tuners pris en charge par la communauté demande un périphérique Windows BDA ou Linux DVB. La voie habituelle depuis une playlist est un proxy de type HDHomeRun, où le numéro arrive comme GuideNumber dans lineup.json, et le numéro en question dépend du proxy : xTeVe substitue le sien, Threadfin transmet la chaîne telle quelle.
Six lignes produisent quatre numéros exploitables
Voici un fragment construit pour toucher tous les chemins à la fois. Chaque hôte est un domaine d’exemple réservé et chaque nom est inventé.
#EXTINF:-1 tvg-id="one.example" tvg-chno="1",Example One
https://example.com/live/one.m3u8
#EXTINF:-1 tvg-id="two.example" tvg-chno="1",Example Two
https://example.com/live/two.m3u8
#EXTINF:-1 tvg-chno=101,Example Three
https://example.com/live/three.m3u8
#EXTINF:-1 tvg-chno="22.1",Example Four
https://example.com/live/four.m3u8
#EXTINF:-1 tvg-chno="BS4K101",Example Five
https://example.com/live/five.m3u8
#EXTINF:-1 tvg-chno="007",Example Six
https://example.com/live/six.m3u8
One et Two revendiquent toutes deux le numéro 1, ce qui est le cas normal de ce fichier plutôt que sa bordure. Notre comparateur garde les deux, ordonnées par une comparaison en ordre naturel de leurs noms puis une comparaison ordinale de leurs id, donc la paire est déterministe. Kodi garde les deux aussi. xTeVe leur donnerait 1000 et 1001 sans jamais lire l’attribut.
Three est sans guillemets, donc Kodi lit 101 alors que nous ne lisons rien du tout, comme Jellyfin d’ailleurs : la ligne atterrit dans la queue des non numérotés chez nous et sur la chaîne 101 chez Kodi.
Four est le cas du sous-canal. Nous gardons 22.1 comme décimal, Kodi le découpe en majeur 22 avec sous-canal 1, Jellyfin analyse un double et garde la chaîne d’origine pour l’affichage, et tout consommateur avec une colonne entière affiche 22.
Five est refusée par notre analyseur de numéros et garde sa ligne, donc elle rejoint Three dans la queue. Kodi s’en sort moins bien que nous : son lecteur passe la valeur à atoi, qui renvoie 0 pour une chaîne dont les chiffres ne sont pas en tête, et rien ne protège le setter, donc la ligne devient la chaîne 0 et le compteur courant est remis à 0 avec elle. La chaîne non numérotée suivante devient 1. Une valeur parasite y fait plus de dégâts qu’une valeur absente.
Six est le cas du zéro en tête : 007 se normalise en 7 chez nous, et Jellyfin analyse le même 7 tout en affichant toujours 007.
Triée par le numéro de la source, notre liste se lit Example One, Example Two, Example Six à 7, Example Four à 22.1, puis Example Five et Example Three dans l’ordre des noms, parce qu’un numéro absent se trie après tous les numéros présents plutôt que comme zéro.
Une collision se résout différemment selon ce que fait le spectateur. Il nous a fallu trois mécanismes plutôt qu’un. Pour le tri, la collision est absorbée par le départage sur le nom. Pour la saisie au clavier numérique, taper 1 parcourt les correspondances : chaque ligne portant ce numéro est collectée et les pressions répétées les font défiler, en rebouclant à la fin, parce qu’épingler pour toujours la première des 107 rendrait les 106 autres inaccessibles. Pour l’agrégation, où plusieurs sources contribuent à une seule chaîne logique, le numéro du membre qui donne son nom l’emporte, et à défaut le premier membre en ordre de priorité qui en a un. Ces mêmes membres forment la file vers laquelle un flux bloqué bascule, ce qui a son propre article.
Le chemin du clavier numérique a une lacune qui ne me plaît pas. Il n’accumule que les touches chiffres, avec une fenêtre de 1.2 seconde entre elles et une réinitialisation après cinq chiffres, et il analyse sans aucun style numérique activé, donc il n’y a aucun moyen de taper 22.1. Un sous-canal est accessible par la recherche, où la requête passe par l’analyseur qui accepte un point décimal, et inaccessible depuis la télécommande. La raison honnête pour laquelle ce n’est pas corrigé, c’est que 25 lignes de notre corpus en ont besoin.
Un numéro que vous attribuez n’est pas le numéro que le fichier a envoyé
Le numéro de la source n’est jamais réécrit. Un geste de réordonnancement écrit une clé fractionnaire séparée, et les deux cohabitent.
Le mécanisme est dicté par une contrainte qui n’a rien à voir avec l’ordonnancement. Notre plafond de lot de synchronisation est de 500 changements et un seul enregistrement rejeté fait échouer tout le lot, emportant chaque changement sans rapport qui voyageait avec lui, donc renuméroter un groupe à chaque dépôt n’a jamais été viable. À la place, chaque chaîne reçoit une clé implicite tirée de sa position dans l’ordre de repli, une chaîne placée utilise sa clé synchronisée, et un dépôt entre deux lignes prend le milieu, ce qui écrit exactement une ligne. Les clés implicites sont espacées d’une unité, donc chaque dépôt dans le même intervalle le divise par deux : face à un intervalle minimal de 1e-9, le trentième dépôt consécutif dans un même emplacement passe encore, et le trente-et-unième rééquilibre le groupe vers des clés uniformément espacées. Ce rééquilibrage est le seul chemin ici qui écrit des milliers de lignes, et il est journalisé avec sa taille au lieu de s’exécuter en silence. Trois commentaires dans notre propre code disent cinquante. Nous nous sommes trompés par intuition, la première version de cet article a répété l’erreur, et le chiffre n’est devenu trente que quand quelqu’un a fait le calcul.
Le badge affiche la position de la ligne plutôt que cette clé, parce que les clés implicites bougent chaque fois que la source ajoute ou retire des chaînes, et qu’un numéro qui se déplace tout seul après une synchronisation est pire que pas de numéro. Une version antérieure de cet ordonnancement vivait dans le modèle de vue, où un ordre enregistré s’appliquait aux groupes de catégories de la source et ne s’appliquait silencieusement pas à Toutes les chaînes, aux favoris ni aux groupes personnalisés.
Les applications Windows et Xbox analysent tout cela sur votre appareil, à partir de la playlist que vous leur indiquez, et vous pouvez créer un compte gratuit pour essayer l’une ou l’autre. Rien de vos numéros de chaîne ne nous parvient, parce que le fichier n’est jamais téléversé et que l’analyse se fait localement.
Si vous émettez des playlists, écrivez tvg-chno sur chaque ligne et gardez-le unique à l’intérieur du fichier, parce que les consommateurs qui l’honorent ne partagent aucune règle sur ce qu’il faut faire quand vous ne le faites pas, et que l’un d’eux renumérotera tout ce qui se trouve en aval de votre première chaîne numérotée. Si vous lisez des playlists, comptez les valeurs distinctes avant de bâtir quoi que ce soit sur l’attribut. Dans notre corpus, ce décompte est la différence entre 1 355 numéros et 263.
Ce que cet article a mesuré46 affirmations, chacune avec les preuves derrière elles
| Réclamation | Preuve | Compté |
|---|---|---|
| La RFC 8216 est une soumission indépendante de statut Informational. Elle définit EXTM3U en section 4.3.1.1, EXTINF en section 4.3.2.1 et la famille de balises EXT-X-, et elle ne définit aucun attribut tvg- ni aucun attribut de numéro de chaîne ; la chaîne tvg n’y apparaît pas. Sa grammaire de liste d’attributs en section 4.2 est séparée par des virgules avec une forme AttributeName fixe, que la convention tvg- séparée par des espaces ne suit pas. La section 4.2 exige aussi des clients qu’ils ignorent les balises et les AttributeName non reconnus, ce qui explique que la convention survive à côté d’elle.RFC 8216, Informational, sections 4.2, 4.3.1.1 and 4.3.2.1. Full text re-read 2026-08-23; no occurrence of the string tvg. | Spécification | Sans objet |
| La plus ancienne apparition publique vérifiable de tvg-chno est la PR #92 contre le PVR IPTV Simple Client de Kodi, ouverte le 2016-11-24 et fusionnée le 2016-12-17, commit 28e607bb7d192c54759bae8e2bb1a5d51297e002 par sangwoo shin, message add channel number tag, qui introduit la constante de marqueur tvg-chno= dans le chargeur de playlist.https://github.com/kodi-pvr/pvr.iptvsimple/pull/92 and commit 28e607bb7d192c54759bae8e2bb1a5d51297e002. Constant re-verified 2026-08-23 against branch Piers: TVG_INFO_CHNO_MARKER = tvg-chno= and CHANNEL_NUMBER_MARKER = ch-number= in src/iptvsimple/PlaylistLoader.h. | Spécification | Sans objet |
| En juillet 2015, le suivi de tickets du même add-on affirmait que le format M3U n’avait pas de balise pour un numéro de chaîne et proposait de l’encoder dans le nom de la chaîne à la place. L’auteur de la PR #92 n’a pas cité ce ticket comme motivation : le corps de sa PR donne la synchronisation avec un backend comme raison, et il a signalé le ticket cinq minutes après avoir ouvert la PR. Le mainteneur a fermé le ticket comme corrigé par la #92 le 2016-12-17.https://github.com/kodi-pvr/pvr.iptvsimple/issues/31, opened 2015-07-20, closed 2016-12-17 as fixed by #92. PR #92 body by excorp, 2016-11-24T04:19Z: Added channel number tag to m3u file. It is useful for syncing channel number with backend in kodi. His follow-up comment: I found something similar in issue #31. | Spécification | Sans objet |
| tvg-name, tvg-logo et group-title sont présents dans le commit initial du projet de l’add-on, 095e2fd du 2013-01-17 par Anton Fedchin. tvg-id arrive le même jour dans 46c58ef, et tvg-shift le 2013-01-21 dans 68a61c4. Le wiki de ce projet documente six attributs, tvg-id, tvg-name, tvg-logo, tvg-shift, group-name et radio, sans indiquer leur origine.afedchin/xbmc-addon-iptvsimple, commit 095e2fd (2013-01-17) at src/iptvsimple/PVRDemoData.cpp:31-33, commit 46c58ef (2013-01-17) at src/iptvsimple/PVRIptvData.cpp:35, commit 68a61c4 (2013-01-21), and https://github.com/afedchin/xbmc-addon-iptvsimple/wiki/IPTV-Simple-Home | Spécification | Sans objet |
| Aucune source de première main n’indique ce que tvg abrège. Guide TV est la lecture universelle et les attributs frères x-tvg-url et url-tvg portent l’adresse XMLTV, ce qui la soutient, mais le développement n’est pas cité.Absence of any expansion in the add-on wiki, the add-on README and the original repository | Spécification | Sans objet |
| Tvheadend écrit tvg-chno= dans sa propre sortie M3U, et l’émet sous la forme major.minor via channel_get_number_as_str, ce qui en fait un émetteur de première main de numéros de chaîne décimaux. Le commit qui l’a ajouté, 35c83be2 du 2018-03-20 par Jaroslav Kysela, a un message disant add tvg-chnum attribute alors que le jeton émis est tvg-chno.tvheadend commit 35c83be2fc8cdded (2018-03-20), message: m3u playlist: add tvg-chnum attribute with the channel number, fixes #5011. The emitter sits at src/webui/webui.c:601 on master today, which is not the line number in that commit. | Spécification | Sans objet |
| Le lecteur d’auto-réseau IPTV de Tvheadend lui-même prend tvh-chnum en premier et retombe sur tvg-chno, si bien que le projet lit une orthographe qu’il n’écrit pas.tvheadend/tvheadend, src/input/mpegts/iptv/iptv_auto.c:106-107 | Spécification | Sans objet |
| Au moins cinq orthographes portent un numéro de chaîne dans la nature : tvg-chno, channel-number, ch-number, tvh-chnum et un channel-id numérique.tvg-chno and ch-number at kodi-pvr/pvr.iptvsimple src/iptvsimple/PlaylistLoader.h:33-34; channel-number in the Channels DVR custom-channels documentation and in TVIP set-top firmware 4.1.12+ at https://wiki.tvip.tv/en/m3u, whose example is unquoted (channel-number=10); tvh-chnum at tvheadend/tvheadend src/input/mpegts/iptv/iptv_auto.c:106-107; numeric channel-id at jellyfin/jellyfin src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs:191-195 | Spécification | Sans objet |
| Sur trois playlists d’exemple détenues localement, totalisant 2 209 entrées, un seul fichier numérote quoi que ce soit : 1 355 de ses 1 895 entrées portent tvg-chno, soit 71.5 pour cent. Les deux autres fichiers portent l’attribut sur zéro de leurs 314 entrées. | n = 2209 | 23 août 2026 |
| Les 1 355 valeurs couvrent 263 nombres distincts, une moyenne de 5.15 entrées par valeur. 179 valeurs sont utilisées exactement une fois. Les cinq premières sont 1 sur 107 entrées, 2 sur 92, 3 sur 80, 4 sur 72 et 5 sur 68. La plage numérique va de 1 à 4 999. | n = 1355 | 23 août 2026 |
| 1 176 des 1 355 entrées partagent leur numéro avec au moins une autre ligne, 86.8 pour cent, et une clé unique en supprimerait 1 092. En ne comptant que les valeurs que l’analyseur accepte, 1 172 sur 1 299 partagent leur numéro, 90.2 pour cent. | n = 1355 | 23 août 2026 |
| 33 des 85 groupes qui portent un numéro contiennent au moins un numéro dupliqué à l’intérieur du groupe, 69 sur 85 contiennent une chaîne numérotée 1, et un groupe compte 7 entrées toutes numérotées 1. Dans le plus grand groupe, 79 entrées numérotées couvrent 74 numéros distincts. | n = 1895 | 23 août 2026 |
| 596 des 1 895 entrées finissent sans numéro exploitable, 31.5 pour cent : 540 n’ont jamais porté l’attribut et 56 portaient une valeur que l’analyseur refuse. | n = 1895 | 23 août 2026 |
| Dans le même fichier, 1 250 entrées portent à la fois tvg-chno et tvg-id, 105 portent un numéro et pas d’id, 142 un id et pas de numéro, et 398 ne portent ni l’un ni l’autre. | n = 1895 | 23 août 2026 |
| 25 entrées sur 24 valeurs distinctes utilisent une numérotation de sous-canal major.minor, allant de 22.1 à 36.2, et 32.1 est le seul décimal qui se répète, sur 2 entrées. Chacune des 25 porte tvg-country=AR, un tvg-id se terminant par .ar et group-title Argentina. Ce groupe compte 26 entrées au total et numérote les 26. L’Argentine diffuse en ISDB-Tb plutôt qu’en ATSC ; les deux systèmes partagent la forme major.minor et non la géographie. | n = 1895 | 23 août 2026 |
| Le numéro est typé en double et stocké dans une colonne SQLite REAL, et les deux endroits indiquent la numérotation de sous-canal comme raison. | n = 1 | 23 août 2026 |
| La colonne ne peut pas être remplie sur place, parce que le numéro vit dans la charge utile de la source et que la playlist brute n’est pas conservée, donc la migration supprime l’empreinte de l’instantané et le marqueur de matérialisation pour forcer la synchronisation suivante à reconstruire le catalogue plutôt qu’à court-circuiter. | n = 1 | 23 août 2026 |
| L’analyseur accepte un point décimal, des espaces en tête et en queue et la culture invariante uniquement. Un signe en tête, un exposant, un séparateur de milliers et une virgule décimale sont hors du jeu de styles. Les valeurs non finies, non positives et au-dessus de 99 999 sont refusées. Les zéros en tête sont acceptés et normalisés en valeur, donc 007 devient 7. Un refus ne renvoie rien et coûte à la ligne son numéro, jamais la ligne. | n = 1 | 23 août 2026 |
| 22.1 est analysé et 22,1 ne l’est pas, y compris sous une culture française, et le badge affiche 5 plutôt que 5.0. | n = 2 | 23 août 2026 |
| 56 valeurs sur 53 orthographes distinctes sont refusées : 4 sont un # littéral, 49 sont des codes de système de diffusion des familles BS, CS, D et JCOM comme D011, BS4K101, CS363 et JCOM307, 2 se lisent CH 106 et CH 108, et 1 est un nom de plateforme en japonais se terminant par 527. | n = 1355 | 23 août 2026 |
| Le script de mesure émule l’analyseur avec float() de Python, qui est plus laxiste que le jeu de styles C#. Le corpus a été vérifié pour chaque forme où les deux divergent, un plus ou un moins en tête, la notation exponentielle, les underscores, le remplissage par des espaces et les zéros en tête, avec zéro occurrence de chacune, si bien que la répartition 1 299 acceptées contre 56 refusées est celle de l’analyseur livré. | n = 1355 | 23 août 2026 |
| Aucun index, unique ou non, n’existe sur la colonne de numéro de chaîne, et trois fichiers source distincts indiquent sur place que rien ne doit utiliser la valeur comme clé. | n = 3 | 23 août 2026 |
| Un seul analyseur sert les deux formats de transport, l’attribut M3U et le champ num du protocole de portail, et le champ du portail est lu via un convertisseur souple parce que les panneaux l’émettent indifféremment sous forme de chaîne ou de nombre. | n = 2 | 23 août 2026 |
| Trier par le numéro de la source place les lignes non numérotées après les numérotées plutôt que de traiter l’absence comme zéro, et les numéros égaux retombent sur une comparaison de noms en ordre naturel puis sur une comparaison ordinale des id. | n = 1 | 23 août 2026 |
| Le badge n’affiche le numéro de la source que lorsque la liste est triée par ce numéro, et sinon la position de la ligne à partir de 1 ; une ligne non numérotée retombe aussi sur la position, parce qu’un badge vide dans une colonne numérotée se lit comme une ligne cassée. | n = 1 | 23 août 2026 |
| La saisie au clavier numérique collecte chaque ligne correspondant au numéro tapé et les parcourt à chaque pression répétée, en rebouclant à la fin, plutôt que d’épingler la première correspondance. | n = 1 | 23 août 2026 |
| Le tampon multi-chiffres n’accumule que les touches chiffres et analyse sans aucun style numérique activé, donc un numéro de sous-canal est inaccessible depuis le clavier numérique ; la même valeur tapée dans la recherche correspond bien, parce que la recherche fait passer la requête par l’analyseur de numéros, qui accepte un point décimal. | n = 1 | 23 août 2026 |
| Quand plusieurs sources contribuent à une seule chaîne logique, le numéro du membre qui donne son nom l’emporte, et s’il n’en a pas, le premier membre du seau qui en annonce un l’emporte ; l’ordre du seau est déjà l’ordre de priorité. | n = 1 | 23 août 2026 |
| Un ordre attribué par l’utilisateur est une clé fractionnaire séparée, jamais une réécriture du numéro de la source. Un dépôt écrit exactement une ligne de synchronisation parce que le plafond du lot de synchronisation est de 500 changements et qu’un seul enregistrement rejeté fait échouer tout le lot. Les clés implicites sont espacées d’une unité et chaque dépôt dans le même intervalle le divise par deux, donc avec un intervalle minimal de 1e-9 le trentième dépôt consécutif dans un même emplacement passe encore et le trente-et-unième rééquilibre ; le rééquilibrage est le seul chemin qui écrit plus d’une ligne. | n = 1 | 23 août 2026 |
| Avant que l’ordonnancement soit extrait dans une seule fonction pure, un ordre synchronisé s’appliquait aux groupes de catégories de la source et ne s’appliquait silencieusement pas à Toutes les chaînes, aux favoris ni aux groupes personnalisés. | n = 1 | 23 août 2026 |
| Le lecteur de marqueurs de Kodi cherche le marqueur n’importe où dans la ligne et accepte une valeur entre guillemets ou délimitée par des espaces, donc un tvg-chno=101 sans guillemets est lu et une orthographe préfixée comme x-tvg-chno= correspond aussi. La valeur est découpée au point en un numéro majeur et un numéro de sous-canal.kodi-pvr/pvr.iptvsimple branch Piers, head 78a023cb31, src/iptvsimple/PlaylistLoader.cpp:336, :375-390 and ReadMarkerValue at :622-664 | Spécification | Sans objet |
| Kodi initialise chaque nouvelle entrée depuis un compteur courant qui démarre au réglage Start channel number, remet le compteur au numéro qu’une chaîne revendique, et l’incrémente après chaque ajout, si bien qu’une seule chaîne numérotée renumérote toutes les chaînes non numérotées suivantes à partir de ce point.kodi-pvr/pvr.iptvsimple, src/iptvsimple/PlaylistLoader.cpp:174 and src/iptvsimple/Channels.cpp:37, :100, :121 | Spécification | Sans objet |
| Kodi convertit la valeur du marqueur avec atoi, qui renvoie 0 pour une chaîne dont les chiffres ne sont pas en tête, comme BS4K101. Rien ne protège le setter, donc la ligne reçoit le numéro de chaîne 0 et le compteur courant est remis à 0, ce qui renumérote la chaîne non numérotée suivante en 1. Une valeur parasite y est plus destructrice qu’une valeur absente.kodi-pvr/pvr.iptvsimple, std::atoi at src/iptvsimple/PlaylistLoader.cpp:388, unguarded SetChannelNumber(int) at src/iptvsimple/Channel.h:64, counter assignment at src/iptvsimple/Channels.cpp:100 and increment at :121 | Spécification | Sans objet |
| Le réglage pvrmanager.usebackendchannelnumbers du cœur de Kodi vaut false par défaut. Avec les numéros backend désactivés, le groupe toutes-chaînes renumérote ses membres 1..n et les autres groupes héritent de cette numérotation, parce que le chemin 1..n ne s’exécute que pour le propriétaire des chaînes ou quand startgroupchannelnumbersfromone est activé, et ce réglage vaut aussi false par défaut. Le numéro de chaîne client est conservé sur le membre et simplement pas affiché.xbmc/xbmc at 3f152e7c12, system/settings/settings.xml:1832 for usebackendchannelnumbers with <default>false</default> and :1841 for startgroupchannelnumbersfromone with <default>false</default>; xbmc/pvr/channels/PVRChannelGroup.cpp:915-919 for the 1..n condition and :927 for SetClientChannelNumber | Spécification | Sans objet |
| L’add-on livre un réglage qui ignore purement et simplement tvg-chno et numérote les chaînes uniquement par leur ordre dans le fichier.kodi-pvr/pvr.iptvsimple README: Only number by channel order in M3U, which ignores any tvg-chno tags | Spécification | Sans objet |
| L’implémentation de référence documente l’attribut en une ligne de son README, donc l’attribut est non documenté par aucun standard plutôt que non documenté tout court.kodi-pvr/pvr.iptvsimple README line 515: tvg-chno: The number to be used for this channel. | Spécification | Sans objet |
| L’expression régulière d’attribut de Jellyfin exige une valeur non vide entre guillemets doubles, donc un numéro sans guillemets lui est invisible. Son numéro de chaîne retombe successivement sur tvg-chno, puis un tvg-id numérique, puis, uniquement quand tvg-id est présent et non numérique, un channel-id numérique, puis un nombre en tête du nom affiché, et enfin le nom de fichier de l’URL. -1 et 0 sont rejetés comme invalides.jellyfin/jellyfin, src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs:36 for the GeneratedRegex, :170-255 for GetChannelNumber with the channel-id branch nested as an else-if inside the tvg-id branch at :185-195, :267-277 for IsValidChannelNumber with -1 and 0 rejected at :270-271. Re-verified against master 2026-08-23. | Spécification | Sans objet |
| Jellyfin a gagné la prise en charge de tvg-chno dans la PR #863, créée le 2019-02-10 et fusionnée le 2019-02-14.https://github.com/jellyfin/jellyfin/pull/863 | Spécification | Sans objet |
| Emby documente tvg-chno comme le numéro de chaîne et indique que les valeurs manquantes sont détectées automatiquement à partir des balises disponibles. Le comportement au-delà n’est pas documenté et l’analyseur livré est en source fermée. La date de prise en charge est 2018 ; la seule source trouvée est le corps de la PR Jellyfin, qui dit qu’Emby l’a annoncé en juillet 2018.EmbySupport/Emby.Docs/M3U-Tuners.md:41, last updated 2026-07-18, published at https://emby.media/support/articles/M3U-Tuners.html; date attribution from the body of https://github.com/jellyfin/jellyfin/pull/863 | Spécification | Sans objet |
| L’instantané public des sources d’Emby est figé à la 3.5.3 du 2018-09-20 et son analyseur M3U ne contient aucun tvg-chno. Ce fichier est l’ancêtre direct de l’analyseur de Jellyfin, ce qui explique que les deux portent les mêmes replis sur le préfixe du nom et le nom de fichier de l’URL.MediaBrowser/Emby at 1d7c2ab4bf, Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs:159-252, last touched 2017-08-20 | Spécification | Sans objet |
| xTeVe stocke chaque attribut de playlist comme métadonnée générique et n’a aucun champ de numéro de chaîne sur la structure dans laquelle il analyse une ligne de playlist. Il alloue ses propres numéros, prenant le premier numéro libre à partir d’une première chaîne configurable qui vaut 1000 par défaut dans le code plutôt que dans la documentation, si bien que deux lignes revendiquant un même numéro ne peuvent pas entrer en collision par construction. Une demande de fonctionnalité pour honorer tvg-chno a été fermée comme non planifiée.xteve-project/xTeVe at 0e999b85b9, src/internal/m3u-parser/xteve_m3uParser.go:54-80 for the generic attribute map with no chno handling, src/struct-system.go:202-216 for M3UChannelStructXEPG, src/system.go:129 for defaults[mapping.first.channel] = 1000, src/xepg.go:332-348 for the first-free scan; issue #269 closed as not planned | Spécification | Sans objet |
| Threadfin, un fork de xTeVe, lit bien tvg-chno, le stocke comme chaîne de caractères, l’analyse en float pour le tri et les comparaisons de dédoublonnage, et copie la valeur telle quelle dans l’id de chaîne qu’il expose, si bien que les collisions persistent. Le dédoublonnage est un md5 sur l’URL plus tvg-id plus l’id de source M3U, avec repli sur l’URL plus l’id de source quand tvg-id est vide ; une seconde passe départage sur le numéro de chaîne analysé le plus bas. La suppression des doublons par nom est présente mais désactivée dans le code source.Threadfin/Threadfin, branch main, head 6b9c0ccf16 dated 2025-09-29. String storage at src/struct-system.go:194 and :232; verbatim copy into xChannelID at src/xepg.go:643-647; float parse for sort at src/m3u.go:287-288; float parse for the dedupe tie-break at src/xepg.go:1923-1927; hash input at src/xepg.go:476-484, :508-516 and :1835-1843; disabled name-based removal at src/xepg.go:1853 | Spécification | Sans objet |
| Plex Live TV n’a aucune ingestion M3U de première main, donc rien à l’intérieur de Plex n’analyse jamais tvg-chno. Sa documentation sur les tuners pris en charge par la communauté demande un périphérique Windows BDA ou Linux DVB API, et nomme TVHProxy parmi les voies que les gens utilisent. La voie habituelle depuis une playlist est un proxy de type HDHomeRun, où le numéro arrive comme GuideNumber dans lineup.json, et le numéro en question dépend du proxy : xTeVe en substitue un des siens, Threadfin transmet la chaîne tvg-chno telle quelle.Plex Live TV and Community Supported Tuners documentation, which describes Windows BDA and Linux DVB API devices and names TVHProxy; the HDHomeRun lineup.json GuideNumber field; Threadfin/Threadfin src/xepg.go:643-647 into src/hdhr.go:160, which sets stream.GuideNumber from the value taken from tvg-chno | Spécification | Sans objet |
| Channels DVR documente channel-number pour les sources de chaînes personnalisées, à côté d’un channel-id obligatoire, de tvg-logo, tvg-name et de ses propres extensions tvc-. Sa documentation ne mentionne pas tvg-chno. Elle indique qu’un numéro en collision avec la grille d’une autre source est fusionné et que la chaîne prioritaire est utilisée.Channels DVR documentation for custom channel sources, verbatim: Be sure not to use a channel number that exists in another source's lineup. Channels will merge them together and use the prioritized channel. | Spécification | Sans objet |
| L’analyseur M3U de VLC lit tvg-logo, tvg-name, tvg-language, tvg-id et group-title, et abandonne silencieusement les clés tvg- inconnues, chno compris. VLC n’a aucun concept de numéro de chaîne, seulement la position dans la playlist.VideoLAN vlc, modules/demux/playlist/m3u.c, tvg attribute handling added 2020-03-11 | Spécification | Sans objet |
| Le fragment construit à la main dans cet article produit six lignes, dont quatre portent un numéro exploitable sous notre analyseur, et un tri numéroté de 1, 1, 7 et 22.1 suivi des deux lignes non numérotées dans l’ordre des noms. | n = 6 | 23 août 2026 |