<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>https://lugwiki.stcgrupo.es/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Admin</id>
	<title>Le Wiki de Lug - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="https://lugwiki.stcgrupo.es/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Admin"/>
	<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Sp%C3%A9cial:Contributions/Admin"/>
	<updated>2026-04-15T17:29:57Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3290</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3290"/>
		<updated>2026-04-13T11:09:22Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec Gmail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = grave&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Attention à supprimer les lignes qui pourraient faire doublon avec les lignes de configuration rajoutées.&lt;br /&gt;
 }}&lt;br /&gt;
Il faut vérifier que le nom de l&#039;expéditeur utiliser par le serveur est valide, tout d&#039;abord on vérifie le nom d&#039;origine utilisé :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 myorigin = /etc/mailname&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis ont édite le nom de serveur utilisé :&lt;br /&gt;
 # vi /etc/mailname&lt;br /&gt;
On peut simplement utiliser &amp;quot;localdomain&amp;quot; pour contourner les problèmes de domaine invalide :&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&amp;lt;font color = blue&amp;gt;.localdomain&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Le nom du serveur.localdomain doit apparaitre dans &amp;quot;/etc/postfix/main.cf&amp;quot; à la ligne &amp;quot;mydestination =&amp;quot; sinon postfix ne comprendra pas qu&#039;il s&#039;agit de la machine locale.&lt;br /&gt;
 }}&lt;br /&gt;
Enfin, on redémarre le service :&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3289</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3289"/>
		<updated>2026-04-13T11:00:23Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec Gmail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = grave&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Attention à supprimer les lignes qui pourraient faire doublon avec les lignes de configuration rajoutées.&lt;br /&gt;
 }}&lt;br /&gt;
Il faut vérifier que le nom de l&#039;expéditeur utiliser par le serveur est valide, tout d&#039;abord on vérifie le nom d&#039;origine utilisé :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 myorigin = /etc/mailname&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis ont édite le nom de serveur utilisé :&lt;br /&gt;
 # vi /etc/mailname&lt;br /&gt;
On peut simplement utiliser &amp;quot;localdomain&amp;quot; pour contourner les problèmes de domaine invalide :&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&amp;lt;font color = blue&amp;gt;.localdomain&amp;lt;/font&amp;gt;&lt;br /&gt;
Enfin, on redémarre le service :&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3288</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3288"/>
		<updated>2026-04-13T10:59:46Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec Gmail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = grave&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Attention à supprimer les lignes qui pourraient faire doublon avec les lignes de configuration rajoutées.&lt;br /&gt;
 }}&lt;br /&gt;
Il faut vérifier que le nom de l&#039;expéditeur utiliser par le serveur est valide, tout d&#039;abord on vérifie le nom d&#039;origine utiliser :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 myorigin = /etc/mailname&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/mailname&lt;br /&gt;
On peut simplement utiliser &amp;quot;localdomain&amp;quot; pour contourner les problèmes de domaine invalide :&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&amp;lt;font color = blue&amp;gt;.localdomain&amp;lt;/font&amp;gt;&lt;br /&gt;
Enfin, on redémarre le service :&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3287</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3287"/>
		<updated>2026-04-13T10:57:42Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec Gmail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = grave&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Attention à supprimer les lignes qui pourraient faire doublon avec les lignes de configuration rajoutées.&lt;br /&gt;
 }}&lt;br /&gt;
Il faut vérifier que le nom de l&#039;expéditeur utiliser par le serveur est valide :&lt;br /&gt;
 # vi /etc/mailname&lt;br /&gt;
On peut simplement utiliser &amp;quot;localdomain&amp;quot; pour contourner les problèmes de domaine invalide :&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&amp;lt;font color = blue&amp;gt;.localdomain&amp;lt;/font&amp;gt;&lt;br /&gt;
Enfin, on redémarre le service :&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3286</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3286"/>
		<updated>2026-04-13T10:37:21Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = grave&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut vérifier que le nom de l&#039;expéditeur utiliser par le serveur est valide :&lt;br /&gt;
 # vi /etc/mailname&lt;br /&gt;
On peut simplement utiliser &amp;quot;localdomain&amp;quot; pour contourner les problèmes de domaine invalide :&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&amp;lt;font color = blue&amp;gt;.localdomain&amp;lt;/font&amp;gt;&lt;br /&gt;
Enfin, on redémarre le service :&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3285</id>
		<title>AutoUpdate</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3285"/>
		<updated>2026-04-13T10:35:23Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Fréquence des mises à jour */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Attention avec les mises à jour automatique sur un serveur en production, il y a un risque de mettre en panne un service qui nécessite une intervention manuelle. Se limiter aux MAJ automatiques de sécurité quand la distribution le propose.&lt;br /&gt;
 }}&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Timzone_LXC#Alpine Linux|&#039;&#039;&#039;ce lien&#039;&#039;&#039;]] pour mettre a l&#039;heure le container LXC.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
==Crontab==&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apkcrontupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
==APK-Autoupdate==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet de mettre à jour et relancer les service actualisés ou effectuer une commande spécifiée.&lt;br /&gt;
 }}&lt;br /&gt;
===Installation===&lt;br /&gt;
Si absent il faut ajouter le dépôt &amp;quot;testing&amp;quot; aux sources :&lt;br /&gt;
 # echo &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;dl-cdn.alpinelinux.org/alpine/edge/testing&#039; &amp;gt;&amp;gt; /etc/apk/repositories&lt;br /&gt;
On installe le paquêt :&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add apk-autoupdate&lt;br /&gt;
&lt;br /&gt;
===Configuration===&lt;br /&gt;
 # vi /etc/apk/autoupdate.conf&lt;br /&gt;
Outre les listes blanche et noire habituelles il est possible de spécifier une commande autre que &amp;quot;restart&amp;quot; (si plusieurs séparées par un espace blanc).&lt;br /&gt;
&lt;br /&gt;
*Par exemple pour recharger Nginx plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/nginx:nginx:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Exemple pour recharger lighttpd plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/lighttpd:lighttpd:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test===&lt;br /&gt;
 # apk-autoupdate -sv&lt;br /&gt;
===Crontab===&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apk-autoupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
= Debian / Ubuntu =&lt;br /&gt;
On utilise l&#039;outil fournit &amp;quot;&#039;&#039;&#039;unattended-upgrades&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install unattended-upgrades&lt;br /&gt;
 # systemctl enable --now unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
=== Sélection des MAJ ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
On active les mises à jour souhaitées en supprimant le &amp;quot;&#039;&#039;&#039;//&#039;&#039;&#039;&amp;quot; devant la ligne idoine, dans l&#039;exemple ci-dessous on ne permet que les mises à jour de sécurité :&lt;br /&gt;
 ...&lt;br /&gt;
 // pocket these get automatically pulled in.&lt;br /&gt;
 Unattended-Upgrade::Allowed-Origins {&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}-security&amp;quot;;&lt;br /&gt;
         // Extended Security Maintenance; doesn&#039;t necessarily exist for&lt;br /&gt;
         // every release and this system may not have it installed, but if&lt;br /&gt;
         // available, the policy for updates is such that unattended-upgrades&lt;br /&gt;
         // should also install from here by default.&lt;br /&gt;
         &amp;quot;${distro_id}ESMApps:${distro_codename}-apps-security&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}ESM:${distro_codename}-infra-security&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt;      &amp;quot;${distro_id}:${distro_codename}-updates&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-proposed&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-backports&amp;quot;;&lt;br /&gt;
 ...&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Supprimer les &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt; pour activer la mise à jour des paquets standards.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
=== Activer l&#039;envoie de rapports par mail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Ubuntu_.2F_Debian|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Mail &amp;quot;root&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Set this value to one of:&lt;br /&gt;
 //    &amp;quot;&amp;lt;font color = green&amp;gt;always&amp;lt;/font&amp;gt;&amp;quot;, &amp;quot;&amp;lt;font color = green&amp;gt;only-on-error&amp;lt;/font&amp;gt;&amp;quot; or &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 // If this is not set, then any legacy MailOnlyOnError (boolean) value&lt;br /&gt;
 // is used to chose between &amp;quot;only-on-error&amp;quot; and &amp;quot;on-change&amp;quot;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::MailReport &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nettoyer automatiquement les dépendences inutiles ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 // Remove unused automatically installed kernel-related packages&lt;br /&gt;
 // (kernel images, kernel headers and kernel version locked tools).&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Kernel-Packages &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of newly unused dependencies after the upgrade&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-New-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of unused packages after the upgrade&lt;br /&gt;
 // (equivalent to apt-get autoremove)&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
=== Fréquence des mises à jour ===&lt;br /&gt;
 # dpkg-reconfigure -plow unattended-upgrades&lt;br /&gt;
-&amp;gt; &#039;&#039;&#039;&amp;lt;Yes&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/20auto-upgrades&lt;br /&gt;
&lt;br /&gt;
 APT::Periodic::Update-Package-Lists &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::Unattended-Upgrade &amp;quot;1&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;APT::Periodic::Verbose &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::AutocleanInterval &amp;quot;7&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Les lignes ajoutées permettent d&#039;ajuster le niveau des rapports et de nettoyer les paquets tous les 7 jours.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
=== On relance le service ===&lt;br /&gt;
Pour s&#039;assurer que toutes les modifications sont prises en comptes :&lt;br /&gt;
 # systemctl restart unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # unattended-upgrades --dry-run --debug&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation==&lt;br /&gt;
On installe le programme de mise à jour automatique :&lt;br /&gt;
 # dnf install dnf-automatic&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On configure le programme :&lt;br /&gt;
 # vi /etc/dnf/automatic.conf&lt;br /&gt;
On active l&#039;installation des mises à jour :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # install.timer override this setting.&amp;lt;/font&amp;gt;&lt;br /&gt;
 apply_updates = &amp;lt;font color =blue&amp;gt;yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
On ne permet que les mises à jour de sécurité :&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # security                           = only the security upgrades&amp;lt;/font&amp;gt;&lt;br /&gt;
 upgrade_type = &amp;lt;font color =blue&amp;gt;security&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;random_sleep = 0&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active l&#039;envoie de rapports par courriel :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alma_Linux_/_Rocky_Linux|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# If emit_via is None or left blank, no messages will be sent.&amp;lt;/font&amp;gt;&lt;br /&gt;
 emit_via = &amp;lt;font color =blue&amp;gt;email&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;[email]&lt;br /&gt;
 # The address to send email messages from.&amp;lt;/font&amp;gt;&lt;br /&gt;
 email_from = &amp;lt;font color =blue&amp;gt;root&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# List of addresses to send messages to.&lt;br /&gt;
 email_to = root&lt;br /&gt;
 &lt;br /&gt;
 # Name of the host to connect to to send email messages.&lt;br /&gt;
 email_host = localhost&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active le service :&lt;br /&gt;
 # systemctl enable --now dnf-automatic.timer&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3284</id>
		<title>AutoUpdate</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3284"/>
		<updated>2026-04-13T10:34:51Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Fréquence des mises à jour */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Attention avec les mises à jour automatique sur un serveur en production, il y a un risque de mettre en panne un service qui nécessite une intervention manuelle. Se limiter aux MAJ automatiques de sécurité quand la distribution le propose.&lt;br /&gt;
 }}&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Timzone_LXC#Alpine Linux|&#039;&#039;&#039;ce lien&#039;&#039;&#039;]] pour mettre a l&#039;heure le container LXC.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
==Crontab==&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apkcrontupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
==APK-Autoupdate==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet de mettre à jour et relancer les service actualisés ou effectuer une commande spécifiée.&lt;br /&gt;
 }}&lt;br /&gt;
===Installation===&lt;br /&gt;
Si absent il faut ajouter le dépôt &amp;quot;testing&amp;quot; aux sources :&lt;br /&gt;
 # echo &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;dl-cdn.alpinelinux.org/alpine/edge/testing&#039; &amp;gt;&amp;gt; /etc/apk/repositories&lt;br /&gt;
On installe le paquêt :&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add apk-autoupdate&lt;br /&gt;
&lt;br /&gt;
===Configuration===&lt;br /&gt;
 # vi /etc/apk/autoupdate.conf&lt;br /&gt;
Outre les listes blanche et noire habituelles il est possible de spécifier une commande autre que &amp;quot;restart&amp;quot; (si plusieurs séparées par un espace blanc).&lt;br /&gt;
&lt;br /&gt;
*Par exemple pour recharger Nginx plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/nginx:nginx:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Exemple pour recharger lighttpd plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/lighttpd:lighttpd:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test===&lt;br /&gt;
 # apk-autoupdate -sv&lt;br /&gt;
===Crontab===&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apk-autoupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
= Debian / Ubuntu =&lt;br /&gt;
On utilise l&#039;outil fournit &amp;quot;&#039;&#039;&#039;unattended-upgrades&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install unattended-upgrades&lt;br /&gt;
 # systemctl enable --now unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
=== Sélection des MAJ ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
On active les mises à jour souhaitées en supprimant le &amp;quot;&#039;&#039;&#039;//&#039;&#039;&#039;&amp;quot; devant la ligne idoine, dans l&#039;exemple ci-dessous on ne permet que les mises à jour de sécurité :&lt;br /&gt;
 ...&lt;br /&gt;
 // pocket these get automatically pulled in.&lt;br /&gt;
 Unattended-Upgrade::Allowed-Origins {&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}-security&amp;quot;;&lt;br /&gt;
         // Extended Security Maintenance; doesn&#039;t necessarily exist for&lt;br /&gt;
         // every release and this system may not have it installed, but if&lt;br /&gt;
         // available, the policy for updates is such that unattended-upgrades&lt;br /&gt;
         // should also install from here by default.&lt;br /&gt;
         &amp;quot;${distro_id}ESMApps:${distro_codename}-apps-security&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}ESM:${distro_codename}-infra-security&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt;      &amp;quot;${distro_id}:${distro_codename}-updates&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-proposed&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-backports&amp;quot;;&lt;br /&gt;
 ...&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Supprimer les &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt; pour activer la mise à jour des paquets standards.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
=== Activer l&#039;envoie de rapports par mail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Ubuntu_.2F_Debian|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Mail &amp;quot;root&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Set this value to one of:&lt;br /&gt;
 //    &amp;quot;&amp;lt;font color = green&amp;gt;always&amp;lt;/font&amp;gt;&amp;quot;, &amp;quot;&amp;lt;font color = green&amp;gt;only-on-error&amp;lt;/font&amp;gt;&amp;quot; or &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 // If this is not set, then any legacy MailOnlyOnError (boolean) value&lt;br /&gt;
 // is used to chose between &amp;quot;only-on-error&amp;quot; and &amp;quot;on-change&amp;quot;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::MailReport &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nettoyer automatiquement les dépendences inutiles ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 // Remove unused automatically installed kernel-related packages&lt;br /&gt;
 // (kernel images, kernel headers and kernel version locked tools).&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Kernel-Packages &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of newly unused dependencies after the upgrade&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-New-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of unused packages after the upgrade&lt;br /&gt;
 // (equivalent to apt-get autoremove)&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
=== Fréquence des mises à jour ===&lt;br /&gt;
 # dpkg-reconfigure -plow unattended-upgrades&lt;br /&gt;
-&amp;gt; &#039;&#039;&#039;&amp;lt;Yes&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/20auto-upgrades&lt;br /&gt;
&lt;br /&gt;
 APT::Periodic::Update-Package-Lists &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::Unattended-Upgrade &amp;quot;1&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;APT::Periodic::Verbose &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::AutocleanInterval &amp;quot;7&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
Les lignes ajoutées permettent d&#039;ajuster le niveau des rapports et de nettoyer les paquets tous les 7 jours.&lt;br /&gt;
&lt;br /&gt;
=== On relance le service ===&lt;br /&gt;
Pour s&#039;assurer que toutes les modifications sont prises en comptes :&lt;br /&gt;
 # systemctl restart unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # unattended-upgrades --dry-run --debug&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation==&lt;br /&gt;
On installe le programme de mise à jour automatique :&lt;br /&gt;
 # dnf install dnf-automatic&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On configure le programme :&lt;br /&gt;
 # vi /etc/dnf/automatic.conf&lt;br /&gt;
On active l&#039;installation des mises à jour :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # install.timer override this setting.&amp;lt;/font&amp;gt;&lt;br /&gt;
 apply_updates = &amp;lt;font color =blue&amp;gt;yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
On ne permet que les mises à jour de sécurité :&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # security                           = only the security upgrades&amp;lt;/font&amp;gt;&lt;br /&gt;
 upgrade_type = &amp;lt;font color =blue&amp;gt;security&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;random_sleep = 0&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active l&#039;envoie de rapports par courriel :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alma_Linux_/_Rocky_Linux|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# If emit_via is None or left blank, no messages will be sent.&amp;lt;/font&amp;gt;&lt;br /&gt;
 emit_via = &amp;lt;font color =blue&amp;gt;email&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;[email]&lt;br /&gt;
 # The address to send email messages from.&amp;lt;/font&amp;gt;&lt;br /&gt;
 email_from = &amp;lt;font color =blue&amp;gt;root&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# List of addresses to send messages to.&lt;br /&gt;
 email_to = root&lt;br /&gt;
 &lt;br /&gt;
 # Name of the host to connect to to send email messages.&lt;br /&gt;
 email_host = localhost&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active le service :&lt;br /&gt;
 # systemctl enable --now dnf-automatic.timer&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3283</id>
		<title>AutoUpdate</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3283"/>
		<updated>2026-04-13T10:26:53Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Fréquence des mises à jour */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Attention avec les mises à jour automatique sur un serveur en production, il y a un risque de mettre en panne un service qui nécessite une intervention manuelle. Se limiter aux MAJ automatiques de sécurité quand la distribution le propose.&lt;br /&gt;
 }}&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Timzone_LXC#Alpine Linux|&#039;&#039;&#039;ce lien&#039;&#039;&#039;]] pour mettre a l&#039;heure le container LXC.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
==Crontab==&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apkcrontupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
==APK-Autoupdate==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet de mettre à jour et relancer les service actualisés ou effectuer une commande spécifiée.&lt;br /&gt;
 }}&lt;br /&gt;
===Installation===&lt;br /&gt;
Si absent il faut ajouter le dépôt &amp;quot;testing&amp;quot; aux sources :&lt;br /&gt;
 # echo &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;dl-cdn.alpinelinux.org/alpine/edge/testing&#039; &amp;gt;&amp;gt; /etc/apk/repositories&lt;br /&gt;
On installe le paquêt :&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add apk-autoupdate&lt;br /&gt;
&lt;br /&gt;
===Configuration===&lt;br /&gt;
 # vi /etc/apk/autoupdate.conf&lt;br /&gt;
Outre les listes blanche et noire habituelles il est possible de spécifier une commande autre que &amp;quot;restart&amp;quot; (si plusieurs séparées par un espace blanc).&lt;br /&gt;
&lt;br /&gt;
*Par exemple pour recharger Nginx plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/nginx:nginx:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Exemple pour recharger lighttpd plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/lighttpd:lighttpd:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test===&lt;br /&gt;
 # apk-autoupdate -sv&lt;br /&gt;
===Crontab===&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apk-autoupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
= Debian / Ubuntu =&lt;br /&gt;
On utilise l&#039;outil fournit &amp;quot;&#039;&#039;&#039;unattended-upgrades&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install unattended-upgrades&lt;br /&gt;
 # systemctl enable --now unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
=== Sélection des MAJ ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
On active les mises à jour souhaitées en supprimant le &amp;quot;&#039;&#039;&#039;//&#039;&#039;&#039;&amp;quot; devant la ligne idoine, dans l&#039;exemple ci-dessous on ne permet que les mises à jour de sécurité :&lt;br /&gt;
 ...&lt;br /&gt;
 // pocket these get automatically pulled in.&lt;br /&gt;
 Unattended-Upgrade::Allowed-Origins {&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}-security&amp;quot;;&lt;br /&gt;
         // Extended Security Maintenance; doesn&#039;t necessarily exist for&lt;br /&gt;
         // every release and this system may not have it installed, but if&lt;br /&gt;
         // available, the policy for updates is such that unattended-upgrades&lt;br /&gt;
         // should also install from here by default.&lt;br /&gt;
         &amp;quot;${distro_id}ESMApps:${distro_codename}-apps-security&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}ESM:${distro_codename}-infra-security&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt;      &amp;quot;${distro_id}:${distro_codename}-updates&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-proposed&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-backports&amp;quot;;&lt;br /&gt;
 ...&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Supprimer les &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt; pour activer la mise à jour des paquets standards.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
=== Activer l&#039;envoie de rapports par mail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Ubuntu_.2F_Debian|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Mail &amp;quot;root&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Set this value to one of:&lt;br /&gt;
 //    &amp;quot;&amp;lt;font color = green&amp;gt;always&amp;lt;/font&amp;gt;&amp;quot;, &amp;quot;&amp;lt;font color = green&amp;gt;only-on-error&amp;lt;/font&amp;gt;&amp;quot; or &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 // If this is not set, then any legacy MailOnlyOnError (boolean) value&lt;br /&gt;
 // is used to chose between &amp;quot;only-on-error&amp;quot; and &amp;quot;on-change&amp;quot;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::MailReport &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nettoyer automatiquement les dépendences inutiles ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 // Remove unused automatically installed kernel-related packages&lt;br /&gt;
 // (kernel images, kernel headers and kernel version locked tools).&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Kernel-Packages &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of newly unused dependencies after the upgrade&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-New-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of unused packages after the upgrade&lt;br /&gt;
 // (equivalent to apt-get autoremove)&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
=== Fréquence des mises à jour ===&lt;br /&gt;
 # dpkg-reconfigure -plow unattended-upgrades&lt;br /&gt;
-&amp;gt; &#039;&#039;&#039;&amp;lt;Yes&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/20auto-upgrades&lt;br /&gt;
&lt;br /&gt;
 APT::Periodic::Update-Package-Lists &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::Unattended-Upgrade &amp;quot;1&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;APT::Periodic::Verbose &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::AutocleanInterval &amp;quot;7&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
Les lignes ajoutées permettent d&#039;avoir des rapports plus complets et de nettoyer les paquets tous les 7 jours.&lt;br /&gt;
&lt;br /&gt;
=== On relance le service ===&lt;br /&gt;
Pour s&#039;assurer que toutes les modifications sont prises en comptes :&lt;br /&gt;
 # systemctl restart unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # unattended-upgrades --dry-run --debug&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation==&lt;br /&gt;
On installe le programme de mise à jour automatique :&lt;br /&gt;
 # dnf install dnf-automatic&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On configure le programme :&lt;br /&gt;
 # vi /etc/dnf/automatic.conf&lt;br /&gt;
On active l&#039;installation des mises à jour :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # install.timer override this setting.&amp;lt;/font&amp;gt;&lt;br /&gt;
 apply_updates = &amp;lt;font color =blue&amp;gt;yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
On ne permet que les mises à jour de sécurité :&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # security                           = only the security upgrades&amp;lt;/font&amp;gt;&lt;br /&gt;
 upgrade_type = &amp;lt;font color =blue&amp;gt;security&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;random_sleep = 0&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active l&#039;envoie de rapports par courriel :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alma_Linux_/_Rocky_Linux|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# If emit_via is None or left blank, no messages will be sent.&amp;lt;/font&amp;gt;&lt;br /&gt;
 emit_via = &amp;lt;font color =blue&amp;gt;email&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;[email]&lt;br /&gt;
 # The address to send email messages from.&amp;lt;/font&amp;gt;&lt;br /&gt;
 email_from = &amp;lt;font color =blue&amp;gt;root&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# List of addresses to send messages to.&lt;br /&gt;
 email_to = root&lt;br /&gt;
 &lt;br /&gt;
 # Name of the host to connect to to send email messages.&lt;br /&gt;
 email_host = localhost&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active le service :&lt;br /&gt;
 # systemctl enable --now dnf-automatic.timer&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3282</id>
		<title>AutoUpdate</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=AutoUpdate&amp;diff=3282"/>
		<updated>2026-04-13T10:21:17Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Sélection des MAJ */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Attention avec les mises à jour automatique sur un serveur en production, il y a un risque de mettre en panne un service qui nécessite une intervention manuelle. Se limiter aux MAJ automatiques de sécurité quand la distribution le propose.&lt;br /&gt;
 }}&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Timzone_LXC#Alpine Linux|&#039;&#039;&#039;ce lien&#039;&#039;&#039;]] pour mettre a l&#039;heure le container LXC.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
==Crontab==&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apkcrontupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apkcrontupdate.log &amp;amp;&amp;amp; /sbin/apk -Uv upgrade &amp;gt;&amp;gt; /var/log/apkcrontupdate.log&lt;br /&gt;
&lt;br /&gt;
==APK-Autoupdate==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet de mettre à jour et relancer les service actualisés ou effectuer une commande spécifiée.&lt;br /&gt;
 }}&lt;br /&gt;
===Installation===&lt;br /&gt;
Si absent il faut ajouter le dépôt &amp;quot;testing&amp;quot; aux sources :&lt;br /&gt;
 # echo &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;dl-cdn.alpinelinux.org/alpine/edge/testing&#039; &amp;gt;&amp;gt; /etc/apk/repositories&lt;br /&gt;
On installe le paquêt :&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add apk-autoupdate&lt;br /&gt;
&lt;br /&gt;
===Configuration===&lt;br /&gt;
 # vi /etc/apk/autoupdate.conf&lt;br /&gt;
Outre les listes blanche et noire habituelles il est possible de spécifier une commande autre que &amp;quot;restart&amp;quot; (si plusieurs séparées par un espace blanc).&lt;br /&gt;
&lt;br /&gt;
*Par exemple pour recharger Nginx plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/nginx:nginx:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Exemple pour recharger lighttpd plutôt que le redémarrer :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 # Note: Case patterns (shell &amp;quot;case&amp;quot;) may be used in program path.&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;programs_services=&amp;quot;/usr/sbin/lighttpd:lighttpd:reload&amp;quot;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test===&lt;br /&gt;
 # apk-autoupdate -sv&lt;br /&gt;
===Crontab===&lt;br /&gt;
 # crontab -e&lt;br /&gt;
On ajoute la ligne suivante pour que chaque jour à 4h30 la mise à jour s&#039;effectue avec les logs dans le fichier /var/log/&#039;&#039;&#039;apk-autoupdate.log&#039;&#039;&#039;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 30      4       *       *       *       /bin/date &amp;gt; /var/log/apk-autoupdate.log &amp;amp;&amp;amp; /usr/sbin/apk-autoupdate -v &amp;gt;&amp;gt; /var/log/apk-autoupdate.log&lt;br /&gt;
&lt;br /&gt;
= Debian / Ubuntu =&lt;br /&gt;
On utilise l&#039;outil fournit &amp;quot;&#039;&#039;&#039;unattended-upgrades&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install unattended-upgrades&lt;br /&gt;
 # systemctl enable --now unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
=== Sélection des MAJ ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
On active les mises à jour souhaitées en supprimant le &amp;quot;&#039;&#039;&#039;//&#039;&#039;&#039;&amp;quot; devant la ligne idoine, dans l&#039;exemple ci-dessous on ne permet que les mises à jour de sécurité :&lt;br /&gt;
 ...&lt;br /&gt;
 // pocket these get automatically pulled in.&lt;br /&gt;
 Unattended-Upgrade::Allowed-Origins {&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}:${distro_codename}-security&amp;quot;;&lt;br /&gt;
         // Extended Security Maintenance; doesn&#039;t necessarily exist for&lt;br /&gt;
         // every release and this system may not have it installed, but if&lt;br /&gt;
         // available, the policy for updates is such that unattended-upgrades&lt;br /&gt;
         // should also install from here by default.&lt;br /&gt;
         &amp;quot;${distro_id}ESMApps:${distro_codename}-apps-security&amp;quot;;&lt;br /&gt;
         &amp;quot;${distro_id}ESM:${distro_codename}-infra-security&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt;      &amp;quot;${distro_id}:${distro_codename}-updates&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-proposed&amp;quot;;&lt;br /&gt;
 //      &amp;quot;${distro_id}:${distro_codename}-backports&amp;quot;;&lt;br /&gt;
 ...&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Supprimer les &amp;lt;font color = red&amp;gt;//&amp;lt;/font&amp;gt; pour activer la mise à jour des paquets standards.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
=== Activer l&#039;envoie de rapports par mail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Ubuntu_.2F_Debian|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Mail &amp;quot;root&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Set this value to one of:&lt;br /&gt;
 //    &amp;quot;&amp;lt;font color = green&amp;gt;always&amp;lt;/font&amp;gt;&amp;quot;, &amp;quot;&amp;lt;font color = green&amp;gt;only-on-error&amp;lt;/font&amp;gt;&amp;quot; or &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 // If this is not set, then any legacy MailOnlyOnError (boolean) value&lt;br /&gt;
 // is used to chose between &amp;quot;only-on-error&amp;quot; and &amp;quot;on-change&amp;quot;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::MailReport &amp;quot;&amp;lt;font color = green&amp;gt;on-change&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nettoyer automatiquement les dépendences inutiles ===&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/50unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 // Remove unused automatically installed kernel-related packages&lt;br /&gt;
 // (kernel images, kernel headers and kernel version locked tools).&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Kernel-Packages &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of newly unused dependencies after the upgrade&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-New-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Do automatic removal of unused packages after the upgrade&lt;br /&gt;
 // (equivalent to apt-get autoremove)&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;Unattended-Upgrade::Remove-Unused-Dependencies &amp;quot;&amp;lt;font color = green&amp;gt;true&amp;lt;/font&amp;gt;&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
=== Fréquence des mises à jour ===&lt;br /&gt;
 # dpkg-reconfigure -plow unattended-upgrades&lt;br /&gt;
-&amp;gt; &#039;&#039;&#039;&amp;lt;Yes&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
 # vi /etc/apt/apt.conf.d/20auto-upgrades&lt;br /&gt;
&lt;br /&gt;
 APT::Periodic::Update-Package-Lists &amp;quot;1&amp;quot;;&lt;br /&gt;
 APT::Periodic::Unattended-Upgrade &amp;quot;1&amp;quot;;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;APT::Periodic::Verbose &amp;quot;2&amp;quot;;&lt;br /&gt;
 APT::Periodic::AutocleanInterval &amp;quot;7&amp;quot;;&amp;lt;/font&amp;gt;&lt;br /&gt;
Les lignes ajoutées permettent d&#039;avoir des rapports plus complets et de nettoyer les paquets tous les 7 jours.&lt;br /&gt;
=== On relance le service ===&lt;br /&gt;
Pour s&#039;assurer que toutes les modifications sont prises en comptes :&lt;br /&gt;
 # systemctl restart unattended-upgrades&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # unattended-upgrades --dry-run --debug&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation==&lt;br /&gt;
On installe le programme de mise à jour automatique :&lt;br /&gt;
 # dnf install dnf-automatic&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On configure le programme :&lt;br /&gt;
 # vi /etc/dnf/automatic.conf&lt;br /&gt;
On active l&#039;installation des mises à jour :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # install.timer override this setting.&amp;lt;/font&amp;gt;&lt;br /&gt;
 apply_updates = &amp;lt;font color =blue&amp;gt;yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
On ne permet que les mises à jour de sécurité :&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&lt;br /&gt;
 # security                           = only the security upgrades&amp;lt;/font&amp;gt;&lt;br /&gt;
 upgrade_type = &amp;lt;font color =blue&amp;gt;security&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;random_sleep = 0&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active l&#039;envoie de rapports par courriel :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alma_Linux_/_Rocky_Linux|installation de Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# If emit_via is None or left blank, no messages will be sent.&amp;lt;/font&amp;gt;&lt;br /&gt;
 emit_via = &amp;lt;font color =blue&amp;gt;email&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;[email]&lt;br /&gt;
 # The address to send email messages from.&amp;lt;/font&amp;gt;&lt;br /&gt;
 email_from = &amp;lt;font color =blue&amp;gt;root&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;# List of addresses to send messages to.&lt;br /&gt;
 email_to = root&lt;br /&gt;
 &lt;br /&gt;
 # Name of the host to connect to to send email messages.&lt;br /&gt;
 email_host = localhost&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On active le service :&lt;br /&gt;
 # systemctl enable --now dnf-automatic.timer&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3281</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3281"/>
		<updated>2026-04-13T10:12:41Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec Gmail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut vérifier que le nom de l&#039;expéditeur utiliser par le serveur est valide :&lt;br /&gt;
 # vi /etc/mailname&lt;br /&gt;
On peut simplement utiliser &amp;quot;localdomain&amp;quot; pour contourner les problèmes de domaine invalide :&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&amp;lt;font color = blue&amp;gt;.localdomain&amp;lt;/font&amp;gt;&lt;br /&gt;
Enfin, on redémarre le service :&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3280</id>
		<title>Postfix</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Postfix&amp;diff=3280"/>
		<updated>2026-04-13T10:02:24Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Exemple avec Gmail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ubuntu / Debian =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install postfix mailutils&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # dpkg-reconfigure postfix&lt;br /&gt;
Renseigner les options suivantes :&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Internet Site&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf1.PNG|border|Drôle de dénomination..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Non de l&#039;expéditeur&#039;&#039;&#039; (par défaut le nom réel du serveur est utiliser, le modifier ne semble pas avoir d&#039;incidence)&lt;br /&gt;
[[File:Postfix_conf2.PNG|border|Nom de l&#039;expéditeur, normalement le nom du serveur pour l&#039;identifier facilement.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Adresse de réception pour &amp;quot;root&amp;quot; et &amp;quot;postmaster&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
[[File:Postfix conf3.PNG|border|Votre adresse mail pour recevoir les mails internes du serveurs.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Domaines autorisés&#039;&#039;&#039; (&amp;lt;u&amp;gt;laisser par défaut&amp;lt;/u&amp;gt; pour ne rediriger que les mails locaux; le nom &amp;quot;réel&amp;quot; de la machine local doit être inscrit)&lt;br /&gt;
[[File:Postfix conf4.PNG|border|Domaines de destination finale pour Postfix.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;File d&#039;attente&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf5.PNG|border|On laisse par défault, à moins d&#039;utiliser un système de ficgier sans journalisation..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Réseau pour relai mail&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf6.PNG|border|On laisse la valeur par défaut..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Limite taille boîte mail&#039;&#039;&#039; (0 par défaut pour illimiter)&lt;br /&gt;
[[File:Postfix conf7.PNG|border|Taille maximal des boîtes mail..]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Caractère d&#039;identification des adresses locales&#039;&#039;&#039; (par défaut)&lt;br /&gt;
[[File:Postfix conf8.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Protocoles réseau d&#039;écoute activés&#039;&#039;&#039; (tous par défaut)&lt;br /&gt;
[[File:Postfix conf9.PNG|border|On laisse par défaut.]]&lt;br /&gt;
&lt;br /&gt;
On recharge la configuration :&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root: &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
On configure le serveur relai pour l&#039;envoie (SMTP relay) :&lt;br /&gt;
=== Exemple avec &amp;quot;aspmx.l.google.com&amp;quot; ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = grave&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Déconseillé, Google bloquera votre IP dès que votre usage sera suffisamment important pour leur paraître suspect, préférez un serveur SMTP authentifié.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = le relai &#039;&#039;&#039;aspmx.l.google.com&#039;&#039;&#039; permet d&#039;envoyer des courriels sans authentification &amp;lt;u&amp;gt;uniquement&amp;lt;/u&amp;gt; aux adresses gmail.com, par défaut les courriels iront dans &amp;quot;spam&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
 ...&lt;br /&gt;
 relayhost = &amp;lt;font color = blue&amp;gt;[aspmx.l.google.com]:25&amp;lt;/font&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 # systemctl reload postfix&lt;br /&gt;
&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
[https://www.it-connect.fr/configurer-postfix-pour-envoyer-des-mails-avec-gmail/ Source de qualitay]&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
*On install une dépendence :&lt;br /&gt;
 # apt install libsasl2-modules&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname, localhost.$mydomain, localhost&amp;lt;font color = blue&amp;gt;, monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost =&amp;lt;font color = blue&amp;gt; [smtp.gmail.com]:587&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;nomduserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 #smtp_use_tls = yes&amp;lt;/font&amp;gt;&lt;br /&gt;
 mynetworks = 127.0.0.0/:8&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/mail.log&lt;br /&gt;
= Alpine Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add postfix mailutils&lt;br /&gt;
On active le service immédiatement et au redémarrage :&lt;br /&gt;
 # rc-update add postfix default&lt;br /&gt;
 # service postfix start&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;mailutils&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # cp /etc/postfix/aliases /etc/aliases&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail. Don&#039;t receive mail as root!&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:&amp;lt;/font&amp;gt;           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
=== Exemple avec Gmail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
*On install les dépendances pour le cryptage :&lt;br /&gt;
 # apk add libsasl openssl&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
Rajouter les lignes suivantes en fin de fichier :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ajouter le &amp;lt;font color = blue&amp;gt;nom complet&amp;lt;/font&amp;gt; de la machine (peut être obtenu avec la commande &amp;quot;hostname -f&amp;quot;) &lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # grep mail /var/log/messages&lt;br /&gt;
= Alma Linux / Rocky Linux =&lt;br /&gt;
== Installation ==&lt;br /&gt;
 # dnf update&lt;br /&gt;
 # dnf install postfix s-nail&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le paquet &amp;quot;&#039;&#039;&#039;s-nail&#039;&#039;&#039;&amp;quot; permet d&#039;installer la commande &amp;quot;&#039;&#039;&#039;mail&#039;&#039;&#039;&amp;quot; pour tester la configuration.&lt;br /&gt;
 }}&lt;br /&gt;
On active et démarre le service :&lt;br /&gt;
 # systemctl enable postfix --now&lt;br /&gt;
== Configuration==&lt;br /&gt;
On redirige les messages à destination de &amp;quot;root&amp;quot; sur un courriel :&lt;br /&gt;
 # vi /etc/aliases&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 # Person who should get root&#039;s mail&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = blue&amp;gt;root:           &amp;lt;font color = green&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # newaliases&lt;br /&gt;
=== Exemple avec GMail ===&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Pour cette méthode il est recommandé d&#039;utiliser [https://support.google.com/mail/answer/185833?hl=fr &#039;&#039;&#039;&amp;lt;u&amp;gt;un mot de passe d&#039;application&amp;lt;/u&amp;gt;&#039;&#039;&#039;]. (L&#039;autre solution étant d&#039;activer [https://support.google.com/accounts/answer/6010255?hl=fr &#039;&#039;&#039;les applications moins sécurisées&#039;&#039;&#039;] en dégradant la sécurité de votre compte Gmail..)&lt;br /&gt;
 }}&lt;br /&gt;
 # dnf install cyrus-sasl-plain&lt;br /&gt;
*On crée le fichier d&#039;authentification :&lt;br /&gt;
 # vi /etc/postfix/sasl_passwd&lt;br /&gt;
&lt;br /&gt;
 [smtp.gmail.com]:587 &amp;lt;font color = blue&amp;gt;monadresse&amp;lt;/font&amp;gt;@gmail.com:&amp;lt;font color = blue&amp;gt;motdepasse&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # chmod 400 /etc/postfix/sasl_passwd&lt;br /&gt;
 # postmap /etc/postfix/sasl_passwd&lt;br /&gt;
*On crée le certificat :&lt;br /&gt;
 # openssl req -newkey rsa:4096 -new -nodes -x509 -out /etc/postfix/cacert.pem -keyout /etc/postfix/privkey.pem -subj &#039;/CN=localhost&#039;&lt;br /&gt;
*On configure Postfix :&lt;br /&gt;
 # rm /etc/postfix/main.cf -f&lt;br /&gt;
 # vi /etc/postfix/main.cf&lt;br /&gt;
&lt;br /&gt;
 alias_maps = lmdb:/etc/aliases&lt;br /&gt;
 alias_database = lmdb:/etc/aliases&lt;br /&gt;
 myhostname = &amp;lt;font color = green&amp;gt;monserveur&amp;lt;/font&amp;gt;&lt;br /&gt;
 mydestination = $myhostname.$mydomain, $myhostname, localhost.$mydomain, localhost, &amp;lt;font color = blue&amp;gt;monserveur.mondomaine.net&amp;lt;/font&amp;gt;&lt;br /&gt;
 relayhost = [smtp.gmail.com]:587&lt;br /&gt;
 smtp_sasl_auth_enable = yes&lt;br /&gt;
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd&lt;br /&gt;
 smtp_sasl_security_options = noanonymous&lt;br /&gt;
 smtp_tls_CAfile = /etc/postfix/cacert.pem&lt;br /&gt;
 smtp_tls_security_level = encrypt&lt;br /&gt;
 smtp_use_tls = yes&lt;br /&gt;
 mynetworks = 127.0.0.0/8&lt;br /&gt;
 recipient_delimiter = +&lt;br /&gt;
 &lt;br /&gt;
 mailbox_size_limit = 0&lt;br /&gt;
On redémarre le service pour activer les modifications :&lt;br /&gt;
 # newaliases&lt;br /&gt;
 # service postfix reload&lt;br /&gt;
== Test ==&lt;br /&gt;
 # mail -s &amp;quot;Courriel de test&amp;quot; root &amp;lt; /dev/null&lt;br /&gt;
Le courriel doit arriver à l&#039;adresse de redirection renseignée pour &amp;quot;root&amp;quot; dans &amp;quot;/etc/aliases&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Pour débugger :&lt;br /&gt;
 # cat /var/log/maillog&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3279</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3279"/>
		<updated>2026-04-11T14:18:38Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Hookscript pour permettre aux cartes réseaux virtuelles de communiquer avec les VF sur le même bridge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre aux cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| Contenu du script :&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inscription du script ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le script ne n&#039;est pas nécessaire sur les machines avec la VF, il doit être inscrit sur les machines avec une carte réseau virtuelle qui souhaite communiquer avec une VF sur le même bridge!&lt;br /&gt;
 }}&lt;br /&gt;
Pour les VM sans VF :&lt;br /&gt;
 # qm set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;br /&gt;
Pour les conteneurs LXC :&lt;br /&gt;
 # pct set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3278</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3278"/>
		<updated>2026-04-11T14:18:20Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Hookscript pour permettre aux Cartes réseaux vistuelles de communiquer avec les VF sur le même bridge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre aux cartes réseaux virtuelles de communiquer avec les VF sur le même bridge =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| Contenu du script :&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inscription du script ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le script ne n&#039;est pas nécessaire sur les machines avec la VF, il doit être inscrit sur les machines avec une carte réseau virtuelle qui souhaite communiquer avec une VF sur le même bridge!&lt;br /&gt;
 }}&lt;br /&gt;
Pour les VM sans VF :&lt;br /&gt;
 # qm set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;br /&gt;
Pour les conteneurs LXC :&lt;br /&gt;
 # pct set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3277</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3277"/>
		<updated>2026-04-11T14:18:01Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Hookscript pour permettre au VF de communiquer entre elle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre aux Cartes réseaux vistuelles de communiquer avec les VF sur le même bridge =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| Contenu du script :&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inscription du script ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le script ne n&#039;est pas nécessaire sur les machines avec la VF, il doit être inscrit sur les machines avec une carte réseau virtuelle qui souhaite communiquer avec une VF sur le même bridge!&lt;br /&gt;
 }}&lt;br /&gt;
Pour les VM sans VF :&lt;br /&gt;
 # qm set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;br /&gt;
Pour les conteneurs LXC :&lt;br /&gt;
 # pct set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3276</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3276"/>
		<updated>2026-04-11T13:59:34Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Installation du script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre au VF de communiquer entre elle =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| Contenu du script :&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inscription du script ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le script ne n&#039;est pas nécessaire sur les machines avec la VF, il doit être inscrit sur les machines avec une carte réseau virtuelle qui souhaite communiquer avec une VF sur le même bridge!&lt;br /&gt;
 }}&lt;br /&gt;
Pour les VM sans VF :&lt;br /&gt;
 # qm set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;br /&gt;
Pour les conteneurs LXC :&lt;br /&gt;
 # pct set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3275</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3275"/>
		<updated>2026-04-11T13:58:38Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Hookscript pour permettre au VF de communiquer entre elle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre au VF de communiquer entre elle =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh https://raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| Contenu du script :&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Inscription du script ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le script ne n&#039;est pas nécessaire sur les machines avec la VF, il doit être inscrit sur les machines avec une carte réseau virtuelle qui souhaite communiquer avec une VF sur le même bridge!&lt;br /&gt;
 }}&lt;br /&gt;
Pour les VM sans VF :&lt;br /&gt;
 # qm set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;br /&gt;
Pour les conteneurs LXC :&lt;br /&gt;
 # pct set &amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt; --hookscript local:snippets/bridgefix.sh&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3274</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3274"/>
		<updated>2026-04-11T13:20:00Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Hookscript pour permettre au VF de communiquer entre elle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre au VF de communiquer entre elle =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh https://raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| Contenu du script :&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3273</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3273"/>
		<updated>2026-04-11T13:18:30Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Créer des VF avec adresses MAC fixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
= Hookscript pour permettre au VF de communiquer entre elle =&lt;br /&gt;
[https://github.com/jdlayman/pve-hookscript-sriov Source github]&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Certaines cartes réseaux (exemple la Intel x710) empêchent les cartes réseaux virtuelles de communiquer avec les VF sur le même &amp;quot;bridge&amp;quot; réseau, ce script permet de contourner le problème en inscrivant les MAC adress sur le &amp;quot;bridge&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
== Installation du script ==&lt;br /&gt;
 # mkdir -p /var/lib/vz/snippets&lt;br /&gt;
 # wget -O /var/lib/vz/snippets/bridgefix.sh https://raw.githubusercontent.com/jdlayman/pve-hookscript-sriov/master/bridgefix.sh&lt;br /&gt;
 # chmod 755 /var/lib/vz/snippets/bridgefix.sh&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Contenu du script&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# Hook script for PVE guests (hookscript config option)&lt;br /&gt;
# You can set this via pct/qm with&lt;br /&gt;
# pct set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# qm set &amp;lt;vmid&amp;gt; -hookscript &amp;lt;volume-id&amp;gt;&lt;br /&gt;
# where &amp;lt;volume-id&amp;gt; has to be an executable file in the snippets folder&lt;br /&gt;
# of any storage with directories e.g.:&lt;br /&gt;
# for KVM: qm set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
# or&lt;br /&gt;
# for CT: pct set 100 -hookscript local:snippets/pf-bridge-fdb.sh&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Modified from ctr&#039;s hookscript to add bridged CT and VM MAC addresses to the upstream PF interface.&lt;br /&gt;
# https://forum.proxmox.com/threads/communication-issue-between-sriov-vm-vf-and-ct-on-pf-bridge.68638/#post-435959&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
USAGE=&amp;quot;Usage: $0 vmid phase&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$#&amp;quot; -ne &amp;quot;2&amp;quot; ]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;GUEST HOOK: $0 $*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# First argument is the vmid&lt;br /&gt;
&lt;br /&gt;
vmid=$1&lt;br /&gt;
if [[ $1 == ?(-)+([:digit:]) ]]; then&lt;br /&gt;
  echo &amp;quot;$USAGE&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Second argument is the phase&lt;br /&gt;
&lt;br /&gt;
phase=$2&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start|post-start|pre-stop|post-stop) : ;;&lt;br /&gt;
  *)                                       echo &amp;quot;got unknown phase ${phase}&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function fixup_bridge_fdb {&lt;br /&gt;
  OPERATION=$1&lt;br /&gt;
  # Lookup Proxmox config for by vmid&lt;br /&gt;
  CONFFILE=$(find /etc/pve -type f -name ${vmid}.conf)&lt;br /&gt;
  if [ -f &amp;quot;${CONFFILE}&amp;quot; ]; then&lt;br /&gt;
    # get defined networks&lt;br /&gt;
    NETWORKS=$(egrep &amp;quot;^net&amp;quot; ${CONFFILE}| fgrep bridge= | awk &#039;{print $2}&#039;)&lt;br /&gt;
    #echo $NETWORKS&lt;br /&gt;
    for i in ${NETWORKS}; do&lt;br /&gt;
      #echo $i&lt;br /&gt;
      declare macaddr=&amp;quot;&amp;quot;&lt;br /&gt;
      declare bridge=&amp;quot;&amp;quot;&lt;br /&gt;
      declare vlan=&amp;quot;&amp;quot;&lt;br /&gt;
      IFS=\, read -a NETWORK &amp;lt;&amp;lt;&amp;lt;&amp;quot;$i&amp;quot;&lt;br /&gt;
      # get attributes for current network&lt;br /&gt;
      for item in &amp;quot;${NETWORK[@]}&amp;quot;; do&lt;br /&gt;
        IFS=\= read -a kv &amp;lt;&amp;lt;&amp;lt;&amp;quot;$item&amp;quot;&lt;br /&gt;
        case &amp;quot;${kv[0]}&amp;quot; in&lt;br /&gt;
          tag)     vlan=${kv[1]};;&lt;br /&gt;
          bridge)  bridge=${kv[1]};;&lt;br /&gt;
          virtio)  macaddr=${kv[1]};;&lt;br /&gt;
          hwaddr)  macaddr=${kv[1]};;&lt;br /&gt;
          vmxnet3) macaddr=${kv[1]};;&lt;br /&gt;
        esac&lt;br /&gt;
      done&lt;br /&gt;
      # special processing needed if member of vlan&lt;br /&gt;
      if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
        vlancheck=${vlan}&lt;br /&gt;
      else&lt;br /&gt;
        vlancheck=&amp;quot;checking&amp;quot;&lt;br /&gt;
      fi&lt;br /&gt;
      # lookup member interfaces of defined bridge interface&lt;br /&gt;
      bridgeinterfaces=$(ls -1 /sys/class/net/${bridge}/brif/ 2&amp;gt;/dev/null)&lt;br /&gt;
      # for every member interface, if it is an SR-IOV PF then ...&lt;br /&gt;
      #echo $bridgeinterfaces&lt;br /&gt;
      for memberint in ${bridgeinterfaces}; do&lt;br /&gt;
        if [ -d &amp;quot;/sys/class/net/${memberint}/bonding&amp;quot; ]; then&lt;br /&gt;
          # find interfaces that are bonded&lt;br /&gt;
          IFS=&#039; &#039; read -a bondedints &amp;lt;&amp;lt;&amp;lt; $(cat /sys/class/net/${memberint}/bonding/slaves 2&amp;gt;/dev/null)&lt;br /&gt;
          for bondedint in ${bondedints[@]}; do&lt;br /&gt;
            #echo $bondedint&lt;br /&gt;
            if [ -L &amp;quot;/sys/class/net/${bondedint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
              # echo $memberint&lt;br /&gt;
              subint=&amp;quot;${bondedint}&amp;quot;&lt;br /&gt;
              # check if entry in fdb and only execute when needed&lt;br /&gt;
              present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
              if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
                echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
                bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
              fi&lt;br /&gt;
            fi&lt;br /&gt;
          done&lt;br /&gt;
        fi&lt;br /&gt;
        # only proceed if memberinterface is PF with VF&lt;br /&gt;
        if [ -L &amp;quot;/sys/class/net/${memberint}/device/virtfn0&amp;quot; ]; then&lt;br /&gt;
          # echo $memberint&lt;br /&gt;
          subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          ## check if a vf is on the vlan&lt;br /&gt;
          #vf=$(ip link show dev ${memberint} 2&amp;gt;/dev/null |awk -v vlan=&amp;quot;${vlancheck}&amp;quot; &#039;{ if( $6 ~ vlan) print $2}&#039;)&lt;br /&gt;
          #if [ ! -z &amp;quot;${vf}&amp;quot; ]; then&lt;br /&gt;
          #  if [ ! -z &amp;quot;${vlan}&amp;quot; ]; then&lt;br /&gt;
          #    subint=&amp;quot;${memberint}.${vlan}&amp;quot;&lt;br /&gt;
          #  else&lt;br /&gt;
          #    subint=&amp;quot;${memberint}&amp;quot;&lt;br /&gt;
          #  fi&lt;br /&gt;
          #fi&lt;br /&gt;
          # check if entry in fdb and only execute when needed&lt;br /&gt;
          present=$(bridge fdb show dev ${subint} | fgrep -i ${macaddr})&lt;br /&gt;
          if [[ -z $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;add&amp;quot; ]] || [[ -n $present &amp;amp;&amp;amp; $OPERATION == &amp;quot;del&amp;quot; ]]; then&lt;br /&gt;
            echo bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
            bridge fdb ${OPERATION} ${macaddr} dev ${subint}&lt;br /&gt;
          fi&lt;br /&gt;
        fi&lt;br /&gt;
      done&lt;br /&gt;
    done&lt;br /&gt;
  else&lt;br /&gt;
    echo &amp;quot;VM or CT does not exist, aborting&amp;quot;&lt;br /&gt;
  fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;${phase}&amp;quot; in&lt;br /&gt;
  pre-start)  echo &amp;quot;${vmid} is starting, doing bridge fdb setup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb add ;;&lt;br /&gt;
  post-stop)  echo &amp;quot;${vmid} stopped. Doing bridge fdb cleanup.&amp;quot; &amp;amp;&amp;amp; fixup_bridge_fdb del ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3272</id>
		<title>NIC SR-IOV</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=NIC_SR-IOV&amp;diff=3272"/>
		<updated>2026-04-11T12:29:17Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Créer des VF */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Voir le nombre MAX de VF supportées=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_totalvfs&lt;br /&gt;
=Voir combien de VF sont actives actuellement=&lt;br /&gt;
 root@proxmox:~# cat /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
=Créer des VF temporaire=&lt;br /&gt;
Exemple : créer 4 VF&lt;br /&gt;
 root@proxmox:~# echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Il est recommandé de ne pas créer plus de VF que nécessaire.&lt;br /&gt;
 }}&lt;br /&gt;
=Créer des VF=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec 4 VF.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = modéré&lt;br /&gt;
  | texte  = Il est préférable d&#039;utiliser [[#Créer_des_VF_avec_adresses_MAC_fixes|le service de création de VF avec MAC adress assignées]] pour plus de stabiliter.&lt;br /&gt;
 }}&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/sh -c &#039;echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;/device/sriov_numvfs&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;br /&gt;
&lt;br /&gt;
=Créer des VF avec adresses MAC fixes=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Permet d&#039;éviter les problèmes de VF sans adresses MAC au reboot des VM etc..&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
On crée un service :&lt;br /&gt;
 root@proxmoxve:~# vi /etc/systemd/system/sriov-x710.service&lt;br /&gt;
Contenu :&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Enable SR-IOV on Intel X710 (fixed VF MACs)&lt;br /&gt;
 After=systemd-modules-load.service&lt;br /&gt;
 Before=pve-guests.service&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=oneshot&lt;br /&gt;
 ExecStart=/bin/bash -c &#039;set -e; \&lt;br /&gt;
 DEV=&amp;lt;font color=green&amp;gt;enp8s0f1np1&amp;lt;/font&amp;gt;; \&lt;br /&gt;
 for i in {1..20}; do [ -e /sys/class/net/$DEV ] &amp;amp;&amp;amp; break; sleep 0.2; done; \&lt;br /&gt;
 echo 0 &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 echo &amp;lt;font color=green&amp;gt;4&amp;lt;/font&amp;gt; &amp;gt; /sys/class/net/$DEV/device/sriov_numvfs; \&lt;br /&gt;
 sleep 1; \&lt;br /&gt;
 ip link set $DEV vf 0 mac 02:00:00:10:01:00; \&lt;br /&gt;
 ip link set $DEV vf 1 mac 02:00:00:10:01:01; \&lt;br /&gt;
 ip link set $DEV vf 2 mac 02:00:00:10:01:02; \&lt;br /&gt;
 ip link set $DEV vf 3 mac 02:00:00:10:01:03&#039;&lt;br /&gt;
 RemainAfterExit=yes&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target&lt;br /&gt;
&lt;br /&gt;
Puis on active le service ;&lt;br /&gt;
 systemctl daemon-reload&lt;br /&gt;
 systemctl enable sriov-x710&lt;br /&gt;
 systemctl start sriov-x710&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3271</id>
		<title>WireGuard LXC Alpine Linux</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3271"/>
		<updated>2026-04-04T13:45:39Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Importation ancienne configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Barre de progression|100|largeur=400px|hauteur=10|couleur1 = green|texte=Testé et approuvé}}&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter qu&#039;il existe une version Docker tout en un (VPN + GUI de gestion) beaucoup plus simple à mettre en place, mais avec surcouche réseau d&#039;un conteneur, nommé [https://github.com/wg-easy/wg-easy wg-easy] (non testé).&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
= LXC Alpine 3.12 =&lt;br /&gt;
== Installation de WireGuard Server ==&lt;br /&gt;
Avec un téléphone android connecté !!! :&lt;br /&gt;
&lt;br /&gt;
[[File:Capture_ressource_WG_AlpineLXC.PNG|Incroyablement économe !]]&lt;br /&gt;
&lt;br /&gt;
On autorise le routage :&lt;br /&gt;
 lxcalpine:~# echo &#039;net.ipv4.ip_forward=1&#039; &amp;gt;&amp;gt; /etc/sysctl.conf&lt;br /&gt;
 lxcalpine:~# sysctl -p&lt;br /&gt;
&lt;br /&gt;
 net.ipv4.ip_forward = 1&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# rc-update add sysctl default&lt;br /&gt;
&lt;br /&gt;
Puis on install :&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 lxcalpine:~# apk add wireguard-tools&lt;br /&gt;
== Configuration de WireGuard Server ==&lt;br /&gt;
On génère la configuration sur [https://www.wireguardconfig.com/ &#039;&#039;&#039;wireguardconfig&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/wg0.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;[Interface]&lt;br /&gt;
Address = 10.0.0.1/24&lt;br /&gt;
ListenPort = 51820&lt;br /&gt;
PrivateKey = *****************************************&lt;br /&gt;
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.2/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.3/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.4/32&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/client1.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 [Interface]&lt;br /&gt;
 Address = 10.0.0.2/24&lt;br /&gt;
 ListenPort = 51820&lt;br /&gt;
 PrivateKey = *****************************************&lt;br /&gt;
 &lt;br /&gt;
 [Peer]&lt;br /&gt;
 PublicKey = *****************************************&lt;br /&gt;
 PresharedKey = *****************************************&lt;br /&gt;
 &amp;lt;font color=blue&amp;gt;AllowedIPs = 0.0.0.0/1, 128.0.0.0/1&amp;lt;/font&amp;gt;&lt;br /&gt;
 Endpoint = myserver.dyndns.org:51820&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Opération à répéter pour chaque client..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; modifié pour ne pas rediriger tout le traffic et perdre l&#039;accès a son réseau local..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; peut aussi être modifié pour ne rediriger que les paquets a destination du réseau distant et conserver Internet localement sans passer par le serveur distant, exemple : &amp;quot;&#039;&#039;&#039;AllowedIPs = 192.168.2.0/24&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Test du service ==&lt;br /&gt;
&amp;lt;code&amp;gt;lxcalpine:~# wg-quick up wg0&amp;lt;/code&amp;gt;&lt;br /&gt;
 [#] ip link add wg0 type wireguard&lt;br /&gt;
 [#] wg setconf wg0 /dev/fd/63&lt;br /&gt;
 [#] ip -4 address add 10.0.0.1/24 dev wg0&lt;br /&gt;
 [#] ip link set mtu 1420 up dev wg0&lt;br /&gt;
 [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;interface: wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: *****************************************&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
   listening port: 51820&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.2/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.3/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.4/32&lt;br /&gt;
&lt;br /&gt;
Fin du test :&lt;br /&gt;
 lxcalpine:~# wg-quick down wg0&lt;br /&gt;
&lt;br /&gt;
 [#] ip link delete dev wg0&lt;br /&gt;
 [#] iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
== Création du script init.d pour lancer le service ==&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/r0v/48e18c9584d4b5ecb96f593dec9a938c &#039;&#039;&#039;source&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# vi /etc/init.d/wireguard&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
    need localmount&lt;br /&gt;
    need net&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 start() {&lt;br /&gt;
    ebegin &amp;quot;Starting wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick up wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 stop() {&lt;br /&gt;
    ebegin &amp;quot;Stopping wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick down wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
On active le script immédiatement et au redémarrage :&lt;br /&gt;
 lxcalpine:~# chmod +x /etc/init.d/wireguard&lt;br /&gt;
 lxcalpine:~# rc-update add wireguard default&lt;br /&gt;
 lxcalpine:~# service wireguard start&lt;br /&gt;
On peut [[#Test_du_service|tester]] avec &amp;quot;wg&amp;quot;, rebooter pour vérifier etc...&lt;br /&gt;
&lt;br /&gt;
== (Optionnel) QR Code ==&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk add libqrencode&lt;br /&gt;
&lt;br /&gt;
Exemple :&lt;br /&gt;
 lxcalpine:~# qrencode -t ansiutf8 &amp;lt; /etc/wireguard/client1.conf&lt;br /&gt;
&lt;br /&gt;
voila!&lt;br /&gt;
== Ajout d&#039;un utilisateur ==&lt;br /&gt;
On crée les clefs de cryptage pour le nouvel utilisateur dans le repertoir &amp;quot;tmp&amp;quot; :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt; par le nom souhaité.&lt;br /&gt;
 }}&lt;br /&gt;
 # cd /tmp/&lt;br /&gt;
 # umask 077&lt;br /&gt;
 # name=&amp;quot;&amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 # wg genkey | tee &amp;quot;${name}.key&amp;quot; | wg pubkey &amp;gt; &amp;quot;${name}.pub&amp;quot;&lt;br /&gt;
 # wg genpsk &amp;gt; &amp;quot;${name}.psk&amp;quot;&lt;br /&gt;
On peuple le fichier de configuration de WireGuard avec le nouvel utilisateur :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt; par une adresse VPN libre.&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PublicKey = $(cat &amp;quot;${name}.pub&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
Ensuite on peuple le fichier de configuration utilisateur, il faut d&#039;abord récupérer la clef pubique du serveur (ici en bleu) :&lt;br /&gt;
 # wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;interface&amp;lt;/font&amp;gt;: &amp;lt;font color = darkgreen&amp;gt;wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt; adresse VPN libre sélectionner dans le fichier utilisateur ci-dessus / &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt; l&#039;adresse du serveur cible à atteindre (différentes configuration possible [[#Configuration_de_WireGuard_Server|voir la configuration serveur)]]&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;[Interface]&amp;quot; &amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Address = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;ListenPort = &amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PrivateKey = $(cat &amp;quot;${name}.key&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PublicKey = &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Endpoint = &amp;lt;font color = blue&amp;gt;monserveurvpn.exemple.net&amp;lt;/font&amp;gt;:&amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
Enfin on redémarre le service pour appliquer les changement :&lt;br /&gt;
 # service wireguard restart&lt;br /&gt;
= wireguard-ui =&lt;br /&gt;
[https://github.com/ngoduykhanh/wireguard-ui Source github]&lt;br /&gt;
== Installation ==&lt;br /&gt;
On récupère le programme :&lt;br /&gt;
 # mkdir -p /opt/wgui&lt;br /&gt;
 # cd /opt/wgui&lt;br /&gt;
 # &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/ngoduykhanh/wireguard-ui/releases/download/v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;/wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # tar -xzf wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # rm wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
On peut tester le fonctionnement du serveur en lançant le bin :&lt;br /&gt;
 # /opt/wgui/wireguard-ui&lt;br /&gt;
Le serveur web sera accessible sur le port 5000, avec l&#039;utilisateur &amp;quot;admin&amp;quot; et mot de passe &amp;quot;admin&amp;quot; par défault (penser à changer le mot de passe immédiatement).&lt;br /&gt;
== Création des scripts RC ==&lt;br /&gt;
=== (Optionnel) Script pour surveillé wg0.conf ===&lt;br /&gt;
On crée le script exécutable :&lt;br /&gt;
 # vi /usr/local/bin/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/bin/sh&lt;br /&gt;
 wg-quick down wg0&lt;br /&gt;
 wg-quick up wg0&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /usr/local/bin/wgui&lt;br /&gt;
Puis le script RC pour l&#039;utiliser :&lt;br /&gt;
 # vi /etc/init.d/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 command=/sbin/inotifyd&lt;br /&gt;
 command_args=&amp;quot;/usr/local/bin/wgui /etc/wireguard/wg0.conf:w&amp;quot;&lt;br /&gt;
 pidfile=/run/${RC_SVCNAME}.pid&lt;br /&gt;
 command_background=yes&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wgui&lt;br /&gt;
Enfin on inscrit le script RC au système :&lt;br /&gt;
 # rc-service wgui start&lt;br /&gt;
 # rc-update add wgui default&lt;br /&gt;
=== Script RC pour lancer le GUI automatiquement ===&lt;br /&gt;
 # vi /etc/init.d/wireguard-ui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 name=&amp;quot;wireguard-ui&amp;quot;&lt;br /&gt;
 directory=&amp;quot;/opt/wgui&amp;quot;&lt;br /&gt;
 command=&amp;quot;/opt/wgui/wireguard-ui&amp;quot;&lt;br /&gt;
 command_background=&amp;quot;yes&amp;quot;&lt;br /&gt;
 pidfile=&amp;quot;/run/${RC_SVCNAME}.pid&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
     need net&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wireguard-ui&lt;br /&gt;
 # rc-update add wireguard-ui default&lt;br /&gt;
 # rc-service wireguard-ui start&lt;br /&gt;
 # reboot&lt;br /&gt;
== Importation ancienne configuration ==&lt;br /&gt;
&amp;quot;wireguard-ui&amp;quot; est un projet fonctionnel mais dont le développement est abandonné/lent. L&#039;interface web n&#039;est pas prévu pour lire une ancienne configuation, si vous ne démarrez pas votre VPN avec la GUI (donc en partant de 0) il faudra modifier des fichiers .json à la main dans &amp;quot;/opt/wgui/db/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
A noter qu&#039;un utilisateur à créé [https://github.com/ngoduykhanh/wireguard-ui/issues/485 un script d&#039;import automatisé] (non testé).&lt;br /&gt;
&lt;br /&gt;
Certains paramètres pourront être recréé via la GUI, d&#039;autres devront être modifié à la main (pensez a sauvegarder votre wg0.conf et fichiers clients), exemples :&lt;br /&gt;
* Les clefs du serveur se trouvent dans &amp;quot;/opt/wgui/db/server/keypair.json&amp;quot;&lt;br /&gt;
* il est possible de recréer les utilisateur via la GUI, mais si vous spécifiez la &amp;quot;PublicKey&amp;quot; et &amp;quot;PresharedKey&amp;quot; alors il faudra renseigner la &amp;quot;private_key&amp;quot; dans &amp;quot;/opt/wgui/db/clients/&amp;lt;font color = green&amp;gt;client&amp;lt;/font&amp;gt;.json&lt;br /&gt;
== vhost nginx ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
 listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name     &amp;lt;font color = blue&amp;gt;wireguard.exemple.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
    # return 404;&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
 server_name     &amp;lt;font color = blue&amp;gt;wireguard.exemple.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
     error_page 403  &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;wireguard.exemple.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
             #proxy_set_header Upgrade           $http_upgrade;&lt;br /&gt;
             #proxy_set_header Connection        &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
             proxy_set_header Host              $host;&lt;br /&gt;
             proxy_set_header X-Real-IP         $remote_addr;&lt;br /&gt;
             proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;&lt;br /&gt;
             proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
             #proxy_set_header X-Forwarded-Host  $host;&lt;br /&gt;
             #proxy_set_header X-Forwarded-Port  $server_port;&lt;br /&gt;
             proxy_pass http://&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:5000;&lt;br /&gt;
             # proxy_pass &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;IP_SERVEUR_WEB:PORT; #Alternatif pour redirection sur https&lt;br /&gt;
             #proxy_buffering off; #activer pour désactiver le &amp;quot;buffering&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     listen [::]:443 ssl; # managed by Certbot&lt;br /&gt;
     listen 443 ssl; # managed by Certbot&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;wireguard.exemple.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;wireguard.exemple.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
     ssl_trusted_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;wireguard.exemple.net&amp;lt;/font&amp;gt;/chain.pem;&lt;br /&gt;
     #ssl_stapling on;&lt;br /&gt;
     #ssl_stapling_verify on;&lt;br /&gt;
 &lt;br /&gt;
 server_tokens off;&lt;br /&gt;
 add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot;;&lt;br /&gt;
 add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot;;&lt;br /&gt;
 add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Nginx_ReverseProxy_LXC_Alpine_Linux&amp;diff=3270</id>
		<title>Nginx ReverseProxy LXC Alpine Linux</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Nginx_ReverseProxy_LXC_Alpine_Linux&amp;diff=3270"/>
		<updated>2026-04-04T13:21:52Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Création d&amp;#039;un &amp;quot;vhost&amp;quot; sécurisé */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Installation d&#039;un reverse proxy Nginx avec Certbot. Le container servira de point d&#039;entrée pour les requêtes HTTP (port:80) et HTTPS (port:443) pour les rediriger sur les serveurs idoines en HTTPS uniquement. Certbot permettra de créer des certificat SSL reconnu par les navigateurs comme sécurisé.&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
On installe les paquets de base :&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add nginx certbot certbot-nginx&lt;br /&gt;
On modifie la configuration de Nginx pour la rapprocher du standard des distributions type Debian :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Optionnel, sinon utiliser silmplement le répertoire &amp;quot;&#039;&#039;&#039;/etc/nginx/conf.d/&#039;&#039;&#039;&amp;quot; (Alpine 3.12) ou &amp;quot;&#039;&#039;&#039;/etc/nginx/http.d/&#039;&#039;&#039;&amp;quot; (Alpine 3.13)&lt;br /&gt;
 }}&lt;br /&gt;
 # mkdir /etc/nginx/sites-available &amp;amp;&amp;amp; mkdir /etc/nginx/sites-enabled&lt;br /&gt;
On modifie le fichier de configuration de Nginx en ajoutant la ligne suivante :&lt;br /&gt;
 # vi /etc/nginx/nginx.conf&lt;br /&gt;
----&lt;br /&gt;
* Alpine Linux 3.12 :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         # Includes virtual hosts configs.                                       &lt;br /&gt;
         include /etc/nginx/conf.d/*.conf;                                         &lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;include /etc/nginx/sites-enabled/*;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
* Alpine Linux 3.13 :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         # Includes virtual hosts configs.&lt;br /&gt;
         include /etc/nginx/http.d/*.conf;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;include /etc/nginx/sites-enabled/*;&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Voir [[UpgradeAlpine#NGINX|&#039;&#039;&#039;ce lien&#039;&#039;&#039;]]&lt;br /&gt;
 }}&lt;br /&gt;
----&lt;br /&gt;
On supprime le site par défaut :&lt;br /&gt;
* Alpine Linux 3.12 :&lt;br /&gt;
 # rm /etc/nginx/conf.d/default.conf&lt;br /&gt;
* Alpine Linux 3.13 :&lt;br /&gt;
 # rm /etc/nginx/http.d/default.conf&lt;br /&gt;
On active le serveur :&lt;br /&gt;
 # rc-update add nginx default&lt;br /&gt;
 # service nginx start&lt;br /&gt;
&lt;br /&gt;
=Création d&#039;un &amp;quot;vhost&amp;quot; sécurisé=&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Exemple avec &amp;quot;&#039;&#039;&#039;site.exemple.net&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
 }}&lt;br /&gt;
==Création du certificat==&lt;br /&gt;
 # certbot certonly --nginx --agree-tos --email &amp;lt;font color = blue&amp;gt;moncourriel@exemple.net&amp;lt;/font&amp;gt; -d &amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Création du vhost==&lt;br /&gt;
On crée le fichier de configuration dans &amp;quot;/etc/nginx/site-availables&amp;quot;&lt;br /&gt;
 # vi /etc/nginx/sites-available/&amp;lt;font color = blue&amp;gt;exemple&amp;lt;/font&amp;gt;.conf&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
 listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name     &amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
    # return 404;&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
 server_name     &amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
     error_page 403  https://&amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
             proxy_set_header Upgrade           $http_upgrade;&lt;br /&gt;
             proxy_set_header Connection        &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
             proxy_set_header Host              $host;&lt;br /&gt;
             proxy_set_header X-Real-IP         $remote_addr;&lt;br /&gt;
             proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;&lt;br /&gt;
             proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
             proxy_set_header X-Forwarded-Host  $host;&lt;br /&gt;
             proxy_set_header X-Forwarded-Port  $server_port;&lt;br /&gt;
             proxy_pass http://&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB:PORT&amp;lt;/font&amp;gt;;&lt;br /&gt;
             # proxy_pass http&amp;lt;font color = red&amp;gt;s&amp;lt;/font&amp;gt;://&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB:PORT&amp;lt;/font&amp;gt;; #Alternatif pour redirection sur https&lt;br /&gt;
             #proxy_buffering off; #activer pour désactiver le &amp;quot;buffering&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     listen [::]:443 ssl; # managed by Certbot&lt;br /&gt;
     listen 443 ssl; # managed by Certbot&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
     ssl_trusted_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;/chain.pem;&lt;br /&gt;
     #ssl_stapling on;&lt;br /&gt;
     #ssl_stapling_verify on;&lt;br /&gt;
 &lt;br /&gt;
 server_tokens off;&lt;br /&gt;
 add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot;;&lt;br /&gt;
 add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot;;&lt;br /&gt;
 add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Test et activation ==&lt;br /&gt;
On crée un lien symbolique du fichier de configuration dans le répertoire des site actifs :&lt;br /&gt;
 # ln -s /etc/nginx/sites-available/&amp;lt;font color = blue&amp;gt;exemple&amp;lt;/font&amp;gt;.conf /etc/nginx/sites-enabled/&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Ainsi pour désactiver et conserver le fichier de configuration d&#039;un site on ne supprime que le lien symbolique et on recharge Nginx.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = modéré&lt;br /&gt;
  | icône = important&lt;br /&gt;
  | texte  = Toujours utiliser les chemins complets pour la création des liens symboliques.&lt;br /&gt;
 }}&lt;br /&gt;
On test la configuration :&lt;br /&gt;
 # nginx -t&lt;br /&gt;
On recharge la configuration pour activation :&lt;br /&gt;
 # service nginx reload&lt;br /&gt;
On peut aussi utiliser la commande suivante :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
&lt;br /&gt;
= Renouvellement certificat &amp;quot;Let&#039;s Encrypt&amp;quot; =&lt;br /&gt;
== Ligne de commande ==&lt;br /&gt;
La commande suivante renouvellera tous les certificats avec une période de validité inférieur à 30 jours :&lt;br /&gt;
 # certbot renew&lt;br /&gt;
== Automatisation via la crontab ==&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Dans cet exemple la commande est lancée tous les premiers du mois à 4h00 avec la date et la sortie texte dans le fichier &amp;quot;/var/log/letsencrypt/crontupdate.log&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
 # crontab -e&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 0       4       1       *       *       /bin/date &amp;gt; /var/log/letsencrypt/crontupdate.log &amp;amp;&amp;amp; /usr/bin/certbot renew &amp;gt;&amp;gt; /var/log/letsencrypt/crontupdate.log&lt;br /&gt;
&lt;br /&gt;
On peut également recevoir le résultat par mail :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Postfix#Alpine_Linux|installation d&#039;un relais local Postfix]].&lt;br /&gt;
 }}&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color =blue&amp;gt;MAILTO=root&amp;lt;/font&amp;gt;&lt;br /&gt;
 0       4       1       *       *       /bin/date &amp;gt; /var/log/letsencrypt/crontupdate.log &amp;amp;&amp;amp; /usr/bin/certbot renew &amp;gt;&amp;gt; /var/log/letsencrypt/crontupdate.log&lt;br /&gt;
&lt;br /&gt;
= Révocation certificat &amp;quot;Let&#039;s Encrypt&amp;quot; =&lt;br /&gt;
 # certbot revoke --cert-name &amp;lt;font color = blue&amp;gt;site.exemple.net&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -&lt;br /&gt;
 Would you like to delete the certificate(s) you just revoked, along with all&lt;br /&gt;
 earlier and later versions of the certificate?&lt;br /&gt;
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -&lt;br /&gt;
 (Y)es (recommended)/(N)o:&amp;lt;/font&amp;gt; &#039;&#039;&#039;Y&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -&lt;br /&gt;
 The following certificate(s) are selected for deletion:&lt;br /&gt;
 &lt;br /&gt;
   * site.exemple.net&lt;br /&gt;
 &lt;br /&gt;
 Are you sure you want to delete the above certificate(s)?&lt;br /&gt;
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -&lt;br /&gt;
 (Y)es/(N)o:&amp;lt;/font&amp;gt; &#039;&#039;&#039;Y&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3269</id>
		<title>WireGuard LXC Alpine Linux</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3269"/>
		<updated>2026-04-04T12:59:57Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Création des scripts RC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Barre de progression|100|largeur=400px|hauteur=10|couleur1 = green|texte=Testé et approuvé}}&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter qu&#039;il existe une version Docker tout en un (VPN + GUI de gestion) beaucoup plus simple à mettre en place, mais avec surcouche réseau d&#039;un conteneur, nommé [https://github.com/wg-easy/wg-easy wg-easy] (non testé).&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
= LXC Alpine 3.12 =&lt;br /&gt;
== Installation de WireGuard Server ==&lt;br /&gt;
Avec un téléphone android connecté !!! :&lt;br /&gt;
&lt;br /&gt;
[[File:Capture_ressource_WG_AlpineLXC.PNG|Incroyablement économe !]]&lt;br /&gt;
&lt;br /&gt;
On autorise le routage :&lt;br /&gt;
 lxcalpine:~# echo &#039;net.ipv4.ip_forward=1&#039; &amp;gt;&amp;gt; /etc/sysctl.conf&lt;br /&gt;
 lxcalpine:~# sysctl -p&lt;br /&gt;
&lt;br /&gt;
 net.ipv4.ip_forward = 1&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# rc-update add sysctl default&lt;br /&gt;
&lt;br /&gt;
Puis on install :&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 lxcalpine:~# apk add wireguard-tools&lt;br /&gt;
== Configuration de WireGuard Server ==&lt;br /&gt;
On génère la configuration sur [https://www.wireguardconfig.com/ &#039;&#039;&#039;wireguardconfig&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/wg0.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;[Interface]&lt;br /&gt;
Address = 10.0.0.1/24&lt;br /&gt;
ListenPort = 51820&lt;br /&gt;
PrivateKey = *****************************************&lt;br /&gt;
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.2/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.3/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.4/32&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/client1.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 [Interface]&lt;br /&gt;
 Address = 10.0.0.2/24&lt;br /&gt;
 ListenPort = 51820&lt;br /&gt;
 PrivateKey = *****************************************&lt;br /&gt;
 &lt;br /&gt;
 [Peer]&lt;br /&gt;
 PublicKey = *****************************************&lt;br /&gt;
 PresharedKey = *****************************************&lt;br /&gt;
 &amp;lt;font color=blue&amp;gt;AllowedIPs = 0.0.0.0/1, 128.0.0.0/1&amp;lt;/font&amp;gt;&lt;br /&gt;
 Endpoint = myserver.dyndns.org:51820&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Opération à répéter pour chaque client..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; modifié pour ne pas rediriger tout le traffic et perdre l&#039;accès a son réseau local..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; peut aussi être modifié pour ne rediriger que les paquets a destination du réseau distant et conserver Internet localement sans passer par le serveur distant, exemple : &amp;quot;&#039;&#039;&#039;AllowedIPs = 192.168.2.0/24&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Test du service ==&lt;br /&gt;
&amp;lt;code&amp;gt;lxcalpine:~# wg-quick up wg0&amp;lt;/code&amp;gt;&lt;br /&gt;
 [#] ip link add wg0 type wireguard&lt;br /&gt;
 [#] wg setconf wg0 /dev/fd/63&lt;br /&gt;
 [#] ip -4 address add 10.0.0.1/24 dev wg0&lt;br /&gt;
 [#] ip link set mtu 1420 up dev wg0&lt;br /&gt;
 [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;interface: wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: *****************************************&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
   listening port: 51820&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.2/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.3/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.4/32&lt;br /&gt;
&lt;br /&gt;
Fin du test :&lt;br /&gt;
 lxcalpine:~# wg-quick down wg0&lt;br /&gt;
&lt;br /&gt;
 [#] ip link delete dev wg0&lt;br /&gt;
 [#] iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
== Création du script init.d pour lancer le service ==&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/r0v/48e18c9584d4b5ecb96f593dec9a938c &#039;&#039;&#039;source&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# vi /etc/init.d/wireguard&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
    need localmount&lt;br /&gt;
    need net&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 start() {&lt;br /&gt;
    ebegin &amp;quot;Starting wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick up wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 stop() {&lt;br /&gt;
    ebegin &amp;quot;Stopping wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick down wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
On active le script immédiatement et au redémarrage :&lt;br /&gt;
 lxcalpine:~# chmod +x /etc/init.d/wireguard&lt;br /&gt;
 lxcalpine:~# rc-update add wireguard default&lt;br /&gt;
 lxcalpine:~# service wireguard start&lt;br /&gt;
On peut [[#Test_du_service|tester]] avec &amp;quot;wg&amp;quot;, rebooter pour vérifier etc...&lt;br /&gt;
&lt;br /&gt;
== (Optionnel) QR Code ==&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk add libqrencode&lt;br /&gt;
&lt;br /&gt;
Exemple :&lt;br /&gt;
 lxcalpine:~# qrencode -t ansiutf8 &amp;lt; /etc/wireguard/client1.conf&lt;br /&gt;
&lt;br /&gt;
voila!&lt;br /&gt;
== Ajout d&#039;un utilisateur ==&lt;br /&gt;
On crée les clefs de cryptage pour le nouvel utilisateur dans le repertoir &amp;quot;tmp&amp;quot; :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt; par le nom souhaité.&lt;br /&gt;
 }}&lt;br /&gt;
 # cd /tmp/&lt;br /&gt;
 # umask 077&lt;br /&gt;
 # name=&amp;quot;&amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 # wg genkey | tee &amp;quot;${name}.key&amp;quot; | wg pubkey &amp;gt; &amp;quot;${name}.pub&amp;quot;&lt;br /&gt;
 # wg genpsk &amp;gt; &amp;quot;${name}.psk&amp;quot;&lt;br /&gt;
On peuple le fichier de configuration de WireGuard avec le nouvel utilisateur :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt; par une adresse VPN libre.&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PublicKey = $(cat &amp;quot;${name}.pub&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
Ensuite on peuple le fichier de configuration utilisateur, il faut d&#039;abord récupérer la clef pubique du serveur (ici en bleu) :&lt;br /&gt;
 # wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;interface&amp;lt;/font&amp;gt;: &amp;lt;font color = darkgreen&amp;gt;wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt; adresse VPN libre sélectionner dans le fichier utilisateur ci-dessus / &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt; l&#039;adresse du serveur cible à atteindre (différentes configuration possible [[#Configuration_de_WireGuard_Server|voir la configuration serveur)]]&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;[Interface]&amp;quot; &amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Address = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;ListenPort = &amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PrivateKey = $(cat &amp;quot;${name}.key&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PublicKey = &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Endpoint = &amp;lt;font color = blue&amp;gt;monserveurvpn.exemple.net&amp;lt;/font&amp;gt;:&amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
Enfin on redémarre le service pour appliquer les changement :&lt;br /&gt;
 # service wireguard restart&lt;br /&gt;
= wireguard-ui =&lt;br /&gt;
[https://github.com/ngoduykhanh/wireguard-ui Source github]&lt;br /&gt;
== Installation ==&lt;br /&gt;
On récupère le programme :&lt;br /&gt;
 # mkdir -p /opt/wgui&lt;br /&gt;
 # cd /opt/wgui&lt;br /&gt;
 # &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/ngoduykhanh/wireguard-ui/releases/download/v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;/wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # tar -xzf wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # rm wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
On peut tester le fonctionnement du serveur en lançant le bin :&lt;br /&gt;
 # /opt/wgui/wireguard-ui&lt;br /&gt;
Le serveur web sera accessible sur le port 5000, avec l&#039;utilisateur &amp;quot;admin&amp;quot; et mot de passe &amp;quot;admin&amp;quot; par défault (penser à changer le mot de passe immédiatement).&lt;br /&gt;
== Création des scripts RC ==&lt;br /&gt;
=== (Optionnel) Script pour surveillé wg0.conf ===&lt;br /&gt;
On crée le script exécutable :&lt;br /&gt;
 # vi /usr/local/bin/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/bin/sh&lt;br /&gt;
 wg-quick down wg0&lt;br /&gt;
 wg-quick up wg0&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /usr/local/bin/wgui&lt;br /&gt;
Puis le script RC pour l&#039;utiliser :&lt;br /&gt;
 # vi /etc/init.d/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 command=/sbin/inotifyd&lt;br /&gt;
 command_args=&amp;quot;/usr/local/bin/wgui /etc/wireguard/wg0.conf:w&amp;quot;&lt;br /&gt;
 pidfile=/run/${RC_SVCNAME}.pid&lt;br /&gt;
 command_background=yes&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wgui&lt;br /&gt;
Enfin on inscrit le script RC au système :&lt;br /&gt;
 # rc-service wgui start&lt;br /&gt;
 # rc-update add wgui default&lt;br /&gt;
=== Script RC pour lancer le GUI automatiquement ===&lt;br /&gt;
 # vi /etc/init.d/wireguard-ui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 name=&amp;quot;wireguard-ui&amp;quot;&lt;br /&gt;
 directory=&amp;quot;/opt/wgui&amp;quot;&lt;br /&gt;
 command=&amp;quot;/opt/wgui/wireguard-ui&amp;quot;&lt;br /&gt;
 command_background=&amp;quot;yes&amp;quot;&lt;br /&gt;
 pidfile=&amp;quot;/run/${RC_SVCNAME}.pid&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
     need net&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wireguard-ui&lt;br /&gt;
 # rc-update add wireguard-ui default&lt;br /&gt;
 # rc-service wireguard-ui start&lt;br /&gt;
 # reboot&lt;br /&gt;
== Importation ancienne configuration ==&lt;br /&gt;
&amp;quot;wireguard-ui&amp;quot; est un projet fonctionnel mais dont le développement est abandonné/lent. L&#039;interface web n&#039;est pas prévu pour lire une ancienne configuation, si vous ne démarrez pas votre VPN avec la GUI (donc en partant de 0) il faudra modifier des fichiers .json à la main dans &amp;quot;/opt/wgui/db/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
A noter qu&#039;un utilisateur à créé [https://github.com/ngoduykhanh/wireguard-ui/issues/485 un script d&#039;import automatisé] (non testé).&lt;br /&gt;
&lt;br /&gt;
Certains paramètres pourront être recréé via la GUI, d&#039;autres devront être modifié à la main (pensez a sauvegarder votre wg0.conf et fichiers clients), exemples :&lt;br /&gt;
* Les clefs du serveur se trouvent dans &amp;quot;/opt/wgui/db/server/keypair.json&amp;quot;&lt;br /&gt;
* il est possible de recréer les utilisateur via la GUI, mais si vous spécifiez la &amp;quot;PublicKey&amp;quot; et &amp;quot;PresharedKey&amp;quot; alors il faudra renseigner la &amp;quot;private_key&amp;quot; dans &amp;quot;/opt/wgui/db/clients/&amp;lt;font color = green&amp;gt;client&amp;lt;/font&amp;gt;.json&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3268</id>
		<title>WireGuard LXC Alpine Linux</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3268"/>
		<updated>2026-04-04T11:49:25Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Script RC pour lancer le GUI automatiquement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Barre de progression|100|largeur=400px|hauteur=10|couleur1 = green|texte=Testé et approuvé}}&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter qu&#039;il existe une version Docker tout en un (VPN + GUI de gestion) beaucoup plus simple à mettre en place, mais avec surcouche réseau d&#039;un conteneur, nommé [https://github.com/wg-easy/wg-easy wg-easy] (non testé).&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
= LXC Alpine 3.12 =&lt;br /&gt;
== Installation de WireGuard Server ==&lt;br /&gt;
Avec un téléphone android connecté !!! :&lt;br /&gt;
&lt;br /&gt;
[[File:Capture_ressource_WG_AlpineLXC.PNG|Incroyablement économe !]]&lt;br /&gt;
&lt;br /&gt;
On autorise le routage :&lt;br /&gt;
 lxcalpine:~# echo &#039;net.ipv4.ip_forward=1&#039; &amp;gt;&amp;gt; /etc/sysctl.conf&lt;br /&gt;
 lxcalpine:~# sysctl -p&lt;br /&gt;
&lt;br /&gt;
 net.ipv4.ip_forward = 1&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# rc-update add sysctl default&lt;br /&gt;
&lt;br /&gt;
Puis on install :&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 lxcalpine:~# apk add wireguard-tools&lt;br /&gt;
== Configuration de WireGuard Server ==&lt;br /&gt;
On génère la configuration sur [https://www.wireguardconfig.com/ &#039;&#039;&#039;wireguardconfig&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/wg0.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;[Interface]&lt;br /&gt;
Address = 10.0.0.1/24&lt;br /&gt;
ListenPort = 51820&lt;br /&gt;
PrivateKey = *****************************************&lt;br /&gt;
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.2/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.3/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.4/32&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/client1.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 [Interface]&lt;br /&gt;
 Address = 10.0.0.2/24&lt;br /&gt;
 ListenPort = 51820&lt;br /&gt;
 PrivateKey = *****************************************&lt;br /&gt;
 &lt;br /&gt;
 [Peer]&lt;br /&gt;
 PublicKey = *****************************************&lt;br /&gt;
 PresharedKey = *****************************************&lt;br /&gt;
 &amp;lt;font color=blue&amp;gt;AllowedIPs = 0.0.0.0/1, 128.0.0.0/1&amp;lt;/font&amp;gt;&lt;br /&gt;
 Endpoint = myserver.dyndns.org:51820&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Opération à répéter pour chaque client..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; modifié pour ne pas rediriger tout le traffic et perdre l&#039;accès a son réseau local..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; peut aussi être modifié pour ne rediriger que les paquets a destination du réseau distant et conserver Internet localement sans passer par le serveur distant, exemple : &amp;quot;&#039;&#039;&#039;AllowedIPs = 192.168.2.0/24&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Test du service ==&lt;br /&gt;
&amp;lt;code&amp;gt;lxcalpine:~# wg-quick up wg0&amp;lt;/code&amp;gt;&lt;br /&gt;
 [#] ip link add wg0 type wireguard&lt;br /&gt;
 [#] wg setconf wg0 /dev/fd/63&lt;br /&gt;
 [#] ip -4 address add 10.0.0.1/24 dev wg0&lt;br /&gt;
 [#] ip link set mtu 1420 up dev wg0&lt;br /&gt;
 [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;interface: wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: *****************************************&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
   listening port: 51820&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.2/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.3/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.4/32&lt;br /&gt;
&lt;br /&gt;
Fin du test :&lt;br /&gt;
 lxcalpine:~# wg-quick down wg0&lt;br /&gt;
&lt;br /&gt;
 [#] ip link delete dev wg0&lt;br /&gt;
 [#] iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
== Création du script init.d pour lancer le service ==&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/r0v/48e18c9584d4b5ecb96f593dec9a938c &#039;&#039;&#039;source&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# vi /etc/init.d/wireguard&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
    need localmount&lt;br /&gt;
    need net&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 start() {&lt;br /&gt;
    ebegin &amp;quot;Starting wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick up wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 stop() {&lt;br /&gt;
    ebegin &amp;quot;Stopping wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick down wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
On active le script immédiatement et au redémarrage :&lt;br /&gt;
 lxcalpine:~# chmod +x /etc/init.d/wireguard&lt;br /&gt;
 lxcalpine:~# rc-update add wireguard default&lt;br /&gt;
 lxcalpine:~# service wireguard start&lt;br /&gt;
On peut [[#Test_du_service|tester]] avec &amp;quot;wg&amp;quot;, rebooter pour vérifier etc...&lt;br /&gt;
&lt;br /&gt;
== (Optionnel) QR Code ==&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk add libqrencode&lt;br /&gt;
&lt;br /&gt;
Exemple :&lt;br /&gt;
 lxcalpine:~# qrencode -t ansiutf8 &amp;lt; /etc/wireguard/client1.conf&lt;br /&gt;
&lt;br /&gt;
voila!&lt;br /&gt;
== Ajout d&#039;un utilisateur ==&lt;br /&gt;
On crée les clefs de cryptage pour le nouvel utilisateur dans le repertoir &amp;quot;tmp&amp;quot; :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt; par le nom souhaité.&lt;br /&gt;
 }}&lt;br /&gt;
 # cd /tmp/&lt;br /&gt;
 # umask 077&lt;br /&gt;
 # name=&amp;quot;&amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 # wg genkey | tee &amp;quot;${name}.key&amp;quot; | wg pubkey &amp;gt; &amp;quot;${name}.pub&amp;quot;&lt;br /&gt;
 # wg genpsk &amp;gt; &amp;quot;${name}.psk&amp;quot;&lt;br /&gt;
On peuple le fichier de configuration de WireGuard avec le nouvel utilisateur :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt; par une adresse VPN libre.&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PublicKey = $(cat &amp;quot;${name}.pub&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
Ensuite on peuple le fichier de configuration utilisateur, il faut d&#039;abord récupérer la clef pubique du serveur (ici en bleu) :&lt;br /&gt;
 # wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;interface&amp;lt;/font&amp;gt;: &amp;lt;font color = darkgreen&amp;gt;wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt; adresse VPN libre sélectionner dans le fichier utilisateur ci-dessus / &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt; l&#039;adresse du serveur cible à atteindre (différentes configuration possible [[#Configuration_de_WireGuard_Server|voir la configuration serveur)]]&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;[Interface]&amp;quot; &amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Address = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;ListenPort = &amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PrivateKey = $(cat &amp;quot;${name}.key&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PublicKey = &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Endpoint = &amp;lt;font color = blue&amp;gt;monserveurvpn.exemple.net&amp;lt;/font&amp;gt;:&amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
Enfin on redémarre le service pour appliquer les changement :&lt;br /&gt;
 # service wireguard restart&lt;br /&gt;
= wireguard-ui =&lt;br /&gt;
[https://github.com/ngoduykhanh/wireguard-ui Source github]&lt;br /&gt;
== Installation ==&lt;br /&gt;
On récupère le programme :&lt;br /&gt;
 # mkdir -p /opt/wgui&lt;br /&gt;
 # cd /opt/wgui&lt;br /&gt;
 # &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/ngoduykhanh/wireguard-ui/releases/download/v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;/wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # tar -xzf wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # rm wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
On peut tester le fonctionnement du serveur en lançant le bin :&lt;br /&gt;
 # /opt/wgui/wireguard-ui&lt;br /&gt;
Le serveur web sera accessible sur le port 5000, avec l&#039;utilisateur &amp;quot;admin&amp;quot; et mot de passe &amp;quot;admin&amp;quot; par défault (penser à changer le mot de passe immédiatement).&lt;br /&gt;
== Création des scripts RC ==&lt;br /&gt;
=== (Optionnel) Script pour surveillé wg0.conf ===&lt;br /&gt;
On crée le script exécutable :&lt;br /&gt;
 # vi /usr/local/bin/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/bin/sh&lt;br /&gt;
 wg-quick down wg0&lt;br /&gt;
 wg-quick up wg0&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /usr/local/bin/wgui&lt;br /&gt;
Puis le script RC pour l&#039;utiliser :&lt;br /&gt;
 # vi /etc/init.d/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 command=/sbin/inotifyd&lt;br /&gt;
 command_args=&amp;quot;/usr/local/bin/wgui /etc/wireguard/wg0.conf:w&amp;quot;&lt;br /&gt;
 pidfile=/run/${RC_SVCNAME}.pid&lt;br /&gt;
 command_background=yes&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wgui&lt;br /&gt;
Enfin on inscrit le script RC au système :&lt;br /&gt;
 # rc-service wgui start&lt;br /&gt;
 # rc-update add wgui default&lt;br /&gt;
=== Script RC pour lancer le GUI automatiquement ===&lt;br /&gt;
 # vi /etc/init.d/wireguard-ui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 name=&amp;quot;wireguard-ui&amp;quot;&lt;br /&gt;
 directory=&amp;quot;/opt/wgui&amp;quot;&lt;br /&gt;
 command=&amp;quot;/opt/wgui/wireguard-ui&amp;quot;&lt;br /&gt;
 command_background=&amp;quot;yes&amp;quot;&lt;br /&gt;
 pidfile=&amp;quot;/run/${RC_SVCNAME}.pid&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
     need net&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wireguard-ui&lt;br /&gt;
 # rc-update add wireguard-ui default&lt;br /&gt;
 # rc-service wireguard-ui start&lt;br /&gt;
 # reboot&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3267</id>
		<title>WireGuard LXC Alpine Linux</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3267"/>
		<updated>2026-04-04T11:33:17Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* wireguard-ui */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Barre de progression|100|largeur=400px|hauteur=10|couleur1 = green|texte=Testé et approuvé}}&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter qu&#039;il existe une version Docker tout en un (VPN + GUI de gestion) beaucoup plus simple à mettre en place, mais avec surcouche réseau d&#039;un conteneur, nommé [https://github.com/wg-easy/wg-easy wg-easy] (non testé).&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
= LXC Alpine 3.12 =&lt;br /&gt;
== Installation de WireGuard Server ==&lt;br /&gt;
Avec un téléphone android connecté !!! :&lt;br /&gt;
&lt;br /&gt;
[[File:Capture_ressource_WG_AlpineLXC.PNG|Incroyablement économe !]]&lt;br /&gt;
&lt;br /&gt;
On autorise le routage :&lt;br /&gt;
 lxcalpine:~# echo &#039;net.ipv4.ip_forward=1&#039; &amp;gt;&amp;gt; /etc/sysctl.conf&lt;br /&gt;
 lxcalpine:~# sysctl -p&lt;br /&gt;
&lt;br /&gt;
 net.ipv4.ip_forward = 1&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# rc-update add sysctl default&lt;br /&gt;
&lt;br /&gt;
Puis on install :&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 lxcalpine:~# apk add wireguard-tools&lt;br /&gt;
== Configuration de WireGuard Server ==&lt;br /&gt;
On génère la configuration sur [https://www.wireguardconfig.com/ &#039;&#039;&#039;wireguardconfig&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/wg0.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;[Interface]&lt;br /&gt;
Address = 10.0.0.1/24&lt;br /&gt;
ListenPort = 51820&lt;br /&gt;
PrivateKey = *****************************************&lt;br /&gt;
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.2/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.3/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.4/32&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/client1.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 [Interface]&lt;br /&gt;
 Address = 10.0.0.2/24&lt;br /&gt;
 ListenPort = 51820&lt;br /&gt;
 PrivateKey = *****************************************&lt;br /&gt;
 &lt;br /&gt;
 [Peer]&lt;br /&gt;
 PublicKey = *****************************************&lt;br /&gt;
 PresharedKey = *****************************************&lt;br /&gt;
 &amp;lt;font color=blue&amp;gt;AllowedIPs = 0.0.0.0/1, 128.0.0.0/1&amp;lt;/font&amp;gt;&lt;br /&gt;
 Endpoint = myserver.dyndns.org:51820&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Opération à répéter pour chaque client..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; modifié pour ne pas rediriger tout le traffic et perdre l&#039;accès a son réseau local..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; peut aussi être modifié pour ne rediriger que les paquets a destination du réseau distant et conserver Internet localement sans passer par le serveur distant, exemple : &amp;quot;&#039;&#039;&#039;AllowedIPs = 192.168.2.0/24&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Test du service ==&lt;br /&gt;
&amp;lt;code&amp;gt;lxcalpine:~# wg-quick up wg0&amp;lt;/code&amp;gt;&lt;br /&gt;
 [#] ip link add wg0 type wireguard&lt;br /&gt;
 [#] wg setconf wg0 /dev/fd/63&lt;br /&gt;
 [#] ip -4 address add 10.0.0.1/24 dev wg0&lt;br /&gt;
 [#] ip link set mtu 1420 up dev wg0&lt;br /&gt;
 [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;interface: wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: *****************************************&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
   listening port: 51820&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.2/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.3/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.4/32&lt;br /&gt;
&lt;br /&gt;
Fin du test :&lt;br /&gt;
 lxcalpine:~# wg-quick down wg0&lt;br /&gt;
&lt;br /&gt;
 [#] ip link delete dev wg0&lt;br /&gt;
 [#] iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
== Création du script init.d pour lancer le service ==&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/r0v/48e18c9584d4b5ecb96f593dec9a938c &#039;&#039;&#039;source&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# vi /etc/init.d/wireguard&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
    need localmount&lt;br /&gt;
    need net&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 start() {&lt;br /&gt;
    ebegin &amp;quot;Starting wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick up wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 stop() {&lt;br /&gt;
    ebegin &amp;quot;Stopping wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick down wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
On active le script immédiatement et au redémarrage :&lt;br /&gt;
 lxcalpine:~# chmod +x /etc/init.d/wireguard&lt;br /&gt;
 lxcalpine:~# rc-update add wireguard default&lt;br /&gt;
 lxcalpine:~# service wireguard start&lt;br /&gt;
On peut [[#Test_du_service|tester]] avec &amp;quot;wg&amp;quot;, rebooter pour vérifier etc...&lt;br /&gt;
&lt;br /&gt;
== (Optionnel) QR Code ==&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk add libqrencode&lt;br /&gt;
&lt;br /&gt;
Exemple :&lt;br /&gt;
 lxcalpine:~# qrencode -t ansiutf8 &amp;lt; /etc/wireguard/client1.conf&lt;br /&gt;
&lt;br /&gt;
voila!&lt;br /&gt;
== Ajout d&#039;un utilisateur ==&lt;br /&gt;
On crée les clefs de cryptage pour le nouvel utilisateur dans le repertoir &amp;quot;tmp&amp;quot; :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt; par le nom souhaité.&lt;br /&gt;
 }}&lt;br /&gt;
 # cd /tmp/&lt;br /&gt;
 # umask 077&lt;br /&gt;
 # name=&amp;quot;&amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 # wg genkey | tee &amp;quot;${name}.key&amp;quot; | wg pubkey &amp;gt; &amp;quot;${name}.pub&amp;quot;&lt;br /&gt;
 # wg genpsk &amp;gt; &amp;quot;${name}.psk&amp;quot;&lt;br /&gt;
On peuple le fichier de configuration de WireGuard avec le nouvel utilisateur :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt; par une adresse VPN libre.&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PublicKey = $(cat &amp;quot;${name}.pub&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
Ensuite on peuple le fichier de configuration utilisateur, il faut d&#039;abord récupérer la clef pubique du serveur (ici en bleu) :&lt;br /&gt;
 # wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;interface&amp;lt;/font&amp;gt;: &amp;lt;font color = darkgreen&amp;gt;wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt; adresse VPN libre sélectionner dans le fichier utilisateur ci-dessus / &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt; l&#039;adresse du serveur cible à atteindre (différentes configuration possible [[#Configuration_de_WireGuard_Server|voir la configuration serveur)]]&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;[Interface]&amp;quot; &amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Address = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;ListenPort = &amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PrivateKey = $(cat &amp;quot;${name}.key&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PublicKey = &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Endpoint = &amp;lt;font color = blue&amp;gt;monserveurvpn.exemple.net&amp;lt;/font&amp;gt;:&amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
Enfin on redémarre le service pour appliquer les changement :&lt;br /&gt;
 # service wireguard restart&lt;br /&gt;
= wireguard-ui =&lt;br /&gt;
[https://github.com/ngoduykhanh/wireguard-ui Source github]&lt;br /&gt;
== Installation ==&lt;br /&gt;
On récupère le programme :&lt;br /&gt;
 # mkdir -p /opt/wgui&lt;br /&gt;
 # cd /opt/wgui&lt;br /&gt;
 # &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/ngoduykhanh/wireguard-ui/releases/download/v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;/wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # tar -xzf wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
 # rm wireguard-ui-v&amp;lt;font color = greenn&amp;gt;0.6.2&amp;lt;/font&amp;gt;-linux-amd64.tar.gz&lt;br /&gt;
On peut tester le fonctionnement du serveur en lançant le bin :&lt;br /&gt;
 # /opt/wgui/wireguard-ui&lt;br /&gt;
Le serveur web sera accessible sur le port 5000, avec l&#039;utilisateur &amp;quot;admin&amp;quot; et mot de passe &amp;quot;admin&amp;quot; par défault (penser à changer le mot de passe immédiatement).&lt;br /&gt;
== Création des scripts RC ==&lt;br /&gt;
=== (Optionnel) Script pour surveillé wg0.conf ===&lt;br /&gt;
On crée le script exécutable :&lt;br /&gt;
 # vi /usr/local/bin/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/bin/sh&lt;br /&gt;
 wg-quick down wg0&lt;br /&gt;
 wg-quick up wg0&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /usr/local/bin/wgui&lt;br /&gt;
Puis le script RC pour l&#039;utiliser :&lt;br /&gt;
 # vi /etc/init.d/wgui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 command=/sbin/inotifyd&lt;br /&gt;
 command_args=&amp;quot;/usr/local/bin/wgui /etc/wireguard/wg0.conf:w&amp;quot;&lt;br /&gt;
 pidfile=/run/${RC_SVCNAME}.pid&lt;br /&gt;
 command_background=yes&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wgui&lt;br /&gt;
Enfin on inscrit le script RC au système :&lt;br /&gt;
 # rc-service wgui start&lt;br /&gt;
 # rc-update add wgui default&lt;br /&gt;
=== Script RC pour lancer le GUI automatiquement ===&lt;br /&gt;
 # vi /etc/init.d/wireguard-ui&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 name=&amp;quot;wireguard-ui&amp;quot;&lt;br /&gt;
 command=&amp;quot;/opt/wgui/wireguard-ui&amp;quot;&lt;br /&gt;
 command_background=&amp;quot;yes&amp;quot;&lt;br /&gt;
 pidfile=&amp;quot;/run/${RC_SVCNAME}.pid&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
     need net&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /etc/init.d/wireguard-ui&lt;br /&gt;
 # rc-update add wireguard-ui default&lt;br /&gt;
 # rc-service wireguard-ui start&lt;br /&gt;
 # reboot&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3266</id>
		<title>WireGuard LXC Alpine Linux</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=WireGuard_LXC_Alpine_Linux&amp;diff=3266"/>
		<updated>2026-04-04T10:41:35Z</updated>

		<summary type="html">&lt;p&gt;Admin : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Barre de progression|100|largeur=400px|hauteur=10|couleur1 = green|texte=Testé et approuvé}}&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter qu&#039;il existe une version Docker tout en un (VPN + GUI de gestion) beaucoup plus simple à mettre en place, mais avec surcouche réseau d&#039;un conteneur, nommé [https://github.com/wg-easy/wg-easy wg-easy] (non testé).&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
= LXC Alpine 3.12 =&lt;br /&gt;
== Installation de WireGuard Server ==&lt;br /&gt;
Avec un téléphone android connecté !!! :&lt;br /&gt;
&lt;br /&gt;
[[File:Capture_ressource_WG_AlpineLXC.PNG|Incroyablement économe !]]&lt;br /&gt;
&lt;br /&gt;
On autorise le routage :&lt;br /&gt;
 lxcalpine:~# echo &#039;net.ipv4.ip_forward=1&#039; &amp;gt;&amp;gt; /etc/sysctl.conf&lt;br /&gt;
 lxcalpine:~# sysctl -p&lt;br /&gt;
&lt;br /&gt;
 net.ipv4.ip_forward = 1&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# rc-update add sysctl default&lt;br /&gt;
&lt;br /&gt;
Puis on install :&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 lxcalpine:~# apk add wireguard-tools&lt;br /&gt;
== Configuration de WireGuard Server ==&lt;br /&gt;
On génère la configuration sur [https://www.wireguardconfig.com/ &#039;&#039;&#039;wireguardconfig&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/wg0.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;[Interface]&lt;br /&gt;
Address = 10.0.0.1/24&lt;br /&gt;
ListenPort = 51820&lt;br /&gt;
PrivateKey = *****************************************&lt;br /&gt;
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.2/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.3/32&lt;br /&gt;
&lt;br /&gt;
[Peer]&lt;br /&gt;
PublicKey = *****************************************&lt;br /&gt;
PresharedKey = *****************************************&lt;br /&gt;
AllowedIPs = 10.0.0.4/32&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# vi /etc/wireguard/client1.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 [Interface]&lt;br /&gt;
 Address = 10.0.0.2/24&lt;br /&gt;
 ListenPort = 51820&lt;br /&gt;
 PrivateKey = *****************************************&lt;br /&gt;
 &lt;br /&gt;
 [Peer]&lt;br /&gt;
 PublicKey = *****************************************&lt;br /&gt;
 PresharedKey = *****************************************&lt;br /&gt;
 &amp;lt;font color=blue&amp;gt;AllowedIPs = 0.0.0.0/1, 128.0.0.0/1&amp;lt;/font&amp;gt;&lt;br /&gt;
 Endpoint = myserver.dyndns.org:51820&lt;br /&gt;
&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Opération à répéter pour chaque client..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; modifié pour ne pas rediriger tout le traffic et perdre l&#039;accès a son réseau local..&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color=blue&amp;gt;Allowed IP&amp;lt;/font&amp;gt; peut aussi être modifié pour ne rediriger que les paquets a destination du réseau distant et conserver Internet localement sans passer par le serveur distant, exemple : &amp;quot;&#039;&#039;&#039;AllowedIPs = 192.168.2.0/24&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Test du service ==&lt;br /&gt;
&amp;lt;code&amp;gt;lxcalpine:~# wg-quick up wg0&amp;lt;/code&amp;gt;&lt;br /&gt;
 [#] ip link add wg0 type wireguard&lt;br /&gt;
 [#] wg setconf wg0 /dev/fd/63&lt;br /&gt;
 [#] ip -4 address add 10.0.0.1/24 dev wg0&lt;br /&gt;
 [#] ip link set mtu 1420 up dev wg0&lt;br /&gt;
 [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;interface: wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: *****************************************&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
   listening port: 51820&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.2/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.3/32&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;font color=gold&amp;gt;peer: *****************************************&amp;lt;/font&amp;gt;&lt;br /&gt;
   preshared key: (hidden)&lt;br /&gt;
   allowed ips: 10.0.0.4/32&lt;br /&gt;
&lt;br /&gt;
Fin du test :&lt;br /&gt;
 lxcalpine:~# wg-quick down wg0&lt;br /&gt;
&lt;br /&gt;
 [#] ip link delete dev wg0&lt;br /&gt;
 [#] iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE&lt;br /&gt;
&lt;br /&gt;
== Création du script init.d pour lancer le service ==&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/r0v/48e18c9584d4b5ecb96f593dec9a938c &#039;&#039;&#039;source&#039;&#039;&#039;]&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# vi /etc/init.d/wireguard&lt;br /&gt;
&lt;br /&gt;
 #!/sbin/openrc-run&lt;br /&gt;
 &lt;br /&gt;
 depend() {&lt;br /&gt;
    need localmount&lt;br /&gt;
    need net&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 start() {&lt;br /&gt;
    ebegin &amp;quot;Starting wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick up wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 stop() {&lt;br /&gt;
    ebegin &amp;quot;Stopping wireguard&amp;quot;&lt;br /&gt;
    /usr/bin/wg-quick down wg0&lt;br /&gt;
    eend $?&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
On active le script immédiatement et au redémarrage :&lt;br /&gt;
 lxcalpine:~# chmod +x /etc/init.d/wireguard&lt;br /&gt;
 lxcalpine:~# rc-update add wireguard default&lt;br /&gt;
 lxcalpine:~# service wireguard start&lt;br /&gt;
On peut [[#Test_du_service|tester]] avec &amp;quot;wg&amp;quot;, rebooter pour vérifier etc...&lt;br /&gt;
&lt;br /&gt;
== (Optionnel) QR Code ==&lt;br /&gt;
&lt;br /&gt;
 lxcalpine:~# apk add libqrencode&lt;br /&gt;
&lt;br /&gt;
Exemple :&lt;br /&gt;
 lxcalpine:~# qrencode -t ansiutf8 &amp;lt; /etc/wireguard/client1.conf&lt;br /&gt;
&lt;br /&gt;
voila!&lt;br /&gt;
== Ajout d&#039;un utilisateur ==&lt;br /&gt;
On crée les clefs de cryptage pour le nouvel utilisateur dans le repertoir &amp;quot;tmp&amp;quot; :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt; par le nom souhaité.&lt;br /&gt;
 }}&lt;br /&gt;
 # cd /tmp/&lt;br /&gt;
 # umask 077&lt;br /&gt;
 # name=&amp;quot;&amp;lt;font color = blue&amp;gt;nom_utilisateur&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
 # wg genkey | tee &amp;quot;${name}.key&amp;quot; | wg pubkey &amp;gt; &amp;quot;${name}.pub&amp;quot;&lt;br /&gt;
 # wg genpsk &amp;gt; &amp;quot;${name}.psk&amp;quot;&lt;br /&gt;
On peuple le fichier de configuration de WireGuard avec le nouvel utilisateur :&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Remplacer &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt; par une adresse VPN libre.&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PublicKey = $(cat &amp;quot;${name}.pub&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;10.0.0.5/32&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; /etc/wireguard/wg0.conf&lt;br /&gt;
Ensuite on peuple le fichier de configuration utilisateur, il faut d&#039;abord récupérer la clef pubique du serveur (ici en bleu) :&lt;br /&gt;
 # wg&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = green&amp;gt;interface&amp;lt;/font&amp;gt;: &amp;lt;font color = darkgreen&amp;gt;wg0&amp;lt;/font&amp;gt;&lt;br /&gt;
   public key: &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&lt;br /&gt;
   private key: (hidden)&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt; adresse VPN libre sélectionner dans le fichier utilisateur ci-dessus / &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt; l&#039;adresse du serveur cible à atteindre (différentes configuration possible [[#Configuration_de_WireGuard_Server|voir la configuration serveur)]]&lt;br /&gt;
 }}&lt;br /&gt;
 # echo &amp;quot;[Interface]&amp;quot; &amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Address = &amp;lt;font color = blue&amp;gt;10.0.0.5/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;ListenPort = &amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PrivateKey = $(cat &amp;quot;${name}.key&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;[Peer]&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PublicKey = &amp;lt;font color = blue&amp;gt;MaClEfPubLiQUe=&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;PresharedKey = $(cat &amp;quot;${name}.psk&amp;quot;)&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;AllowedIPs = &amp;lt;font color = blue&amp;gt;192.168.1.0/24&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
 # echo &amp;quot;Endpoint = &amp;lt;font color = blue&amp;gt;monserveurvpn.exemple.net&amp;lt;/font&amp;gt;:&amp;lt;font color = green&amp;gt;51820&amp;lt;/font&amp;gt;&amp;quot; &amp;gt;&amp;gt; &amp;quot;/etc/wireguard/${name}.conf&amp;quot;&lt;br /&gt;
Enfin on redémarre le service pour appliquer les changement :&lt;br /&gt;
 # service wireguard restart&lt;br /&gt;
= wireguard-ui =&lt;br /&gt;
[https://github.com/ngoduykhanh/wireguard-ui Source github]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3265</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3265"/>
		<updated>2026-04-04T10:40:26Z</updated>

		<summary type="html">&lt;p&gt;Admin : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;/mnt/Media/Media/Emulation/roms&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = True&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: &#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;3do&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/opera_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def normalize_relpath(path_str: str) -&amp;gt; str:&lt;br /&gt;
     path_str = clean(path_str)&lt;br /&gt;
     if path_str.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
         path_str = path_str[2:]&lt;br /&gt;
     return path_str&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def read_m3u_entries(m3u_path: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Lit un .m3u et retourne un set de chemins relatifs normalisés,&lt;br /&gt;
     tels qu&#039;ils apparaissent dans le dossier de la rom.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     entries = set()&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         with m3u_path.open(&amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;, errors=&amp;quot;ignore&amp;quot;) as f:&lt;br /&gt;
             for line in f:&lt;br /&gt;
                 line = line.strip()&lt;br /&gt;
                  if not line or line.startswith(&amp;quot;#&amp;quot;):&lt;br /&gt;
                     continue&lt;br /&gt;
 &lt;br /&gt;
                 rel = normalize_relpath(line)&lt;br /&gt;
                 entries.add(rel)&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[WARN] Failed to read m3u {m3u_path}: {e}&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     return entries&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def build_m3u_referenced_files(games, romdir: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Retourne l&#039;ensemble des fichiers référencés par les .m3u présents&lt;br /&gt;
     dans ce gamelist.xml.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     referenced = set()&lt;br /&gt;
 &lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if not path:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         rel = normalize_relpath(path)&lt;br /&gt;
         p = romdir / rel&lt;br /&gt;
 &lt;br /&gt;
         if p.suffix.lower() == &amp;quot;.m3u&amp;quot; and p.is_file():&lt;br /&gt;
             referenced.update(read_m3u_entries(p))&lt;br /&gt;
 &lt;br /&gt;
     return referenced&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     # Tous les fichiers référencés par des .m3u présents dans ce dossier&lt;br /&gt;
     m3u_referenced_files = build_m3u_referenced_files(games, romdir)&lt;br /&gt;
 &lt;br /&gt;
     written_count = 0&lt;br /&gt;
     skipped_count = 0&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             rel_path = normalize_relpath(path)&lt;br /&gt;
             rel_path_posix = Path(rel_path).as_posix()&lt;br /&gt;
 &lt;br /&gt;
             # On skip si ce fichier est référencé par un .m3u&lt;br /&gt;
             # MAIS on ne skip pas le .m3u lui-même&lt;br /&gt;
             if rel_path_posix in m3u_referenced_files and Path(rel_path_posix).suffix.lower() != &amp;quot;.m3u&amp;quot;:&lt;br /&gt;
                 skipped_count += 1&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(rel_path_posix).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {rel_path_posix}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
             written_count += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({written_count} games, {skipped_count} skipped via m3u)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Variante pour ignorer parenthèses après &amp;quot;(Disc 1)&amp;quot; etc :&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]:-}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3264</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3264"/>
		<updated>2026-04-04T10:27:14Z</updated>

		<summary type="html">&lt;p&gt;Admin : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter qu&#039;il existe une version Docker tout en un (VPN + GUI de gestion) beaucoup plus simple à mettre en place, mais avec surcouche réseau d&#039;un conteneur, nommé [https://github.com/wg-easy/wg-easy wg-easy] (non testé).&lt;br /&gt;
 }}&lt;br /&gt;
= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;/mnt/Media/Media/Emulation/roms&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = True&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: &#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;3do&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/opera_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def normalize_relpath(path_str: str) -&amp;gt; str:&lt;br /&gt;
     path_str = clean(path_str)&lt;br /&gt;
     if path_str.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
         path_str = path_str[2:]&lt;br /&gt;
     return path_str&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def read_m3u_entries(m3u_path: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Lit un .m3u et retourne un set de chemins relatifs normalisés,&lt;br /&gt;
     tels qu&#039;ils apparaissent dans le dossier de la rom.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     entries = set()&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         with m3u_path.open(&amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;, errors=&amp;quot;ignore&amp;quot;) as f:&lt;br /&gt;
             for line in f:&lt;br /&gt;
                 line = line.strip()&lt;br /&gt;
                  if not line or line.startswith(&amp;quot;#&amp;quot;):&lt;br /&gt;
                     continue&lt;br /&gt;
 &lt;br /&gt;
                 rel = normalize_relpath(line)&lt;br /&gt;
                 entries.add(rel)&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[WARN] Failed to read m3u {m3u_path}: {e}&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     return entries&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def build_m3u_referenced_files(games, romdir: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Retourne l&#039;ensemble des fichiers référencés par les .m3u présents&lt;br /&gt;
     dans ce gamelist.xml.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     referenced = set()&lt;br /&gt;
 &lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if not path:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         rel = normalize_relpath(path)&lt;br /&gt;
         p = romdir / rel&lt;br /&gt;
 &lt;br /&gt;
         if p.suffix.lower() == &amp;quot;.m3u&amp;quot; and p.is_file():&lt;br /&gt;
             referenced.update(read_m3u_entries(p))&lt;br /&gt;
 &lt;br /&gt;
     return referenced&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     # Tous les fichiers référencés par des .m3u présents dans ce dossier&lt;br /&gt;
     m3u_referenced_files = build_m3u_referenced_files(games, romdir)&lt;br /&gt;
 &lt;br /&gt;
     written_count = 0&lt;br /&gt;
     skipped_count = 0&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             rel_path = normalize_relpath(path)&lt;br /&gt;
             rel_path_posix = Path(rel_path).as_posix()&lt;br /&gt;
 &lt;br /&gt;
             # On skip si ce fichier est référencé par un .m3u&lt;br /&gt;
             # MAIS on ne skip pas le .m3u lui-même&lt;br /&gt;
             if rel_path_posix in m3u_referenced_files and Path(rel_path_posix).suffix.lower() != &amp;quot;.m3u&amp;quot;:&lt;br /&gt;
                 skipped_count += 1&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(rel_path_posix).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {rel_path_posix}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
             written_count += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({written_count} games, {skipped_count} skipped via m3u)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Variante pour ignorer parenthèses après &amp;quot;(Disc 1)&amp;quot; etc :&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]:-}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3263</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3263"/>
		<updated>2026-04-01T15:57:30Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;/mnt/Media/Media/Emulation/roms&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = True&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: &#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;3do&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/opera_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def normalize_relpath(path_str: str) -&amp;gt; str:&lt;br /&gt;
     path_str = clean(path_str)&lt;br /&gt;
     if path_str.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
         path_str = path_str[2:]&lt;br /&gt;
     return path_str&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def read_m3u_entries(m3u_path: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Lit un .m3u et retourne un set de chemins relatifs normalisés,&lt;br /&gt;
     tels qu&#039;ils apparaissent dans le dossier de la rom.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     entries = set()&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         with m3u_path.open(&amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;, errors=&amp;quot;ignore&amp;quot;) as f:&lt;br /&gt;
             for line in f:&lt;br /&gt;
                 line = line.strip()&lt;br /&gt;
                  if not line or line.startswith(&amp;quot;#&amp;quot;):&lt;br /&gt;
                     continue&lt;br /&gt;
 &lt;br /&gt;
                 rel = normalize_relpath(line)&lt;br /&gt;
                 entries.add(rel)&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[WARN] Failed to read m3u {m3u_path}: {e}&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     return entries&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def build_m3u_referenced_files(games, romdir: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Retourne l&#039;ensemble des fichiers référencés par les .m3u présents&lt;br /&gt;
     dans ce gamelist.xml.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     referenced = set()&lt;br /&gt;
 &lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if not path:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         rel = normalize_relpath(path)&lt;br /&gt;
         p = romdir / rel&lt;br /&gt;
 &lt;br /&gt;
         if p.suffix.lower() == &amp;quot;.m3u&amp;quot; and p.is_file():&lt;br /&gt;
             referenced.update(read_m3u_entries(p))&lt;br /&gt;
 &lt;br /&gt;
     return referenced&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     # Tous les fichiers référencés par des .m3u présents dans ce dossier&lt;br /&gt;
     m3u_referenced_files = build_m3u_referenced_files(games, romdir)&lt;br /&gt;
 &lt;br /&gt;
     written_count = 0&lt;br /&gt;
     skipped_count = 0&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             rel_path = normalize_relpath(path)&lt;br /&gt;
             rel_path_posix = Path(rel_path).as_posix()&lt;br /&gt;
 &lt;br /&gt;
             # On skip si ce fichier est référencé par un .m3u&lt;br /&gt;
             # MAIS on ne skip pas le .m3u lui-même&lt;br /&gt;
             if rel_path_posix in m3u_referenced_files and Path(rel_path_posix).suffix.lower() != &amp;quot;.m3u&amp;quot;:&lt;br /&gt;
                 skipped_count += 1&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(rel_path_posix).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {rel_path_posix}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
             written_count += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({written_count} games, {skipped_count} skipped via m3u)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Variante pour ignorer parenthèses après &amp;quot;(Disc 1)&amp;quot; etc :&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]:-}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3262</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3262"/>
		<updated>2026-03-27T18:00:12Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* convertir les gamelist.xml en metadata.txt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;/mnt/Media/Media/Emulation/roms&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = True&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: &#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;3do&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/opera_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def normalize_relpath(path_str: str) -&amp;gt; str:&lt;br /&gt;
     path_str = clean(path_str)&lt;br /&gt;
     if path_str.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
         path_str = path_str[2:]&lt;br /&gt;
     return path_str&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def read_m3u_entries(m3u_path: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Lit un .m3u et retourne un set de chemins relatifs normalisés,&lt;br /&gt;
     tels qu&#039;ils apparaissent dans le dossier de la rom.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     entries = set()&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         with m3u_path.open(&amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;, errors=&amp;quot;ignore&amp;quot;) as f:&lt;br /&gt;
             for line in f:&lt;br /&gt;
                 line = line.strip()&lt;br /&gt;
                  if not line or line.startswith(&amp;quot;#&amp;quot;):&lt;br /&gt;
                     continue&lt;br /&gt;
 &lt;br /&gt;
                 rel = normalize_relpath(line)&lt;br /&gt;
                 entries.add(rel)&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[WARN] Failed to read m3u {m3u_path}: {e}&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     return entries&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def build_m3u_referenced_files(games, romdir: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Retourne l&#039;ensemble des fichiers référencés par les .m3u présents&lt;br /&gt;
     dans ce gamelist.xml.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     referenced = set()&lt;br /&gt;
 &lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if not path:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         rel = normalize_relpath(path)&lt;br /&gt;
         p = romdir / rel&lt;br /&gt;
 &lt;br /&gt;
         if p.suffix.lower() == &amp;quot;.m3u&amp;quot; and p.is_file():&lt;br /&gt;
             referenced.update(read_m3u_entries(p))&lt;br /&gt;
 &lt;br /&gt;
     return referenced&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     # Tous les fichiers référencés par des .m3u présents dans ce dossier&lt;br /&gt;
     m3u_referenced_files = build_m3u_referenced_files(games, romdir)&lt;br /&gt;
 &lt;br /&gt;
     written_count = 0&lt;br /&gt;
     skipped_count = 0&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             rel_path = normalize_relpath(path)&lt;br /&gt;
             rel_path_posix = Path(rel_path).as_posix()&lt;br /&gt;
 &lt;br /&gt;
             # On skip si ce fichier est référencé par un .m3u&lt;br /&gt;
             # MAIS on ne skip pas le .m3u lui-même&lt;br /&gt;
             if rel_path_posix in m3u_referenced_files and Path(rel_path_posix).suffix.lower() != &amp;quot;.m3u&amp;quot;:&lt;br /&gt;
                 skipped_count += 1&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(rel_path_posix).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {rel_path_posix}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
             written_count += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({written_count} games, {skipped_count} skipped via m3u)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Variante pour ignorer parenthèses après &amp;quot;(Disc 1)&amp;quot; etc :&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]:-}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3261</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3261"/>
		<updated>2026-03-27T10:57:53Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* convertir les gamelist.xml en metadata.txt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;/mnt/Media/Media/Emulation/roms&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = True&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: &#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: &#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: &#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{file.path}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def normalize_relpath(path_str: str) -&amp;gt; str:&lt;br /&gt;
     path_str = clean(path_str)&lt;br /&gt;
     if path_str.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
         path_str = path_str[2:]&lt;br /&gt;
     return path_str&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def read_m3u_entries(m3u_path: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Lit un .m3u et retourne un set de chemins relatifs normalisés,&lt;br /&gt;
     tels qu&#039;ils apparaissent dans le dossier de la rom.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     entries = set()&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         with m3u_path.open(&amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;, errors=&amp;quot;ignore&amp;quot;) as f:&lt;br /&gt;
             for line in f:&lt;br /&gt;
                 line = line.strip()&lt;br /&gt;
                  if not line or line.startswith(&amp;quot;#&amp;quot;):&lt;br /&gt;
                     continue&lt;br /&gt;
 &lt;br /&gt;
                 rel = normalize_relpath(line)&lt;br /&gt;
                 entries.add(rel)&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[WARN] Failed to read m3u {m3u_path}: {e}&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     return entries&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def build_m3u_referenced_files(games, romdir: Path):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     Retourne l&#039;ensemble des fichiers référencés par les .m3u présents&lt;br /&gt;
     dans ce gamelist.xml.&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     referenced = set()&lt;br /&gt;
 &lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if not path:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         rel = normalize_relpath(path)&lt;br /&gt;
         p = romdir / rel&lt;br /&gt;
 &lt;br /&gt;
         if p.suffix.lower() == &amp;quot;.m3u&amp;quot; and p.is_file():&lt;br /&gt;
             referenced.update(read_m3u_entries(p))&lt;br /&gt;
 &lt;br /&gt;
     return referenced&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     # Tous les fichiers référencés par des .m3u présents dans ce dossier&lt;br /&gt;
     m3u_referenced_files = build_m3u_referenced_files(games, romdir)&lt;br /&gt;
 &lt;br /&gt;
     written_count = 0&lt;br /&gt;
     skipped_count = 0&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             rel_path = normalize_relpath(path)&lt;br /&gt;
             rel_path_posix = Path(rel_path).as_posix()&lt;br /&gt;
 &lt;br /&gt;
             # On skip si ce fichier est référencé par un .m3u&lt;br /&gt;
             # MAIS on ne skip pas le .m3u lui-même&lt;br /&gt;
             if rel_path_posix in m3u_referenced_files and Path(rel_path_posix).suffix.lower() != &amp;quot;.m3u&amp;quot;:&lt;br /&gt;
                 skipped_count += 1&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(rel_path_posix).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {rel_path_posix}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
             written_count += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({written_count} games, {skipped_count} skipped via m3u)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Variante pour ignorer parenthèses après &amp;quot;(Disc 1)&amp;quot; etc :&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]:-}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3260</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3260"/>
		<updated>2026-03-24T17:52:52Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Fichiers .m3u */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Variante pour ignorer parenthèses après &amp;quot;(Disc 1)&amp;quot; etc :&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
          &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&lt;br /&gt;
         s/(disc|disk|cd)[[:space:]]*[0-9]+([[:space:]]*\([^)]*\))*$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]:-}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3259</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3259"/>
		<updated>2026-03-23T14:52:01Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* convertir les gamelist.xml en metadata.txt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: (&amp;quot;Sega Naomi&amp;quot;, &amp;quot;naomi&amp;quot;),&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: (&amp;quot;Sega Naomi 2&amp;quot;, &amp;quot;naomi2&amp;quot;),&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: (&amp;quot;Sega Triforce&amp;quot;, &amp;quot;triforce&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;naomi2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;triforce&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3258</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3258"/>
		<updated>2026-03-23T00:21:47Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Le changement ne sera pas persistant, permet de faire un nettoyage important lors de manipulation de grosse base de donénes de romes..&lt;br /&gt;
 }}&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3257</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3257"/>
		<updated>2026-03-23T00:18:18Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* vhost nginx */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= Augmenter le timeout du Nginx du conteneur pour grosse BDD de roms =&lt;br /&gt;
On rentre dans le conteneur :&lt;br /&gt;
 # docker exec -it romm sh&lt;br /&gt;
(Optionnel) On sauvegarde la configuration d&#039;origine :&lt;br /&gt;
 # cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak&lt;br /&gt;
On édite la configuration :&lt;br /&gt;
 # vi /etc/nginx/conf.d/default.conf&lt;br /&gt;
Et on ajoute (en bleu) :&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
     # OpenAPI for swagger and redoc&lt;br /&gt;
     location /openapi.json {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     # Backend api calls&lt;br /&gt;
     location /api {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_request_buffering off;&lt;br /&gt;
         proxy_buffering off;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location ~ ^/(ws|netplay) {&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;wsgi_server;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         &amp;lt;font color = blue&amp;gt;proxy_connect_timeout 3600s;&lt;br /&gt;
         proxy_send_timeout 3600s;&lt;br /&gt;
         proxy_read_timeout 3600s;&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 ...&amp;lt;/font&amp;gt;&lt;br /&gt;
Puis on redémarre le service :&lt;br /&gt;
 # nginx -s reload&lt;br /&gt;
 # exit&lt;br /&gt;
&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3256</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3256"/>
		<updated>2026-03-23T00:06:43Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
       - TASK_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
       - WEB_SERVER_TIMEOUT=3600 # plus gros timeout&lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3255</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3255"/>
		<updated>2026-03-22T23:56:51Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Prérequis */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque (Dépend du nombres de roms, peut monter très vite..)&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
 &lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3254</id>
		<title>Moonlight web stream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3254"/>
		<updated>2026-03-22T16:34:58Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Paramètres forcés */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://github.com/MrCreativ3001/moonlight-web-stream Source github]&lt;br /&gt;
= Installation version Docker =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
= Configuration =&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
== Auhtentification ==&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
== Paramètres forcés ==&lt;br /&gt;
Exemple avec une configuration retrogaming en 720p :&lt;br /&gt;
&lt;br /&gt;
remplacer à la fin :&lt;br /&gt;
  &amp;quot;default_settings&amp;quot;: null&lt;br /&gt;
Par :&lt;br /&gt;
&lt;br /&gt;
     &amp;quot;default_settings&amp;quot;: {&lt;br /&gt;
         // possible values: &amp;quot;left&amp;quot;, &amp;quot;right&amp;quot;, &amp;quot;up&amp;quot;, &amp;quot;down&amp;quot;&lt;br /&gt;
         &amp;quot;sidebarEdge&amp;quot;: &amp;quot;left&amp;quot;,&lt;br /&gt;
         &amp;quot;bitrate&amp;quot;: 10000,&lt;br /&gt;
         &amp;quot;packetSize&amp;quot;: 2048,&lt;br /&gt;
         &amp;quot;fps&amp;quot;: 60,&lt;br /&gt;
         &amp;quot;videoFrameQueueSize&amp;quot;: 3,&lt;br /&gt;
         // possible values: &amp;quot;720p&amp;quot;, &amp;quot;1080p&amp;quot;, &amp;quot;1440p&amp;quot;, &amp;quot;4k&amp;quot;, &amp;quot;native&amp;quot;, &amp;quot;custom&amp;quot;&lt;br /&gt;
         &amp;quot;videoSize&amp;quot;: &amp;quot;720p&amp;quot;,&lt;br /&gt;
         // only works if videoSize=custom&lt;br /&gt;
         //&amp;quot;videoSizeCustom&amp;quot;: {&lt;br /&gt;
         //    &amp;quot;width&amp;quot;: 1920,&lt;br /&gt;
         //    &amp;quot;height&amp;quot;: 1080&lt;br /&gt;
         //},&lt;br /&gt;
         // possible values: &amp;quot;h264&amp;quot;, &amp;quot;h265&amp;quot;, &amp;quot;av1&amp;quot;, &amp;quot;auto&amp;quot;&lt;br /&gt;
         &amp;quot;videoCodec&amp;quot;: &amp;quot;h265&amp;quot;,&lt;br /&gt;
         &amp;quot;forceVideoElementRenderer&amp;quot;: false,&lt;br /&gt;
         &amp;quot;canvasRenderer&amp;quot;: false,&lt;br /&gt;
         &amp;quot;playAudioLocal&amp;quot;: true,&lt;br /&gt;
         &amp;quot;audioSampleQueueSize&amp;quot;: 20,&lt;br /&gt;
         // possible values: &amp;quot;highres&amp;quot;, &amp;quot;normal&amp;quot;&lt;br /&gt;
         &amp;quot;mouseScrollMode&amp;quot;: &amp;quot;highres&amp;quot;,&lt;br /&gt;
         &amp;quot;controllerConfig&amp;quot;: {&lt;br /&gt;
             &amp;quot;invertAB&amp;quot;: false,&lt;br /&gt;
             &amp;quot;invertXY&amp;quot;: false,&lt;br /&gt;
             // possible values: null or a number, example: 60, 120&lt;br /&gt;
             &amp;quot;sendIntervalOverride&amp;quot;: null&lt;br /&gt;
         },&lt;br /&gt;
         // possible values: &amp;quot;auto&amp;quot;, &amp;quot;webrtc&amp;quot;, &amp;quot;websocket&amp;quot;&lt;br /&gt;
         &amp;quot;dataTransport&amp;quot;: &amp;quot;auto&amp;quot;,&lt;br /&gt;
         &amp;quot;toggleFullscreenWithKeybind&amp;quot;: false,&lt;br /&gt;
         // possible values: &amp;quot;standard&amp;quot;, &amp;quot;old&amp;quot;&lt;br /&gt;
         &amp;quot;pageStyle&amp;quot;: &amp;quot;standard&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
= vhost =&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3253</id>
		<title>Moonlight web stream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3253"/>
		<updated>2026-03-22T16:30:11Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://github.com/MrCreativ3001/moonlight-web-stream Source github]&lt;br /&gt;
= Installation version Docker =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
= Configuration =&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
== Auhtentification ==&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
== Paramètres forcés ==&lt;br /&gt;
Exemple avec une configuration retrogaming en 720p :&lt;br /&gt;
&lt;br /&gt;
remplacer à la fin :&lt;br /&gt;
  &amp;quot;default_settings&amp;quot;: null&lt;br /&gt;
Par :&lt;br /&gt;
&lt;br /&gt;
     &amp;quot;default_settings&amp;quot;: {&lt;br /&gt;
         // possible values: &amp;quot;left&amp;quot;, &amp;quot;right&amp;quot;, &amp;quot;up&amp;quot;, &amp;quot;down&amp;quot;&lt;br /&gt;
         &amp;quot;sidebarEdge&amp;quot;: &amp;quot;left&amp;quot;,&lt;br /&gt;
         &amp;quot;bitrate&amp;quot;: 10000,&lt;br /&gt;
         &amp;quot;packetSize&amp;quot;: 2048,&lt;br /&gt;
         &amp;quot;fps&amp;quot;: 60,&lt;br /&gt;
         &amp;quot;videoFrameQueueSize&amp;quot;: 3,&lt;br /&gt;
         // possible values: &amp;quot;720p&amp;quot;, &amp;quot;1080p&amp;quot;, &amp;quot;1440p&amp;quot;, &amp;quot;4k&amp;quot;, &amp;quot;native&amp;quot;, &amp;quot;custom&amp;quot;&lt;br /&gt;
         &amp;quot;videoSize&amp;quot;: &amp;quot;720p&amp;quot;,&lt;br /&gt;
         // only works if videoSize=custom&lt;br /&gt;
         //&amp;quot;videoSizeCustom&amp;quot;: {&lt;br /&gt;
         //    &amp;quot;width&amp;quot;: 1920,&lt;br /&gt;
         //    &amp;quot;height&amp;quot;: 1080&lt;br /&gt;
         },&lt;br /&gt;
         // possible values: &amp;quot;h264&amp;quot;, &amp;quot;h265&amp;quot;, &amp;quot;av1&amp;quot;, &amp;quot;auto&amp;quot;&lt;br /&gt;
         &amp;quot;videoCodec&amp;quot;: &amp;quot;h265&amp;quot;,&lt;br /&gt;
         &amp;quot;forceVideoElementRenderer&amp;quot;: false,&lt;br /&gt;
         &amp;quot;canvasRenderer&amp;quot;: false,&lt;br /&gt;
         &amp;quot;playAudioLocal&amp;quot;: true,&lt;br /&gt;
         &amp;quot;audioSampleQueueSize&amp;quot;: 20,&lt;br /&gt;
         // possible values: &amp;quot;highres&amp;quot;, &amp;quot;normal&amp;quot;&lt;br /&gt;
         &amp;quot;mouseScrollMode&amp;quot;: &amp;quot;highres&amp;quot;,&lt;br /&gt;
         &amp;quot;controllerConfig&amp;quot;: {&lt;br /&gt;
             &amp;quot;invertAB&amp;quot;: false,&lt;br /&gt;
             &amp;quot;invertXY&amp;quot;: false,&lt;br /&gt;
             // possible values: null or a number, example: 60, 120&lt;br /&gt;
             &amp;quot;sendIntervalOverride&amp;quot;: null&lt;br /&gt;
         },&lt;br /&gt;
         // possible values: &amp;quot;auto&amp;quot;, &amp;quot;webrtc&amp;quot;, &amp;quot;websocket&amp;quot;&lt;br /&gt;
         &amp;quot;dataTransport&amp;quot;: &amp;quot;auto&amp;quot;,&lt;br /&gt;
         &amp;quot;toggleFullscreenWithKeybind&amp;quot;: false,&lt;br /&gt;
         // possible values: &amp;quot;standard&amp;quot;, &amp;quot;old&amp;quot;&lt;br /&gt;
         &amp;quot;pageStyle&amp;quot;: &amp;quot;standard&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
= vhost =&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3252</id>
		<title>Gowstream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3252"/>
		<updated>2026-03-22T16:14:19Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Moonlight Wev Stream */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= LXC Debian =&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html Source]&lt;br /&gt;
== Prérequis ==&lt;br /&gt;
* LXC avec Privilèges et Nesting&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Virtual devices support ===&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html#_virtual_devices_support Source]&lt;br /&gt;
&lt;br /&gt;
Depuis le serveur :&lt;br /&gt;
 # vi /etc/udev/rules.d/85-wolf-virtual-inputs.rules&lt;br /&gt;
&lt;br /&gt;
 # Allows Wolf to acces /dev/uinput (only needed for joypad support)&lt;br /&gt;
 KERNEL==&amp;quot;uinput&amp;quot;, SUBSYSTEM==&amp;quot;misc&amp;quot;, MODE=&amp;quot;0660&amp;quot;, GROUP=&amp;quot;input&amp;quot;, OPTIONS+=&amp;quot;static_node=uinput&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Allows Wolf to access /dev/uhid (only needed for DualSense emulation)&lt;br /&gt;
 KERNEL==&amp;quot;uhid&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Joypads&lt;br /&gt;
 KERNEL==&amp;quot;hidraw*&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf X-Box One (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf gamepad (virtual) motion sensors&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf Nintendo (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On redémarre le serveur ou l&#039;on charge la configuration avec la commande :&lt;br /&gt;
 # udevadm control --reload-rules &amp;amp;&amp;amp; udevadm trigger&lt;br /&gt;
== NVidia ==&lt;br /&gt;
Après création du conteneur, depuis le serveur :&lt;br /&gt;
 # vi /etc/pve/lxc/&amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt;.conf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 dev0: /dev/uinput&lt;br /&gt;
 dev1: /dev/uhid&lt;br /&gt;
 dev2: /dev/nvidia0&lt;br /&gt;
 dev3: /dev/nvidiactl&lt;br /&gt;
 dev4: /dev/nvidia-modeset&lt;br /&gt;
 dev5: /dev/nvidia-uvm&lt;br /&gt;
 dev6: /dev/nvidia-uvm-tools&lt;br /&gt;
 dev7: /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
 dev8: /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
 lxc.cgroup2.devices.allow: a&lt;br /&gt;
 lxc.cap.drop:&lt;br /&gt;
 lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /run/udev mnt/udev none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /dev mnt/dev none bind,optional,create=dir&lt;br /&gt;
On démarre le LXC, ensuite depuis le LXC :&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
On installe le pilote NVidia ([https://lugwiki.stcgrupo.es/index.php?title=GPU_Passthrough référence]), exemple :&lt;br /&gt;
 # bash /opt/nvidia-driver/NVIDIA-Linux-x86_64-&amp;lt;font color = green&amp;gt;590.48.01&amp;lt;/font&amp;gt;.run --no-kernel-module&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch.sh&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch-fbc.sh&lt;br /&gt;
On installe docker et docker-compose :&lt;br /&gt;
 # apt install docker.io docker-compose-plugin curl&lt;br /&gt;
On install le docker NVidia :&lt;br /&gt;
 # cd /opt/&lt;br /&gt;
 # curl &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile | docker build -t gow/nvidia-driver:latest -f - --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) .&lt;br /&gt;
 # docker create --rm --mount source=nvidia-driver-vol,destination=/usr/nvidia gow/nvidia-driver:latest sh&lt;br /&gt;
On installe le conteneur de GOW :&lt;br /&gt;
 # mkdir /opt/gow&lt;br /&gt;
 # cd /opt/gow/&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3&amp;quot;&lt;br /&gt;
 services:&lt;br /&gt;
   wolf:&lt;br /&gt;
     image: ghcr.io/games-on-whales/wolf:stable&lt;br /&gt;
     environment:&lt;br /&gt;
       - NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol&lt;br /&gt;
       #- WOLF_RENDER_NODE=/dev/dri/renderD12X  ##pour multiGPU&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /etc/wolf/:/etc/wolf:rw&lt;br /&gt;
       - /var/run/docker.sock:/var/run/docker.sock:rw&lt;br /&gt;
       - /mnt/udev:/run/udev:rw&lt;br /&gt;
       - /mnt/dev:/dev:rw&lt;br /&gt;
       - nvidia-driver-vol:/usr/nvidia:rw&lt;br /&gt;
     devices:&lt;br /&gt;
       - /dev/dri&lt;br /&gt;
       - /dev/uinput&lt;br /&gt;
       - /dev/uhid&lt;br /&gt;
       - /dev/nvidia-uvm&lt;br /&gt;
       - /dev/nvidia-uvm-tools&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
       - /dev/nvidiactl&lt;br /&gt;
       - /dev/nvidia0&lt;br /&gt;
       - /dev/nvidia-modeset&lt;br /&gt;
     device_cgroup_rules:&lt;br /&gt;
       - &#039;c 13:* rmw&#039;&lt;br /&gt;
     network_mode: host&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   nvidia-driver-vol:&lt;br /&gt;
     external: true&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= Autoriser client =&lt;br /&gt;
Lors de la première tentative de connexion d&#039;un client il faut rentrer le pin dans un lien web pour l&#039;autoriser, depuis le LXC :&lt;br /&gt;
 # docker ps&lt;br /&gt;
Exemple :&lt;br /&gt;
 CONTAINER ID   IMAGE                                       COMMAND            CREATED          STATUS          PORTS      NAMES&lt;br /&gt;
 c5e43d2fc724   ghcr.io/games-on-whales/pulseaudio:master   &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes   4713/tcp   WolfPulseAudio&lt;br /&gt;
 c581ab5d8827   ghcr.io/games-on-whales/wolf:stable         &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes              &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # docker logs &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt; Insert pin at &amp;lt;font color = blue&amp;gt;&amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;192.168.1.123:47989/pin/#EXEMPLE&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Par défaut :&lt;br /&gt;
 WOLF_CFG_FILE=/etc/wolf/cfg/config.toml&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
== Monter un dossier dans une app ==&lt;br /&gt;
Exemple avec Pegasus : ([https://games-on-whales.github.io/wildlife/apps/pegasus/ source])&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
Modifier :&lt;br /&gt;
&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;[[profiles.apps]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     icon_png_path = &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;games-on-whales.github.io/wildlife/apps/pegasus/assets/icon.png&#039;&lt;br /&gt;
     start_virtual_compositor = true&lt;br /&gt;
     title = &#039;Pegasus&#039;&lt;br /&gt;
 &lt;br /&gt;
         [profiles.apps.runner]&lt;br /&gt;
         base_create_json = &#039;&#039;&#039;{&lt;br /&gt;
   &amp;quot;HostConfig&amp;quot;: {&lt;br /&gt;
     &amp;quot;IpcMode&amp;quot;: &amp;quot;host&amp;quot;,&lt;br /&gt;
     &amp;quot;CapAdd&amp;quot;: [&amp;quot;NET_RAW&amp;quot;, &amp;quot;MKNOD&amp;quot;, &amp;quot;NET_ADMIN&amp;quot;, &amp;quot;SYS_ADMIN&amp;quot;, &amp;quot;SYS_NICE&amp;quot;],&lt;br /&gt;
     &amp;quot;Privileged&amp;quot;: false,&lt;br /&gt;
     &amp;quot;DeviceCgroupRules&amp;quot;: [&amp;quot;c 13:* rmw&amp;quot;, &amp;quot;c 244:* rmw&amp;quot;]&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 &#039;&#039;&#039;&lt;br /&gt;
         devices = []&lt;br /&gt;
         env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
         image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&lt;br /&gt;
         &amp;lt;font color = green&amp;gt;mounts = []&amp;lt;/font&amp;gt;&lt;br /&gt;
         name = &#039;WolfPegasus&#039;&lt;br /&gt;
         ports = []&lt;br /&gt;
         type = &#039;docker&#039;&lt;br /&gt;
Par, exemple :&lt;br /&gt;
&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
          devices = []&lt;br /&gt;
          env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
          image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = blue&amp;gt;mounts = [&lt;br /&gt;
              &amp;quot;/mnt/Emulation/resources:/assets/romm/resources:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# exemple de compatibilite avec assets ROMM&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# pack de bios pour retroarch&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/roms:/ROMs:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;&amp;lt;font color = black&amp;gt;# roms dans le dossier par default, a ajouter dans le menu de Pegasus pour certains dossiers..&amp;lt;/font&amp;gt;&lt;br /&gt;
          ]&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;name = &#039;WolfPegasus&#039;&lt;br /&gt;
          ports = []&lt;br /&gt;
          ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On redémarre le conteneur :&lt;br /&gt;
 # docker restart opt-wolf-1&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter que contrairement à ROMM Retroarch réclame d&#039;avoir tous les bios dans le même dossier.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Montage utile pour Pegasus ==&lt;br /&gt;
Installer des bin d&#039;émulateur :&lt;br /&gt;
 &amp;quot;/opt/emulator:/opt/emulator:rw&amp;quot;,&lt;br /&gt;
Artwork pour Game and Watch (mame) ( exemple avec &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:ro&amp;quot;) :&lt;br /&gt;
 # mkdir -p &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios/retroarchbioses&amp;lt;/font&amp;gt;/mame&lt;br /&gt;
 # ln -s &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms/&amp;lt;/font&amp;gt;g-and-w_artwork &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios/retroarchbioses&amp;lt;/font&amp;gt;/mame/artwork&lt;br /&gt;
&lt;br /&gt;
= Importer un thème dans Pegasus =&lt;br /&gt;
 # cd /etc/wolf/profile-data/&amp;lt;font color = blue&amp;gt;user&amp;lt;/font&amp;gt;/WolfPegasus/.config/pegasus-frontend/themes/ &lt;br /&gt;
Exemple avec &amp;quot;gameOS&amp;quot; (normalement déjà présent..) :&lt;br /&gt;
 # wget &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/PlayingKarrde/gameOS/archive/master.zip&lt;br /&gt;
 # unzip master.zip&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
Voir [[ROMM#convertir_les_gamelist.xml_en_metadata.txt|ce lien]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3251</id>
		<title>Moonlight web stream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3251"/>
		<updated>2026-03-22T16:14:07Z</updated>

		<summary type="html">&lt;p&gt;Admin : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://github.com/MrCreativ3001/moonlight-web-stream Source github]&lt;br /&gt;
= Installation version Docker =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
= Configuration =&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
== Auhtentification ==&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= vhost =&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3250</id>
		<title>Moonlight web stream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Moonlight_web_stream&amp;diff=3250"/>
		<updated>2026-03-22T16:13:38Z</updated>

		<summary type="html">&lt;p&gt;Admin : Page créée avec « [https://github.com/MrCreativ3001/moonlight-web-stream Source] = Installation version Docker = [https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]  # mkdir /opt/moonlightwebstream  # cd /opt/moonlightwebstream  # vi docker-compose.yml    services:    moonlight-web:      image: mrcreativ3001/moonlight-web-stream:latest      container_name: moonlight-web      restart: unless-stopped      ports:        - &amp;quot;&amp;lt;font color = green&amp;gt;8... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://github.com/MrCreativ3001/moonlight-web-stream Source]&lt;br /&gt;
= Installation version Docker =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
= Configuration =&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
== Auhtentification ==&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= vhost =&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Accueil&amp;diff=3249</id>
		<title>Accueil</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Accueil&amp;diff=3249"/>
		<updated>2026-03-22T16:12:38Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Games On Whales */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= ProxMox =&lt;br /&gt;
&lt;br /&gt;
* [[sources_no_enterprise|Activer les dépots ProxMox sans souscription]]&lt;br /&gt;
* [[WireGuard_LXC_Alpine_Linux|VPN WireGuard in Alpine LXC]]&lt;br /&gt;
* [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Alpine LXC]]&lt;br /&gt;
* [[AutoUpdate|Mises à jour automatiques]]&lt;br /&gt;
* [[UpgradeAlpine|Mise à niveau d&#039;Alpine Linux]]&lt;br /&gt;
* [[timezone_LXC|Mettre à l&#039;heure un containeur LXC]]&lt;br /&gt;
* [[Postfix|Redirection des courriels locaux via &amp;quot;Postfix&amp;quot;]]&lt;br /&gt;
* [[Stunnel|Stunnel]]&lt;br /&gt;
* [[ACME_ProxMox|Obtenir un certificat pour ProxMox via ACME]]&lt;br /&gt;
* [[SystemD|Création service SystemD]]&lt;br /&gt;
* [[NFS|Partage NFS]]&lt;br /&gt;
* [[SSH|Serveur SSH Alpine Linux]]&lt;br /&gt;
* [[DDclient|Mise à jour d&#039;un nom de domaine dynamique via DDclient]]&lt;br /&gt;
* [[Samba|Monter un partage Samba/CIFS sur Linux]]&lt;br /&gt;
* [[bindmount|Monter un dossier local dans un &amp;quot;container LXC&amp;quot;]]&lt;br /&gt;
* [[GPU_Passthrough|GPU Passtrough sans limite de sessions dans un LXC]]&lt;br /&gt;
* [[LXC_ZFS_Reduire|Réduire la taille du disque d&#039;un conteneur LXC sur un support ZFS]]&lt;br /&gt;
* [[restore_mknod|Restaurer un conteneur avec erreur &amp;quot;Cannot mknod: Operation not permitted&amp;quot;]]&lt;br /&gt;
* [[RAMDISK|disque en mémoire vive (ramdisk)]]&lt;br /&gt;
* [[NIC_SR-IOV|Cartes réseaux virtuelles SR-IOV]]&lt;br /&gt;
* [[DummyMonitor|Ajout d&#039;un écran virtuel à une VM Linux]] (à faire..)&lt;br /&gt;
==Optimisation ProxMox==&lt;br /&gt;
* [[CPU_Flags|CPU Flags]]&lt;br /&gt;
* [[ProxMox_swapiness|Limiter l&#039;utilisation de la mémoire &amp;quot;swap&amp;quot; au minimum nécessaire sur ProxMox]]&lt;br /&gt;
* [[PCI_passthrough|Activer le &amp;quot;PCI Passthrough&amp;quot;]]&lt;br /&gt;
* [[SuperMicro_X9|Gestion des ventilateurs sur carte mère SuperMicro X9]]&lt;br /&gt;
* [[CPU_governor|Gestion de l&#039;énergie CPU]]&lt;br /&gt;
* [[spectre-meltdown-checker|Protection contre les failles de sécurité du processeur (Spectre &amp;amp; Meltdown)]]&lt;br /&gt;
* [[fwupd|Mise à jour des &amp;quot;Firmware&amp;quot; via fwupd]]&lt;br /&gt;
* [[No_High_Availability|Désactiver les service de &amp;quot;High Availability&amp;quot; pour réduire l&#039;écriture sur les SSD]]&lt;br /&gt;
* [[les_logs|Réduire les logs]]&lt;br /&gt;
* [[ZFS_import|Pool ZFS chargé rapidement au boot]]&lt;br /&gt;
&lt;br /&gt;
= ZFS =&lt;br /&gt;
* [[ZFS|Commandes ZFS]]&lt;br /&gt;
&lt;br /&gt;
= TrueNas Core =&lt;br /&gt;
* [[Portail_PlugIns|Changer l&#039;adresse &amp;quot;Portails administratifs&amp;quot; des plugins]]&lt;br /&gt;
* [[TrueNasCore_QemuGuestAgent|Installer le &amp;quot;Qemu Guest Agent&amp;quot; dans un TrueNas Core (BSD) virtualisé]]&lt;br /&gt;
&lt;br /&gt;
= MySQL =&lt;br /&gt;
[[MySQL|MySQL]]&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;nowiki&amp;gt;Prometheus&amp;lt;/nowiki&amp;gt; =&lt;br /&gt;
&lt;br /&gt;
* [[Prometheus| Suveillance via &amp;lt;nowiki&amp;gt;Prometheus&amp;lt;/nowiki&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= SyncThing =&lt;br /&gt;
&lt;br /&gt;
* [[SyncThing| Synchronisation de fichier via SyncThing]]&lt;br /&gt;
&lt;br /&gt;
= NextCloud =&lt;br /&gt;
&lt;br /&gt;
* [[NextCloud|Serveur Cloud Libre NextCloud]]&lt;br /&gt;
&lt;br /&gt;
= SuiteCRM =&lt;br /&gt;
&lt;br /&gt;
* [[SuiteCRM|SuiteCRM]]&lt;br /&gt;
&lt;br /&gt;
= MediaWiki =&lt;br /&gt;
&lt;br /&gt;
* [[MediaWiki| MediaWiki]]&lt;br /&gt;
&lt;br /&gt;
= PrestaShop =&lt;br /&gt;
&lt;br /&gt;
* [[PrestaShop| PrestaShop]]&lt;br /&gt;
&lt;br /&gt;
= ZoneMinder=&lt;br /&gt;
&lt;br /&gt;
* [[ZoneMinder|ZoneMinder]]&lt;br /&gt;
&lt;br /&gt;
= Dépot Debian &amp;quot;Testing&amp;quot; disponibles en conservant les dépots &amp;quot;Stable&amp;quot; par défaut =&lt;br /&gt;
* [[Debian_APT|Gestion des dépots Debian]]&lt;br /&gt;
&lt;br /&gt;
= Jellyfin =&lt;br /&gt;
* [[Jellyfin|Serveur de médias Jellyfin]]&lt;br /&gt;
&lt;br /&gt;
= Serveur RustDesk =&lt;br /&gt;
* [[RustDesk|Serveur contrôle à distance Open-Source RustDesk]]&lt;br /&gt;
&lt;br /&gt;
= Serveur web retrogaming LXC =&lt;br /&gt;
* [[ROMM|Serveur de jeux rétro web LXC]]&lt;br /&gt;
&lt;br /&gt;
= Games On Whales =&lt;br /&gt;
* [[gowstream|Service de streaming de bureau Linux avec prise en charge de l&#039;accélération 3D via un conteneur]]&lt;br /&gt;
= Moonlight Wev Stream =&lt;br /&gt;
* [[moonlight_web_stream|CLient Moonlight via un navigateur web]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3248</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3248"/>
		<updated>2026-03-22T11:34:21Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* convertir les gamelist.xml en metadata.txt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
 &lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gc&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gc&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3247</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3247"/>
		<updated>2026-03-22T10:07:36Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Fichiers .m3u */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
 &lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gamecube&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamecube&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
== Spécial Saturn ==&lt;br /&gt;
 # vi make_m3u.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 from __future__ import annotations&lt;br /&gt;
 &lt;br /&gt;
 import re&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 from collections import defaultdict&lt;br /&gt;
 &lt;br /&gt;
 DELETE_OLD_M3U = True&lt;br /&gt;
 EXTENSIONS = {&amp;quot;.chd&amp;quot;, &amp;quot;.rvz&amp;quot;}&lt;br /&gt;
 &lt;br /&gt;
 # Si un fichier contient un de ces termes, on l&#039;ignore complètement&lt;br /&gt;
 EXCLUDE_FILE_TERMS = [&lt;br /&gt;
     &amp;quot;omake&amp;quot;,&lt;br /&gt;
     &amp;quot;portrait&amp;quot;,&lt;br /&gt;
     &amp;quot;interview&amp;quot;,&lt;br /&gt;
     &amp;quot;making&amp;quot;,&lt;br /&gt;
     &amp;quot;calendar&amp;quot;,&lt;br /&gt;
     &amp;quot;yobikake-kun&amp;quot;,&lt;br /&gt;
     &amp;quot;demo&amp;quot;,&lt;br /&gt;
     &amp;quot;trial&amp;quot;,&lt;br /&gt;
     &amp;quot;kawaraban&amp;quot;,&lt;br /&gt;
     &amp;quot;digital edition&amp;quot;,&lt;br /&gt;
     &amp;quot;special cd&amp;quot;,&lt;br /&gt;
     &amp;quot;premium cd&amp;quot;,&lt;br /&gt;
     &amp;quot;opening disc&amp;quot;,&lt;br /&gt;
     &amp;quot;extra disc&amp;quot;,&lt;br /&gt;
     &amp;quot;addition&amp;quot;,&lt;br /&gt;
     &amp;quot;append&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus disc&amp;quot;,&lt;br /&gt;
     &amp;quot;bonus&amp;quot;,&lt;br /&gt;
     &amp;quot;premium disc&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Groupes qu&#039;on ne veut jamais transformer en .m3u&lt;br /&gt;
 EXCLUDE_GROUP_TERMS = [&lt;br /&gt;
     &amp;quot;private idol&amp;quot;,&lt;br /&gt;
     &amp;quot;thunder storm &amp;amp; road blaster&amp;quot;,&lt;br /&gt;
     &amp;quot;time gal &amp;amp; ninja hayate&amp;quot;,&lt;br /&gt;
     &amp;quot;dungeons &amp;amp; dragons collection&amp;quot;,&lt;br /&gt;
     &amp;quot;street fighter collection&amp;quot;,&lt;br /&gt;
     &amp;quot;wangan dead heat + real arrange&amp;quot;,&lt;br /&gt;
     &amp;quot;virtuacall s&amp;quot;,&lt;br /&gt;
     &amp;quot;twinkle star sprites&amp;quot;,&lt;br /&gt;
     &amp;quot;senkutsu katsuryu taisen - chaos seed&amp;quot;,&lt;br /&gt;
     &amp;quot;pia carrot e youkoso!! 2&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong-kyou jidai cebu island&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong doukyuusei special&amp;quot;,&lt;br /&gt;
     &amp;quot;mahjong gakuensai&amp;quot;,&lt;br /&gt;
     &amp;quot;falcom classics&amp;quot;,&lt;br /&gt;
     &amp;quot;friends - seishun no kagayaki&amp;quot;,&lt;br /&gt;
     &amp;quot;last bronx&amp;quot;,&lt;br /&gt;
     &amp;quot;voice fantasia s - ushinawareta voice power&amp;quot;,&lt;br /&gt;
     &amp;quot;idol janshi suchie-pai ii&amp;quot;,&lt;br /&gt;
     &amp;quot;jantei battle cos-player&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi&amp;quot;,&lt;br /&gt;
     &amp;quot;elf o karu monotachi ii&amp;quot;,&lt;br /&gt;
     &amp;quot;daisuki&amp;quot;,&lt;br /&gt;
     &amp;quot;sengoku blade - sengoku ace episode ii&amp;quot;,&lt;br /&gt;
     &amp;quot;nanatsu kaze no shima monogatari&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 # Détection stricte de Disc/CD/Disk + numéro/lettre&lt;br /&gt;
 DISC_RE = re.compile(&lt;br /&gt;
     r&#039;(?i)(?:^|[\s(.\-])(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w]|$)&#039;&lt;br /&gt;
 )&lt;br /&gt;
 &lt;br /&gt;
 # Pour extraire la base commune avant le premier marqueur disque&lt;br /&gt;
 BASE_PATTERNS = [&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)[ \t]*-[ \t]*(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s*\((?:disc|disk|cd)\s*([0-9]+|[A-Za-z])\)(?:.*|$)&#039;),&lt;br /&gt;
     re.compile(r&#039;(?i)^(.*?)\s+(?:disc|disk|cd)\s*([0-9]+|[A-Za-z])(?:[^\w].*|$)&#039;),&lt;br /&gt;
 ]&lt;br /&gt;
 &lt;br /&gt;
 def norm_spaces(s: str) -&amp;gt; str:&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+&amp;quot;, &amp;quot; &amp;quot;, s).strip()&lt;br /&gt;
     s = re.sub(r&amp;quot;\s+\)&amp;quot;, &amp;quot;)&amp;quot;, s)&lt;br /&gt;
     s = re.sub(r&amp;quot;\(\s+&amp;quot;, &amp;quot;(&amp;quot;, s)&lt;br /&gt;
     return s.strip()&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_file(stem: str) -&amp;gt; bool:&lt;br /&gt;
     s = stem.lower()&lt;br /&gt;
     return any(term in s for term in EXCLUDE_FILE_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def should_exclude_group(group: str) -&amp;gt; bool:&lt;br /&gt;
    s = group.lower()&lt;br /&gt;
      return any(term in s for term in EXCLUDE_GROUP_TERMS)&lt;br /&gt;
 &lt;br /&gt;
 def has_real_disc_marker(stem: str) -&amp;gt; bool:&lt;br /&gt;
     return DISC_RE.search(stem) is not None&lt;br /&gt;
 &lt;br /&gt;
 def extract_base(stem: str) -&amp;gt; str | None:&lt;br /&gt;
     for pat in BASE_PATTERNS:&lt;br /&gt;
         m = pat.match(stem)&lt;br /&gt;
         if m:&lt;br /&gt;
             return norm_spaces(m.group(1))&lt;br /&gt;
     return None&lt;br /&gt;
 &lt;br /&gt;
 def disc_sort_key(stem: str) -&amp;gt; tuple[int, str]:&lt;br /&gt;
     m = DISC_RE.search(stem)&lt;br /&gt;
     if not m:&lt;br /&gt;
         return (9999, stem.lower())&lt;br /&gt;
     token = m.group(1)&lt;br /&gt;
     if token.isdigit():&lt;br /&gt;
         return (int(token), stem.lower())&lt;br /&gt;
     return (ord(token.lower()) - 96, stem.lower())&lt;br /&gt;
 &lt;br /&gt;
 def main() -&amp;gt; None:&lt;br /&gt;
     cwd = Path(&amp;quot;.&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     if DELETE_OLD_M3U:&lt;br /&gt;
         for m3u in cwd.glob(&amp;quot;*.m3u&amp;quot;):&lt;br /&gt;
             m3u.unlink()&lt;br /&gt;
 &lt;br /&gt;
     groups: dict[str, list[Path]] = defaultdict(list)&lt;br /&gt;
     seen = set()&lt;br /&gt;
 &lt;br /&gt;
     for path in sorted(cwd.iterdir(), key=lambda p: p.name.lower()):&lt;br /&gt;
         if not path.is_file():&lt;br /&gt;
             continue&lt;br /&gt;
         if path.suffix.lower() not in EXTENSIONS:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         stem = path.stem&lt;br /&gt;
 &lt;br /&gt;
         if should_exclude_file(stem):&lt;br /&gt;
             continue&lt;br /&gt;
         if not has_real_disc_marker(stem):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         base = extract_base(stem)&lt;br /&gt;
         if not base or base == stem:&lt;br /&gt;
             continue&lt;br /&gt;
         if should_exclude_group(base):&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         key = (base, path.name)&lt;br /&gt;
         if key in seen:&lt;br /&gt;
             continue&lt;br /&gt;
         seen.add(key)&lt;br /&gt;
 &lt;br /&gt;
         groups[base].append(path)&lt;br /&gt;
 &lt;br /&gt;
     generated = 0&lt;br /&gt;
 &lt;br /&gt;
     for base in sorted(groups, key=str.lower):&lt;br /&gt;
         files = sorted(groups[base], key=lambda p: disc_sort_key(p.stem))&lt;br /&gt;
         if len(files) &amp;lt; 2:&lt;br /&gt;
             continue&lt;br /&gt;
 &lt;br /&gt;
         outfile = cwd / f&amp;quot;{base}.m3u&amp;quot;&lt;br /&gt;
         outfile.write_text(&lt;br /&gt;
             &amp;quot;\n&amp;quot;.join(p.name for p in files) + &amp;quot;\n&amp;quot;,&lt;br /&gt;
             encoding=&amp;quot;utf-8&amp;quot;&lt;br /&gt;
         )&lt;br /&gt;
         print(f&amp;quot;Créé : {outfile.name} ({len(files)} disques)&amp;quot;)&lt;br /&gt;
         generated += 1&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;\nTerminé. {generated} fichier(s) .m3u généré(s).&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x make_m3u.py&lt;br /&gt;
 # ./make_m3u.py&lt;br /&gt;
 # rm make_m3u.py&lt;br /&gt;
&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3246</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3246"/>
		<updated>2026-03-21T19:29:23Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* convertir les gamelist.xml en metadata.txt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
 &lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gamecube&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamecube&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3245</id>
		<title>ROMM</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=ROMM&amp;diff=3245"/>
		<updated>2026-03-21T16:52:34Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* convertir les gamelist.xml en metadata.txt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
* Alpine LXC avec Nesting et keyctl activé (Docker).&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = En cas d&#039;utilisation d&#039;autre service de rétrogaming comme [[Gowstream|Games On Whales]] il peut être préférable de les regrouper sur une même machine pour le partage les ressources de &amp;quot;scraping&amp;quot;.&lt;br /&gt;
 }}&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Sans les droits d&#039;écritures sur les dossiers certaines fonctions de gestion de ROMM ne pourront pas fonctionner (suppression / import..)..&lt;br /&gt;
 }}&lt;br /&gt;
Ressource confortable :&lt;br /&gt;
* 2 CPU&lt;br /&gt;
* 2Gb de RAM&lt;br /&gt;
* 16GB de Disque&lt;br /&gt;
Il est recommandé d&#039;organiser les fichiers de la façon suivante :&lt;br /&gt;
 /roms/{platform}/&lt;br /&gt;
 /bios/{platform}/&lt;br /&gt;
Il est également nécessaire d&#039;utiliser des noms de dossier reconnu par ROMM, exemple :&lt;br /&gt;
 roms/&lt;br /&gt;
   nes&lt;br /&gt;
   snes&lt;br /&gt;
   n64&lt;br /&gt;
   gb&lt;br /&gt;
   gbc&lt;br /&gt;
   gba&lt;br /&gt;
   genesis&lt;br /&gt;
   segacd&lt;br /&gt;
   sega32&lt;br /&gt;
   neogeomvs&lt;br /&gt;
   neo-geo-pocket&lt;br /&gt;
   tg16&lt;br /&gt;
   pcenginecd&lt;br /&gt;
   wonderswan&lt;br /&gt;
   wonderswan-color&lt;br /&gt;
&lt;br /&gt;
== Metadata ==&lt;br /&gt;
Afin d&#039;obtenir les jaquettes etc il est recommandé de :&lt;br /&gt;
 * créer un compte chez [https://www.screenscraper.fr/membreinscription.php screenscraper]&lt;br /&gt;
 * créer une clef API chez [https://www.steamgriddb.com/profile/preferences/api steamgriddb]&lt;br /&gt;
 * créer une application &amp;quot;twitch developers&amp;quot; (nécessite un mobile) pour certaines focntions, suivre [https://docs.romm.app/4.5.0/Getting-Started/Metadata-Providers/#igdb ce tutoriel].&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
 # apk update &amp;amp;&amp;amp; apk upgrade&lt;br /&gt;
 # apk add docker docker-cli-compose&lt;br /&gt;
 # rc-update add docker default&lt;br /&gt;
 # rc-service docker start&lt;br /&gt;
 # mkdir -p /opt/romm /opt/romm/config /opt/romm/data /opt/romm/assets&lt;br /&gt;
 # cd /opt/romm&lt;br /&gt;
Fichier de configuration du conteneur :&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3.8&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   mysql_data:&lt;br /&gt;
   romm_resources:&lt;br /&gt;
   romm_redis_data:&lt;br /&gt;
 &lt;br /&gt;
 services:&lt;br /&gt;
   romm:&lt;br /&gt;
     image: ghcr.io/rommapp/romm:latest&lt;br /&gt;
     container_name: romm&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - TZ=&amp;lt;font color = blue&amp;gt;Europe/Madrid&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Database (MariaDB)&lt;br /&gt;
       - DB_HOST=romm-db&lt;br /&gt;
       - DB_NAME=romm&lt;br /&gt;
       - DB_USER=romm-user&lt;br /&gt;
       - DB_PASSWD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt; # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus bas!!&lt;br /&gt;
 &lt;br /&gt;
       # Required auth secret (IMPORTANT)&lt;br /&gt;
       - ROMM_AUTH_SECRET_KEY=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_AUTH_SECRET_KEY&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Metadata providers (optionnel mais recommandé)&lt;br /&gt;
       - SCREENSCRAPER_USER=&amp;lt;font color = blue&amp;gt;ton_user&amp;lt;/font&amp;gt;&lt;br /&gt;
       - SCREENSCRAPER_PASSWORD=&amp;lt;font color = blue&amp;gt;ton_mdp&amp;lt;/font&amp;gt;&lt;br /&gt;
       # - RETROACHIEVEMENTS_API_KEY=ta_cle&lt;br /&gt;
       - STEAMGRIDDB_API_KEY=&amp;lt;font color = blue&amp;gt;ta_cle&amp;lt;/font&amp;gt;&lt;br /&gt;
       - HASHEOUS_API_ENABLED=true&lt;br /&gt;
       - IGDB_CLIENT_ID=&amp;lt;font color = blue&amp;gt;idapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
       - IGDB_CLIENT_SECRET=&amp;lt;font color = blue&amp;gt;motdepassapplitwitch&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       - SCAN_TIMEOUT=86400 # timeout de 24h&lt;br /&gt;
       - SCAN_WORKERS=4 # limite IGBD&lt;br /&gt;
 &lt;br /&gt;
     volumes:&lt;br /&gt;
       # Cache / ressources / jobs&lt;br /&gt;
       #- romm_resources:/romm/resources&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/resources&amp;lt;/font&amp;gt;:/romm/resources:rw # stockage images, manuels etc..&lt;br /&gt;
       - romm_redis_data:/redis-data&lt;br /&gt;
 &lt;br /&gt;
       # Ta bibliothèque ROM (lecture seule)&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms&amp;lt;/font&amp;gt;:/romm/library/roms:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt; # ro pour read-only&lt;br /&gt;
       - &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios&amp;lt;/font&amp;gt;:/romm/library/bios:&amp;lt;font color = red&amp;gt;rw&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
       # Assets RomM (écriture)&lt;br /&gt;
       - /opt/romm/assets:/romm/assets&lt;br /&gt;
 &lt;br /&gt;
       # Config (config.yml)&lt;br /&gt;
       - /opt/romm/config:/romm/config:rw&lt;br /&gt;
 &lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;8080:8080&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     depends_on:&lt;br /&gt;
       romm-db:&lt;br /&gt;
         condition: service_healthy&lt;br /&gt;
 &lt;br /&gt;
   romm-db:&lt;br /&gt;
     image: mariadb:11&lt;br /&gt;
     container_name: romm-db&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     environment:&lt;br /&gt;
       - MARIADB_ROOT_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROOT_PASSWORD&amp;lt;/font&amp;gt;&lt;br /&gt;
       - MARIADB_DATABASE=romm&lt;br /&gt;
       - MARIADB_USER=romm-user&lt;br /&gt;
       - MARIADB_PASSWORD=&amp;lt;font color = blue&amp;gt;CHANGE_MOI_ROMM_MARIADB_PASSWORD&amp;lt;/font&amp;gt;  # mot de passe de &amp;quot;romm-user&amp;quot;, identique plus haut!!&lt;br /&gt;
     volumes:&lt;br /&gt;
       - mysql_data:/var/lib/mysql&lt;br /&gt;
     healthcheck:&lt;br /&gt;
       test: [&amp;quot;CMD&amp;quot;, &amp;quot;healthcheck.sh&amp;quot;, &amp;quot;--connect&amp;quot;, &amp;quot;--innodb_initialized&amp;quot;]&lt;br /&gt;
       start_period: 30s&lt;br /&gt;
       interval: 10s&lt;br /&gt;
       timeout: 5s&lt;br /&gt;
       retries: 10&lt;br /&gt;
Fichier de configuration de ROMM :&lt;br /&gt;
 # vi /opt/romm/config/config.yml&lt;br /&gt;
&lt;br /&gt;
 filesystem:&lt;br /&gt;
   roms: /romm/library/roms&lt;br /&gt;
   bios: /romm/library/bios&lt;br /&gt;
 &lt;br /&gt;
 scan:&lt;br /&gt;
   export_gamelist: true # optionnel&lt;br /&gt;
   priority:&lt;br /&gt;
     metadata:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;hasheous&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     artwork:&lt;br /&gt;
       - &amp;quot;ss&amp;quot;&lt;br /&gt;
       - &amp;quot;steamgriddb&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     region:&lt;br /&gt;
       - &amp;quot;eu&amp;quot;&lt;br /&gt;
       - &amp;quot;us&amp;quot;&lt;br /&gt;
       - &amp;quot;jp&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     language:&lt;br /&gt;
       - &amp;quot;fr&amp;quot;&lt;br /&gt;
       - &amp;quot;en&amp;quot;&lt;br /&gt;
       - &amp;quot;es&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
   media:&lt;br /&gt;
     - box2d&lt;br /&gt;
     - screenshot&lt;br /&gt;
     - marquee&lt;br /&gt;
     - fanart&lt;br /&gt;
     - manual&lt;br /&gt;
     - title_screen&lt;br /&gt;
     - bezel&lt;br /&gt;
 &lt;br /&gt;
 emulatorjs:&lt;br /&gt;
   debug: false&lt;br /&gt;
   cache_limit: null&lt;br /&gt;
   disable_batch_bootup: false&lt;br /&gt;
   disable_auto_unload: false&lt;br /&gt;
 &lt;br /&gt;
   netplay:&lt;br /&gt;
     enabled: true&lt;br /&gt;
     ice_servers:&lt;br /&gt;
       - urls: &amp;quot;stun:stun.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun1.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;stun:stun2.l.google.com:19302&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:80&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
       - urls: &amp;quot;turn:openrelay.metered.ca:443&amp;quot;&lt;br /&gt;
         username: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
         credential: &amp;quot;openrelayproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= vhost nginx =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Voir [[Nginx_ReverseProxy_LXC_Alpine_Linux|Reverse Proxy Nginx]]&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
 server {&lt;br /&gt;
     listen 80;&lt;br /&gt;
     listen [::]:80;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Force HTTPS&lt;br /&gt;
     return 301 &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;$host$request_uri;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     listen [::]:443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     # Uploads (RomM peut être &amp;quot;gros&amp;quot;)&lt;br /&gt;
     client_max_body_size 0;&lt;br /&gt;
 &lt;br /&gt;
     # TLS (Certbot)&lt;br /&gt;
     ssl_certificate     /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;site.example.net&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
     include             /etc/letsencrypt/options-ssl-nginx.conf;&lt;br /&gt;
     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;&lt;br /&gt;
 &lt;br /&gt;
     # HSTS (optionnel)&lt;br /&gt;
     add_header Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     # Sécurité / info&lt;br /&gt;
     server_tokens off;&lt;br /&gt;
     add_header X-Frame-Options &amp;quot;SAMEORIGIN&amp;quot; always;&lt;br /&gt;
     add_header X-XSS-Protection &amp;quot;1; mode=block&amp;quot; always;&lt;br /&gt;
     add_header X-Content-Type-Options &amp;quot;nosniff&amp;quot; always;&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         # WebSocket + keep-alive&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         # Headers reverse-proxy&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
         proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
 &lt;br /&gt;
         # Timeouts (scans/transferts longs)&lt;br /&gt;
         proxy_read_timeout 3600;&lt;br /&gt;
         proxy_send_timeout 3600;&lt;br /&gt;
 &lt;br /&gt;
         # Backend RomM&lt;br /&gt;
         proxy_pass &amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;&amp;lt;font color = blue&amp;gt;IP_SERVEUR_WEB&amp;lt;/font&amp;gt;:8080;&lt;br /&gt;
 &lt;br /&gt;
         # Optionnel: si tu as des soucis de buffering&lt;br /&gt;
         # proxy_buffering off;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = Penser à exclure &amp;quot;&#039;&#039;&#039;metadata.pegasus.txt&#039;&#039;&#039;&amp;quot; des fichiers à scanner dans ROMM.}}&lt;br /&gt;
Pour passer de ROMM à Pegasus : [https://pegasus-frontend.org/tools/convert/ lien] (déconseillé)&lt;br /&gt;
&lt;br /&gt;
Mais le convertisseur officiel ne convertie pas les assets et il faut rajouter à la main les collections etc, on peut utiliser un script local plus optimisé :&lt;br /&gt;
&lt;br /&gt;
 # vi /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
 import sys&lt;br /&gt;
 import xml.etree.ElementTree as ET&lt;br /&gt;
 from pathlib import Path&lt;br /&gt;
 &lt;br /&gt;
 ROOT = Path(&amp;quot;&amp;lt;font color = blue&amp;gt;/mnt/Media/Media/Emulation/roms&amp;lt;/font&amp;gt;&amp;quot;)&lt;br /&gt;
 if len(sys.argv) &amp;gt; 1:&lt;br /&gt;
     ROOT = Path(sys.argv[1])&lt;br /&gt;
 &lt;br /&gt;
 ADD_LAUNCH = &amp;lt;font color = red&amp;gt;True&amp;lt;/font&amp;gt;&lt;br /&gt;
 RETROARCH = &amp;quot;retroarch --fullscreen&amp;quot;&lt;br /&gt;
 CORE_DIR = &amp;quot;/home/retro/.config/retroarch/cores&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def clean(text):&lt;br /&gt;
     if text is None:&lt;br /&gt;
         return &amp;quot;&amp;quot;&lt;br /&gt;
     return text.replace(&amp;quot;\r&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot; &amp;quot;).strip()&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def collection_info_from_dir(dirname: str):&lt;br /&gt;
     slug = dirname.strip().lower()&lt;br /&gt;
 &lt;br /&gt;
     mapping = {&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: (&amp;quot;Arcade&amp;quot;, &amp;quot;arcade&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: (&amp;quot;Neo Geo&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: (&amp;quot;Neo Geo AES&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: (&amp;quot;Neo Geo MVS&amp;quot;, &amp;quot;neogeo&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-cd&amp;quot;: (&amp;quot;Neo Geo CD&amp;quot;, &amp;quot;neo-geo-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket&amp;quot;: (&amp;quot;Neo Geo Pocket&amp;quot;, &amp;quot;ngp&amp;quot;),&lt;br /&gt;
         &amp;quot;neo-geo-pocket-color&amp;quot;: (&amp;quot;Neo Geo Pocket Color&amp;quot;, &amp;quot;ngpc&amp;quot;),&lt;br /&gt;
         &amp;quot;genesis&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: (&amp;quot;Sega Mega Drive/Genesis&amp;quot;, &amp;quot;megadrive&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: (&amp;quot;Sega CD&amp;quot;, &amp;quot;segacd&amp;quot;),&lt;br /&gt;
         &amp;quot;segacd32&amp;quot;: (&amp;quot;Sega CD 32X&amp;quot;, &amp;quot;segacd32&amp;quot;),&lt;br /&gt;
         &amp;quot;sega32&amp;quot;: (&amp;quot;Sega 32X&amp;quot;, &amp;quot;sega32x&amp;quot;),&lt;br /&gt;
         &amp;quot;sms&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;mastersystem&amp;quot;),&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: (&amp;quot;Sega Master System/Mark III&amp;quot;, &amp;quot;sms&amp;quot;),&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: (&amp;quot;Sega Game Gear&amp;quot;, &amp;quot;gamegear&amp;quot;),&lt;br /&gt;
         &amp;quot;sg1000&amp;quot;: (&amp;quot;SG-1000&amp;quot;, &amp;quot;sg-1000&amp;quot;),&lt;br /&gt;
         &amp;quot;sc3000&amp;quot;: (&amp;quot;SC-3000&amp;quot;, &amp;quot;sc-3000&amp;quot;),&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcengine&amp;quot;: (&amp;quot;TurboGrafx-16/PC Engine&amp;quot;, &amp;quot;tg16&amp;quot;),&lt;br /&gt;
         &amp;quot;pcenginecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;pcecd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;turbografx-cd&amp;quot;: (&amp;quot;Turbografx-16/PC Engine CD&amp;quot;, &amp;quot;tg-cd&amp;quot;),&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: (&amp;quot;PC Engine SuperGrafx&amp;quot;, &amp;quot;supergrafx&amp;quot;),&lt;br /&gt;
         &amp;quot;nes&amp;quot;: (&amp;quot;Nintendo Entertainment System&amp;quot;, &amp;quot;nes&amp;quot;),&lt;br /&gt;
         &amp;quot;snes&amp;quot;: (&amp;quot;Super Nintendo Entertainment System&amp;quot;, &amp;quot;snes&amp;quot;),&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: (&amp;quot;Super Famicom&amp;quot;, &amp;quot;sfam&amp;quot;),&lt;br /&gt;
         &amp;quot;gb&amp;quot;: (&amp;quot;Game Boy&amp;quot;, &amp;quot;gb&amp;quot;),&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: (&amp;quot;Game Boy Color&amp;quot;, &amp;quot;gbc&amp;quot;),&lt;br /&gt;
         &amp;quot;gba&amp;quot;: (&amp;quot;Game Boy Advance&amp;quot;, &amp;quot;gba&amp;quot;),&lt;br /&gt;
         &amp;quot;psx&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;ps1&amp;quot;: (&amp;quot;PlayStation&amp;quot;, &amp;quot;psx&amp;quot;),&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: (&amp;quot;Sega Saturn&amp;quot;, &amp;quot;saturn&amp;quot;),&lt;br /&gt;
         &amp;quot;dc&amp;quot;: (&amp;quot;Dreamcast&amp;quot;, &amp;quot;dreamcast&amp;quot;),&lt;br /&gt;
         &amp;quot;n64&amp;quot;: (&amp;quot;Nintendo 64&amp;quot;, &amp;quot;n64&amp;quot;),&lt;br /&gt;
         &amp;quot;nds&amp;quot;: (&amp;quot;Nintendo DS&amp;quot;, &amp;quot;nds&amp;quot;),&lt;br /&gt;
         &amp;quot;ngc&amp;quot;: (&amp;quot;Nintendo GameCube&amp;quot;, &amp;quot;gamecube&amp;quot;),&lt;br /&gt;
         &amp;quot;wii&amp;quot;: (&amp;quot;Wii&amp;quot;, &amp;quot;wii&amp;quot;),&lt;br /&gt;
         &amp;quot;lynx&amp;quot;: (&amp;quot;Atari Lynx&amp;quot;, &amp;quot;atarilynx&amp;quot;),&lt;br /&gt;
         &amp;quot;jaguar&amp;quot;: (&amp;quot;Atari Jaguar&amp;quot;, &amp;quot;atarijaguar&amp;quot;),&lt;br /&gt;
         &amp;quot;atari-jaguar-cd&amp;quot;: (&amp;quot;Atari Jaguar CD&amp;quot;, &amp;quot;atarijaguarcd&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: (&amp;quot;WonderSwan&amp;quot;, &amp;quot;wonderswan&amp;quot;),&lt;br /&gt;
         &amp;quot;wonderswan-color&amp;quot;: (&amp;quot;WonderSwan Color&amp;quot;, &amp;quot;wonderswancolor&amp;quot;),&lt;br /&gt;
         &amp;quot;msx&amp;quot;: (&amp;quot;MSX&amp;quot;, &amp;quot;msx&amp;quot;),&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: (&amp;quot;MSX2&amp;quot;, &amp;quot;msx2&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: (&amp;quot;Amiga&amp;quot;, &amp;quot;amiga&amp;quot;),&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: (&amp;quot;Amiga CD32&amp;quot;, &amp;quot;amiga-cd32&amp;quot;),&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: (&amp;quot;ScummVM&amp;quot;, &amp;quot;scummvm&amp;quot;),&lt;br /&gt;
         &amp;quot;3do&amp;quot;: (&amp;quot;3DO Interactive Multiplayer&amp;quot;, &amp;quot;3do&amp;quot;),&lt;br /&gt;
         &amp;quot;psp&amp;quot;: (&amp;quot;PlayStation Portable&amp;quot;, &amp;quot;psp&amp;quot;),&lt;br /&gt;
         &amp;quot;psvita&amp;quot;: (&amp;quot;PlayStation Vita&amp;quot;, &amp;quot;psvita&amp;quot;),&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: (&amp;quot;Vectrex&amp;quot;, &amp;quot;vectrex&amp;quot;),&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: (&amp;quot;Virtual Boy&amp;quot;, &amp;quot;virtualboy&amp;quot;),&lt;br /&gt;
         &amp;quot;zxs&amp;quot;: (&amp;quot;ZX Spectrum&amp;quot;, &amp;quot;zxs&amp;quot;),&lt;br /&gt;
         &amp;quot;c64&amp;quot;: (&amp;quot;Commodore C64/128/MAX&amp;quot;, &amp;quot;c64&amp;quot;),&lt;br /&gt;
         &amp;quot;c128&amp;quot;: (&amp;quot;Commodore 128&amp;quot;, &amp;quot;c128&amp;quot;),&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: (&amp;quot;Commodore VIC-20&amp;quot;, &amp;quot;vic-20&amp;quot;),&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: (&amp;quot;ColecoVision&amp;quot;, &amp;quot;colecovision&amp;quot;),&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: (&amp;quot;Atari 2600&amp;quot;, &amp;quot;atari2600&amp;quot;),&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: (&amp;quot;Atari 5200&amp;quot;, &amp;quot;atari5200&amp;quot;),&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: (&amp;quot;Atari 7800&amp;quot;, &amp;quot;atari7800&amp;quot;),&lt;br /&gt;
         &amp;quot;g-and-w&amp;quot;: (&amp;quot;Game &amp;amp; Watch&amp;quot;, &amp;quot;gameandwatch&amp;quot;),&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if slug in mapping:&lt;br /&gt;
         return mapping[slug]&lt;br /&gt;
 &lt;br /&gt;
     return slug.replace(&amp;quot;-&amp;quot;, &amp;quot; &amp;quot;).replace(&amp;quot;_&amp;quot;, &amp;quot; &amp;quot;).title(), slug&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def launch_for_shortname(shortname: str):&lt;br /&gt;
     launches = {&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;# arcade / neo geo&lt;br /&gt;
         &amp;quot;arcade&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeo&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeoaes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;neogeomvs&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fbneo_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngp&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;ngpc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_ngp_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # sega&lt;br /&gt;
         &amp;quot;megadrive&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;segacd32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sega32x&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/picodrive_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;mastersystem&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamegear&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sg-1000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sc-3000&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/genesis_plus_gx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # NEC&lt;br /&gt;
         &amp;quot;tg16&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;tg-cd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;supergrafx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_pce_fast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Nintendo&lt;br /&gt;
         &amp;quot;nes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/nestopia_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;snes&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;sfam&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/snes9x_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gb&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gbc&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gambatte_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gba&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mgba_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;n64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mupen64plus_next_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;nds&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/melondsds_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;gamecube&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wii&amp;quot;: f&#039;/Applications/dolphin-emu.AppImage --appimage-extract-and-run -b -e &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;virtualboy&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_vb_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gw_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version gw&lt;br /&gt;
         &amp;quot;gameandwatch&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mame_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;, # version mame&lt;br /&gt;
 &lt;br /&gt;
         # Sony&lt;br /&gt;
         &amp;quot;psx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/pcsx_rearmed_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;psp&amp;quot;: f&#039;/opt/emulator/ppsspp/PPSSPP-v1.20.3-anylinux-x86_64.AppImage &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Sega Saturn / Dreamcast&lt;br /&gt;
         &amp;quot;saturn&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_saturn_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;dreamcast&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/flycast_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # Atari&lt;br /&gt;
         &amp;quot;atarilynx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_lynx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguar&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         #&amp;quot;atarijaguarcd&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/virtualjaguar_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atarijaguar&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atarijaguarcd&amp;quot;: f&#039;/opt/emulator/bigpemu/bigpemu &amp;quot;{{file.path}}&amp;quot;&#039;, # installation manuelle bigpemu&lt;br /&gt;
         &amp;quot;atari2600&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/stella_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari5200&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/a5200_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;atari7800&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/prosystem_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
 &lt;br /&gt;
         # others&lt;br /&gt;
         &amp;quot;wonderswan&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;wonderswancolor&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/mednafen_wswan_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;msx2&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/fmsx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;amiga-cd32&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/puae_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;scummvm&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/scummvm_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vectrex&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vecx_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;colecovision&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/gearcoleco_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c64&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x64_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;c128&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_x128_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&lt;br /&gt;
         &amp;quot;vic-20&amp;quot;: f&#039;{RETROARCH} -L {CORE_DIR}/vice_xvic_libretro.so &amp;quot;{{file.path}}&amp;quot;&#039;,&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
     return launches.get(shortname)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def detect_extensions(games):&lt;br /&gt;
     exts = set()&lt;br /&gt;
     for game in games:&lt;br /&gt;
         path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
         if path:&lt;br /&gt;
             suffix = Path(path).suffix.lower().lstrip(&amp;quot;.&amp;quot;)&lt;br /&gt;
             if suffix:&lt;br /&gt;
                 exts.add(suffix)&lt;br /&gt;
     return sorted(exts)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def write_line(out, key, value):&lt;br /&gt;
     value = clean(value)&lt;br /&gt;
     if value:&lt;br /&gt;
         out.write(f&amp;quot;{key}: {value}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def convert_gamelist(gamelist_path: Path):&lt;br /&gt;
     romdir = gamelist_path.parent&lt;br /&gt;
     dirname = romdir.name&lt;br /&gt;
     out_path = romdir / &amp;quot;metadata.pegasus.txt&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     try:&lt;br /&gt;
         tree = ET.parse(gamelist_path)&lt;br /&gt;
         root = tree.getroot()&lt;br /&gt;
     except Exception as e:&lt;br /&gt;
         print(f&amp;quot;[ERR] {gamelist_path}: {e}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     games = root.findall(&amp;quot;game&amp;quot;)&lt;br /&gt;
     if not games:&lt;br /&gt;
         print(f&amp;quot;[WARN] {gamelist_path}: no &amp;lt;game&amp;gt; entries&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     collection, shortname = collection_info_from_dir(dirname)&lt;br /&gt;
     exts = detect_extensions(games)&lt;br /&gt;
 &lt;br /&gt;
     with out_path.open(&amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as out:&lt;br /&gt;
         out.write(f&amp;quot;collection: {collection}\n&amp;quot;)&lt;br /&gt;
         out.write(f&amp;quot;shortname: {shortname}\n&amp;quot;)&lt;br /&gt;
         if exts:&lt;br /&gt;
             out.write(f&amp;quot;extensions: {&#039; &#039;.join(exts)}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         if ADD_LAUNCH:&lt;br /&gt;
             launch = launch_for_shortname(shortname)&lt;br /&gt;
             if launch:&lt;br /&gt;
                 out.write(f&amp;quot;launch: {launch}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
         for game in games:&lt;br /&gt;
             name = clean(game.findtext(&amp;quot;name&amp;quot;))&lt;br /&gt;
             path = clean(game.findtext(&amp;quot;path&amp;quot;))&lt;br /&gt;
             if not path:&lt;br /&gt;
                 continue&lt;br /&gt;
 &lt;br /&gt;
             # IMPORTANT: on garde le chemin relatif, comme quand ça marchait&lt;br /&gt;
             if path.startswith(&amp;quot;./&amp;quot;):&lt;br /&gt;
                 path = path[2:]&lt;br /&gt;
 &lt;br /&gt;
             out.write(f&amp;quot;game: {name or Path(path).stem}\n&amp;quot;)&lt;br /&gt;
             out.write(f&amp;quot;file: {path}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             write_line(out, &amp;quot;description&amp;quot;, game.findtext(&amp;quot;desc&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;developer&amp;quot;, game.findtext(&amp;quot;developer&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;publisher&amp;quot;, game.findtext(&amp;quot;publisher&amp;quot;))&lt;br /&gt;
             write_line(out, &amp;quot;genre&amp;quot;, game.findtext(&amp;quot;genre&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             releasedate = clean(game.findtext(&amp;quot;releasedate&amp;quot;))&lt;br /&gt;
             if releasedate:&lt;br /&gt;
                 if len(releasedate) &amp;gt;= 8 and releasedate[:8].isdigit():&lt;br /&gt;
                     rd = releasedate[:8]&lt;br /&gt;
                     out.write(f&amp;quot;release: {rd[:4]}-{rd[4:6]}-{rd[6:8]}\n&amp;quot;)&lt;br /&gt;
                 else:&lt;br /&gt;
                     out.write(f&amp;quot;release: {releasedate}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             rating = clean(game.findtext(&amp;quot;rating&amp;quot;))&lt;br /&gt;
             if rating:&lt;br /&gt;
                 out.write(f&amp;quot;rating: {rating}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             thumbnail = clean(game.findtext(&amp;quot;thumbnail&amp;quot;))&lt;br /&gt;
             image = clean(game.findtext(&amp;quot;image&amp;quot;))&lt;br /&gt;
             screenshot = clean(game.findtext(&amp;quot;screenshot&amp;quot;))&lt;br /&gt;
             fanart = clean(game.findtext(&amp;quot;fanart&amp;quot;))&lt;br /&gt;
             video = clean(game.findtext(&amp;quot;video&amp;quot;))&lt;br /&gt;
 &lt;br /&gt;
             if thumbnail:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {thumbnail}\n&amp;quot;)&lt;br /&gt;
             elif image:&lt;br /&gt;
                 out.write(f&amp;quot;assets.box_front: {image}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             if screenshot:&lt;br /&gt;
                 out.write(f&amp;quot;assets.screenshot: {screenshot}\n&amp;quot;)&lt;br /&gt;
             if fanart:&lt;br /&gt;
                 out.write(f&amp;quot;assets.background: {fanart}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             # On garde la vidéo, mais si GameOS spam avec YouTube, commente ces 2 lignes&lt;br /&gt;
             if video:&lt;br /&gt;
                 out.write(f&amp;quot;assets.video: {video}\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
             out.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     print(f&amp;quot;[OK] {gamelist_path} -&amp;gt; {out_path} ({len(games)} games)&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 def main():&lt;br /&gt;
     gamelists = list(ROOT.rglob(&amp;quot;gamelist.xml&amp;quot;))&lt;br /&gt;
     if not gamelists:&lt;br /&gt;
         print(f&amp;quot;[WARN] No gamelist.xml found under {ROOT}&amp;quot;)&lt;br /&gt;
         return&lt;br /&gt;
 &lt;br /&gt;
     for gamelist in sorted(gamelists):&lt;br /&gt;
         convert_gamelist(gamelist)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
     main()&lt;br /&gt;
&lt;br /&gt;
 # chmod +x /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
Pour l&#039;exécuter :&lt;br /&gt;
 # python3 /mnt/Media/Media/Emulation/gamelist_to_pegasus.py&lt;br /&gt;
&lt;br /&gt;
Il faudra penser à télécharger le launcher associé si nécessaire dans le Retroarch de Pegasus (Main Menu -&amp;gt; Online Updater -&amp;gt; -&amp;gt; Update Core Info Files -&amp;gt; Core Downloader)&lt;br /&gt;
&lt;br /&gt;
= Fichiers .m3u =&lt;br /&gt;
Afin de gérer les jeu utilisant plusieurs CD/DVD il est nécessaire de creer un fichier liste .m3u :&lt;br /&gt;
&lt;br /&gt;
Pour les fichiers .CHD ou .RVZ :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mes/roms/plateforme/&amp;lt;/font&amp;gt;&lt;br /&gt;
Coller la commande suivante :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 unset groups counts seen_files&lt;br /&gt;
 declare -A groups&lt;br /&gt;
 declare -A counts&lt;br /&gt;
 declare -A seen_files&lt;br /&gt;
 &lt;br /&gt;
 for file in *.chd *.rvz; do&lt;br /&gt;
     [ -e &amp;quot;$file&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${file%.*}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     # Enlever uniquement le suffixe disque en fin de nom&lt;br /&gt;
     base=&amp;quot;$(printf &#039;%s\n&#039; &amp;quot;$name&amp;quot; | sed -E &#039;&lt;br /&gt;
            &amp;lt;nowiki&amp;gt;s/[[:space:]]*-[[:space:]]*(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/[[:space:]]*\((disc|disk|cd)[[:space:]]*[0-9]+\)$//I&lt;br /&gt;
           s/[[:space:]]+(disc|disk|cd)[[:space:]]*[0-9]+$//I&lt;br /&gt;
           s/(disc|disk|cd)[[:space:]]*[0-9]+$//I&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &#039;)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     [ &amp;quot;$base&amp;quot; != &amp;quot;$name&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     group=&amp;quot;$base&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     key=&amp;quot;$group&amp;quot;$&#039;\t&#039;&amp;quot;$file&amp;quot;&lt;br /&gt;
     [ -n &amp;quot;${seen_files[&amp;quot;$key&amp;quot;]}&amp;quot; ] &amp;amp;&amp;amp; continue&lt;br /&gt;
     seen_files[&amp;quot;$key&amp;quot;]=1&lt;br /&gt;
 &lt;br /&gt;
     groups[&amp;quot;$group&amp;quot;]+=&amp;quot;$file&amp;quot;$&#039;\n&#039;&lt;br /&gt;
     ((counts[&amp;quot;$group&amp;quot;]++))&lt;br /&gt;
 done&lt;br /&gt;
 &lt;br /&gt;
 for group in &amp;quot;${!groups[@]}&amp;quot;; do&lt;br /&gt;
     [ &amp;quot;${counts[&amp;quot;$group&amp;quot;]}&amp;quot; -ge 2 ] || continue&lt;br /&gt;
     printf &#039;%s&#039; &amp;quot;${groups[&amp;quot;$group&amp;quot;]}&amp;quot; | sed &#039;/^$/d&#039; | sort -V &amp;gt; &amp;quot;$group.m3u&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
= Convertir bin et cue dans une archive 7z/zip en fichier CHD =&lt;br /&gt;
Si nécessaire il faut installer les outils de MAME pour avoir chdman :&lt;br /&gt;
 # apt install mame-tools&lt;br /&gt;
Si nécessaire installer les outils pour gérer les archives 7z :&lt;br /&gt;
 # apt install p7zip-full&lt;br /&gt;
Puis on créé le script (à placer dans le dossier contenant les archives .7z/zip :&lt;br /&gt;
 # cd &amp;lt;font color = blue&amp;gt;/mon/dossier/&amp;lt;/font&amp;gt;&lt;br /&gt;
 # vi convertchd.sh&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env bash&lt;br /&gt;
 &lt;br /&gt;
 shopt -s nullglob&lt;br /&gt;
 &lt;br /&gt;
 for f in *.7z *.zip; do&lt;br /&gt;
     [ -e &amp;quot;$f&amp;quot; ] || continue&lt;br /&gt;
 &lt;br /&gt;
     name=&amp;quot;${f%.*}&amp;quot;&lt;br /&gt;
     tmpdir=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Extraction de : $f&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     case &amp;quot;${f##*.}&amp;quot; in&lt;br /&gt;
         7z)&lt;br /&gt;
             if ! 7z x -y &amp;quot;$f&amp;quot; -o&amp;quot;$tmpdir&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         zip)&lt;br /&gt;
             if ! unzip -q &amp;quot;$f&amp;quot; -d &amp;quot;$tmpdir&amp;quot;; then&lt;br /&gt;
                 echo &amp;quot;Erreur extraction : $f&amp;quot;&lt;br /&gt;
                 rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
                 continue&lt;br /&gt;
             fi&lt;br /&gt;
             ;;&lt;br /&gt;
         *)&lt;br /&gt;
             echo &amp;quot;Extension non supportée : $f&amp;quot;&lt;br /&gt;
             rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
             continue&lt;br /&gt;
             ;;&lt;br /&gt;
     esac&lt;br /&gt;
 &lt;br /&gt;
     cue=&amp;quot;$(find &amp;quot;$tmpdir&amp;quot; -type f -iname &amp;quot;*.cue&amp;quot; | head -n 1)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
     if [ -z &amp;quot;$cue&amp;quot; ]; then&lt;br /&gt;
         echo &amp;quot;Aucun .cue trouvé dans : $f&amp;quot;&lt;br /&gt;
         rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
         continue&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     echo &amp;quot;==&amp;gt; Conversion en CHD : $name.chd&amp;quot;&lt;br /&gt;
     if chdman createcd -i &amp;quot;$cue&amp;quot; -o &amp;quot;${name}.chd&amp;quot;; then&lt;br /&gt;
         echo &amp;quot;OK : ${name}.chd&amp;quot;&lt;br /&gt;
         rm -f -- &amp;quot;$f&amp;quot;&lt;br /&gt;
     else&lt;br /&gt;
         echo &amp;quot;Erreur conversion : $f&amp;quot;&lt;br /&gt;
     fi&lt;br /&gt;
 &lt;br /&gt;
     rm -rf &amp;quot;$tmpdir&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
 # chmod +x convertchd.sh&lt;br /&gt;
Puis il suffit de lancer le script :&lt;br /&gt;
 # ./convertchd.sh&lt;br /&gt;
= trier les roms par langues, régions.. (à revérifier/tester) =&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
 # apt install git python3 pip python3.13-venv python3-pip&lt;br /&gt;
 # mkdir /opt/triederoms&lt;br /&gt;
 # cd /opt/triederoms&lt;br /&gt;
 # python3 -m venv retool-env&lt;br /&gt;
 # source retool-env/bin/activate&lt;br /&gt;
 # python -m pip install --upgrade pip&lt;br /&gt;
 # pip install alive-progress darkdetect lxml psutil pyside6 strictyaml validators&lt;br /&gt;
 # git clone https://github.com/unexpectedpanda/retool.git&lt;br /&gt;
 # cd retool&lt;br /&gt;
 # source ../retool-env/bin/activate&lt;br /&gt;
On organise le trie avec pour exemple :&lt;br /&gt;
 # nano config/user-config.yaml&lt;br /&gt;
&lt;br /&gt;
 ---&lt;br /&gt;
 config version: 2.4.0&lt;br /&gt;
 &lt;br /&gt;
 # If the -l option is used, only include titles with the following languages.&lt;br /&gt;
 language order:&lt;br /&gt;
 - French&lt;br /&gt;
 - English&lt;br /&gt;
 &lt;br /&gt;
 region order:&lt;br /&gt;
 - France&lt;br /&gt;
 - Europe&lt;br /&gt;
 - World&lt;br /&gt;
 - USA&lt;br /&gt;
 - Japan&lt;br /&gt;
 &lt;br /&gt;
 video order:&lt;br /&gt;
 - PAL&lt;br /&gt;
 - PAL 60Hz&lt;br /&gt;
 - NTSC&lt;br /&gt;
 - MPAL&lt;br /&gt;
 - SECAM&lt;br /&gt;
 &lt;br /&gt;
 list prefix:&lt;br /&gt;
 &lt;br /&gt;
 list suffix:&lt;br /&gt;
 &lt;br /&gt;
 exclude:&lt;br /&gt;
 &lt;br /&gt;
 include:&lt;br /&gt;
 &lt;br /&gt;
 filters:&lt;br /&gt;
On récupère le ficher .dat correspondant à la plateforme à trier [http://redump.org/downloads/ à cette adresse] puis on lance le trie :&lt;br /&gt;
 # python retool.py &amp;lt;font color = blue&amp;gt;maplateforme&amp;lt;/font&amp;gt;.dat --exclude a B d D m M o P r u v --output .&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3244</id>
		<title>Gowstream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3244"/>
		<updated>2026-03-21T15:59:20Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Montage utile pour Pegasus */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= LXC Debian =&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html Source]&lt;br /&gt;
== Prérequis ==&lt;br /&gt;
* LXC avec Privilèges et Nesting&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Virtual devices support ===&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html#_virtual_devices_support Source]&lt;br /&gt;
&lt;br /&gt;
Depuis le serveur :&lt;br /&gt;
 # vi /etc/udev/rules.d/85-wolf-virtual-inputs.rules&lt;br /&gt;
&lt;br /&gt;
 # Allows Wolf to acces /dev/uinput (only needed for joypad support)&lt;br /&gt;
 KERNEL==&amp;quot;uinput&amp;quot;, SUBSYSTEM==&amp;quot;misc&amp;quot;, MODE=&amp;quot;0660&amp;quot;, GROUP=&amp;quot;input&amp;quot;, OPTIONS+=&amp;quot;static_node=uinput&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Allows Wolf to access /dev/uhid (only needed for DualSense emulation)&lt;br /&gt;
 KERNEL==&amp;quot;uhid&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Joypads&lt;br /&gt;
 KERNEL==&amp;quot;hidraw*&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf X-Box One (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf gamepad (virtual) motion sensors&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf Nintendo (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On redémarre le serveur ou l&#039;on charge la configuration avec la commande :&lt;br /&gt;
 # udevadm control --reload-rules &amp;amp;&amp;amp; udevadm trigger&lt;br /&gt;
== NVidia ==&lt;br /&gt;
Après création du conteneur, depuis le serveur :&lt;br /&gt;
 # vi /etc/pve/lxc/&amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt;.conf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 dev0: /dev/uinput&lt;br /&gt;
 dev1: /dev/uhid&lt;br /&gt;
 dev2: /dev/nvidia0&lt;br /&gt;
 dev3: /dev/nvidiactl&lt;br /&gt;
 dev4: /dev/nvidia-modeset&lt;br /&gt;
 dev5: /dev/nvidia-uvm&lt;br /&gt;
 dev6: /dev/nvidia-uvm-tools&lt;br /&gt;
 dev7: /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
 dev8: /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
 lxc.cgroup2.devices.allow: a&lt;br /&gt;
 lxc.cap.drop:&lt;br /&gt;
 lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /run/udev mnt/udev none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /dev mnt/dev none bind,optional,create=dir&lt;br /&gt;
On démarre le LXC, ensuite depuis le LXC :&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
On installe le pilote NVidia ([https://lugwiki.stcgrupo.es/index.php?title=GPU_Passthrough référence]), exemple :&lt;br /&gt;
 # bash /opt/nvidia-driver/NVIDIA-Linux-x86_64-&amp;lt;font color = green&amp;gt;590.48.01&amp;lt;/font&amp;gt;.run --no-kernel-module&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch.sh&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch-fbc.sh&lt;br /&gt;
On installe docker et docker-compose :&lt;br /&gt;
 # apt install docker.io docker-compose-plugin curl&lt;br /&gt;
On install le docker NVidia :&lt;br /&gt;
 # cd /opt/&lt;br /&gt;
 # curl &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile | docker build -t gow/nvidia-driver:latest -f - --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) .&lt;br /&gt;
 # docker create --rm --mount source=nvidia-driver-vol,destination=/usr/nvidia gow/nvidia-driver:latest sh&lt;br /&gt;
On installe le conteneur de GOW :&lt;br /&gt;
 # mkdir /opt/gow&lt;br /&gt;
 # cd /opt/gow/&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3&amp;quot;&lt;br /&gt;
 services:&lt;br /&gt;
   wolf:&lt;br /&gt;
     image: ghcr.io/games-on-whales/wolf:stable&lt;br /&gt;
     environment:&lt;br /&gt;
       - NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol&lt;br /&gt;
       #- WOLF_RENDER_NODE=/dev/dri/renderD12X  ##pour multiGPU&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /etc/wolf/:/etc/wolf:rw&lt;br /&gt;
       - /var/run/docker.sock:/var/run/docker.sock:rw&lt;br /&gt;
       - /mnt/udev:/run/udev:rw&lt;br /&gt;
       - /mnt/dev:/dev:rw&lt;br /&gt;
       - nvidia-driver-vol:/usr/nvidia:rw&lt;br /&gt;
     devices:&lt;br /&gt;
       - /dev/dri&lt;br /&gt;
       - /dev/uinput&lt;br /&gt;
       - /dev/uhid&lt;br /&gt;
       - /dev/nvidia-uvm&lt;br /&gt;
       - /dev/nvidia-uvm-tools&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
       - /dev/nvidiactl&lt;br /&gt;
       - /dev/nvidia0&lt;br /&gt;
       - /dev/nvidia-modeset&lt;br /&gt;
     device_cgroup_rules:&lt;br /&gt;
       - &#039;c 13:* rmw&#039;&lt;br /&gt;
     network_mode: host&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   nvidia-driver-vol:&lt;br /&gt;
     external: true&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= Autoriser client =&lt;br /&gt;
Lors de la première tentative de connexion d&#039;un client il faut rentrer le pin dans un lien web pour l&#039;autoriser, depuis le LXC :&lt;br /&gt;
 # docker ps&lt;br /&gt;
Exemple :&lt;br /&gt;
 CONTAINER ID   IMAGE                                       COMMAND            CREATED          STATUS          PORTS      NAMES&lt;br /&gt;
 c5e43d2fc724   ghcr.io/games-on-whales/pulseaudio:master   &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes   4713/tcp   WolfPulseAudio&lt;br /&gt;
 c581ab5d8827   ghcr.io/games-on-whales/wolf:stable         &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes              &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # docker logs &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt; Insert pin at &amp;lt;font color = blue&amp;gt;&amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;192.168.1.123:47989/pin/#EXEMPLE&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Par défaut :&lt;br /&gt;
 WOLF_CFG_FILE=/etc/wolf/cfg/config.toml&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
== Monter un dossier dans une app ==&lt;br /&gt;
Exemple avec Pegasus : ([https://games-on-whales.github.io/wildlife/apps/pegasus/ source])&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
Modifier :&lt;br /&gt;
&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;[[profiles.apps]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     icon_png_path = &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;games-on-whales.github.io/wildlife/apps/pegasus/assets/icon.png&#039;&lt;br /&gt;
     start_virtual_compositor = true&lt;br /&gt;
     title = &#039;Pegasus&#039;&lt;br /&gt;
 &lt;br /&gt;
         [profiles.apps.runner]&lt;br /&gt;
         base_create_json = &#039;&#039;&#039;{&lt;br /&gt;
   &amp;quot;HostConfig&amp;quot;: {&lt;br /&gt;
     &amp;quot;IpcMode&amp;quot;: &amp;quot;host&amp;quot;,&lt;br /&gt;
     &amp;quot;CapAdd&amp;quot;: [&amp;quot;NET_RAW&amp;quot;, &amp;quot;MKNOD&amp;quot;, &amp;quot;NET_ADMIN&amp;quot;, &amp;quot;SYS_ADMIN&amp;quot;, &amp;quot;SYS_NICE&amp;quot;],&lt;br /&gt;
     &amp;quot;Privileged&amp;quot;: false,&lt;br /&gt;
     &amp;quot;DeviceCgroupRules&amp;quot;: [&amp;quot;c 13:* rmw&amp;quot;, &amp;quot;c 244:* rmw&amp;quot;]&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 &#039;&#039;&#039;&lt;br /&gt;
         devices = []&lt;br /&gt;
         env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
         image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&lt;br /&gt;
         &amp;lt;font color = green&amp;gt;mounts = []&amp;lt;/font&amp;gt;&lt;br /&gt;
         name = &#039;WolfPegasus&#039;&lt;br /&gt;
         ports = []&lt;br /&gt;
         type = &#039;docker&#039;&lt;br /&gt;
Par, exemple :&lt;br /&gt;
&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
          devices = []&lt;br /&gt;
          env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
          image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = blue&amp;gt;mounts = [&lt;br /&gt;
              &amp;quot;/mnt/Emulation/resources:/assets/romm/resources:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# exemple de compatibilite avec assets ROMM&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# pack de bios pour retroarch&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/roms:/ROMs:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;&amp;lt;font color = black&amp;gt;# roms dans le dossier par default, a ajouter dans le menu de Pegasus pour certains dossiers..&amp;lt;/font&amp;gt;&lt;br /&gt;
          ]&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;name = &#039;WolfPegasus&#039;&lt;br /&gt;
          ports = []&lt;br /&gt;
          ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On redémarre le conteneur :&lt;br /&gt;
 # docker restart opt-wolf-1&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter que contrairement à ROMM Retroarch réclame d&#039;avoir tous les bios dans le même dossier.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Montage utile pour Pegasus ==&lt;br /&gt;
Installer des bin d&#039;émulateur :&lt;br /&gt;
 &amp;quot;/opt/emulator:/opt/emulator:rw&amp;quot;,&lt;br /&gt;
Artwork pour Game and Watch (mame) ( exemple avec &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:ro&amp;quot;) :&lt;br /&gt;
 # mkdir -p &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios/retroarchbioses&amp;lt;/font&amp;gt;/mame&lt;br /&gt;
 # ln -s &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms/&amp;lt;/font&amp;gt;g-and-w_artwork &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios/retroarchbioses&amp;lt;/font&amp;gt;/mame/artwork&lt;br /&gt;
&lt;br /&gt;
= Importer un thème dans Pegasus =&lt;br /&gt;
 # cd /etc/wolf/profile-data/&amp;lt;font color = blue&amp;gt;user&amp;lt;/font&amp;gt;/WolfPegasus/.config/pegasus-frontend/themes/ &lt;br /&gt;
Exemple avec &amp;quot;gameOS&amp;quot; (normalement déjà présent..) :&lt;br /&gt;
 # wget &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/PlayingKarrde/gameOS/archive/master.zip&lt;br /&gt;
 # unzip master.zip&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
Voir [[ROMM#convertir_les_gamelist.xml_en_metadata.txt|ce lien]]&lt;br /&gt;
&lt;br /&gt;
= Moonlight Wev Stream =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream Source]&lt;br /&gt;
== Docker ==&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
=== Auhtentification ===&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
== vhost ==&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3243</id>
		<title>Gowstream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3243"/>
		<updated>2026-03-21T15:58:58Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Montage utile pour Pegasus */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= LXC Debian =&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html Source]&lt;br /&gt;
== Prérequis ==&lt;br /&gt;
* LXC avec Privilèges et Nesting&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Virtual devices support ===&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html#_virtual_devices_support Source]&lt;br /&gt;
&lt;br /&gt;
Depuis le serveur :&lt;br /&gt;
 # vi /etc/udev/rules.d/85-wolf-virtual-inputs.rules&lt;br /&gt;
&lt;br /&gt;
 # Allows Wolf to acces /dev/uinput (only needed for joypad support)&lt;br /&gt;
 KERNEL==&amp;quot;uinput&amp;quot;, SUBSYSTEM==&amp;quot;misc&amp;quot;, MODE=&amp;quot;0660&amp;quot;, GROUP=&amp;quot;input&amp;quot;, OPTIONS+=&amp;quot;static_node=uinput&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Allows Wolf to access /dev/uhid (only needed for DualSense emulation)&lt;br /&gt;
 KERNEL==&amp;quot;uhid&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Joypads&lt;br /&gt;
 KERNEL==&amp;quot;hidraw*&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf X-Box One (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf gamepad (virtual) motion sensors&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf Nintendo (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On redémarre le serveur ou l&#039;on charge la configuration avec la commande :&lt;br /&gt;
 # udevadm control --reload-rules &amp;amp;&amp;amp; udevadm trigger&lt;br /&gt;
== NVidia ==&lt;br /&gt;
Après création du conteneur, depuis le serveur :&lt;br /&gt;
 # vi /etc/pve/lxc/&amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt;.conf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 dev0: /dev/uinput&lt;br /&gt;
 dev1: /dev/uhid&lt;br /&gt;
 dev2: /dev/nvidia0&lt;br /&gt;
 dev3: /dev/nvidiactl&lt;br /&gt;
 dev4: /dev/nvidia-modeset&lt;br /&gt;
 dev5: /dev/nvidia-uvm&lt;br /&gt;
 dev6: /dev/nvidia-uvm-tools&lt;br /&gt;
 dev7: /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
 dev8: /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
 lxc.cgroup2.devices.allow: a&lt;br /&gt;
 lxc.cap.drop:&lt;br /&gt;
 lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /run/udev mnt/udev none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /dev mnt/dev none bind,optional,create=dir&lt;br /&gt;
On démarre le LXC, ensuite depuis le LXC :&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
On installe le pilote NVidia ([https://lugwiki.stcgrupo.es/index.php?title=GPU_Passthrough référence]), exemple :&lt;br /&gt;
 # bash /opt/nvidia-driver/NVIDIA-Linux-x86_64-&amp;lt;font color = green&amp;gt;590.48.01&amp;lt;/font&amp;gt;.run --no-kernel-module&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch.sh&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch-fbc.sh&lt;br /&gt;
On installe docker et docker-compose :&lt;br /&gt;
 # apt install docker.io docker-compose-plugin curl&lt;br /&gt;
On install le docker NVidia :&lt;br /&gt;
 # cd /opt/&lt;br /&gt;
 # curl &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile | docker build -t gow/nvidia-driver:latest -f - --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) .&lt;br /&gt;
 # docker create --rm --mount source=nvidia-driver-vol,destination=/usr/nvidia gow/nvidia-driver:latest sh&lt;br /&gt;
On installe le conteneur de GOW :&lt;br /&gt;
 # mkdir /opt/gow&lt;br /&gt;
 # cd /opt/gow/&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3&amp;quot;&lt;br /&gt;
 services:&lt;br /&gt;
   wolf:&lt;br /&gt;
     image: ghcr.io/games-on-whales/wolf:stable&lt;br /&gt;
     environment:&lt;br /&gt;
       - NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol&lt;br /&gt;
       #- WOLF_RENDER_NODE=/dev/dri/renderD12X  ##pour multiGPU&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /etc/wolf/:/etc/wolf:rw&lt;br /&gt;
       - /var/run/docker.sock:/var/run/docker.sock:rw&lt;br /&gt;
       - /mnt/udev:/run/udev:rw&lt;br /&gt;
       - /mnt/dev:/dev:rw&lt;br /&gt;
       - nvidia-driver-vol:/usr/nvidia:rw&lt;br /&gt;
     devices:&lt;br /&gt;
       - /dev/dri&lt;br /&gt;
       - /dev/uinput&lt;br /&gt;
       - /dev/uhid&lt;br /&gt;
       - /dev/nvidia-uvm&lt;br /&gt;
       - /dev/nvidia-uvm-tools&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
       - /dev/nvidiactl&lt;br /&gt;
       - /dev/nvidia0&lt;br /&gt;
       - /dev/nvidia-modeset&lt;br /&gt;
     device_cgroup_rules:&lt;br /&gt;
       - &#039;c 13:* rmw&#039;&lt;br /&gt;
     network_mode: host&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   nvidia-driver-vol:&lt;br /&gt;
     external: true&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= Autoriser client =&lt;br /&gt;
Lors de la première tentative de connexion d&#039;un client il faut rentrer le pin dans un lien web pour l&#039;autoriser, depuis le LXC :&lt;br /&gt;
 # docker ps&lt;br /&gt;
Exemple :&lt;br /&gt;
 CONTAINER ID   IMAGE                                       COMMAND            CREATED          STATUS          PORTS      NAMES&lt;br /&gt;
 c5e43d2fc724   ghcr.io/games-on-whales/pulseaudio:master   &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes   4713/tcp   WolfPulseAudio&lt;br /&gt;
 c581ab5d8827   ghcr.io/games-on-whales/wolf:stable         &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes              &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # docker logs &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt; Insert pin at &amp;lt;font color = blue&amp;gt;&amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;192.168.1.123:47989/pin/#EXEMPLE&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Par défaut :&lt;br /&gt;
 WOLF_CFG_FILE=/etc/wolf/cfg/config.toml&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
== Monter un dossier dans une app ==&lt;br /&gt;
Exemple avec Pegasus : ([https://games-on-whales.github.io/wildlife/apps/pegasus/ source])&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
Modifier :&lt;br /&gt;
&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;[[profiles.apps]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     icon_png_path = &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;games-on-whales.github.io/wildlife/apps/pegasus/assets/icon.png&#039;&lt;br /&gt;
     start_virtual_compositor = true&lt;br /&gt;
     title = &#039;Pegasus&#039;&lt;br /&gt;
 &lt;br /&gt;
         [profiles.apps.runner]&lt;br /&gt;
         base_create_json = &#039;&#039;&#039;{&lt;br /&gt;
   &amp;quot;HostConfig&amp;quot;: {&lt;br /&gt;
     &amp;quot;IpcMode&amp;quot;: &amp;quot;host&amp;quot;,&lt;br /&gt;
     &amp;quot;CapAdd&amp;quot;: [&amp;quot;NET_RAW&amp;quot;, &amp;quot;MKNOD&amp;quot;, &amp;quot;NET_ADMIN&amp;quot;, &amp;quot;SYS_ADMIN&amp;quot;, &amp;quot;SYS_NICE&amp;quot;],&lt;br /&gt;
     &amp;quot;Privileged&amp;quot;: false,&lt;br /&gt;
     &amp;quot;DeviceCgroupRules&amp;quot;: [&amp;quot;c 13:* rmw&amp;quot;, &amp;quot;c 244:* rmw&amp;quot;]&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 &#039;&#039;&#039;&lt;br /&gt;
         devices = []&lt;br /&gt;
         env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
         image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&lt;br /&gt;
         &amp;lt;font color = green&amp;gt;mounts = []&amp;lt;/font&amp;gt;&lt;br /&gt;
         name = &#039;WolfPegasus&#039;&lt;br /&gt;
         ports = []&lt;br /&gt;
         type = &#039;docker&#039;&lt;br /&gt;
Par, exemple :&lt;br /&gt;
&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
          devices = []&lt;br /&gt;
          env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
          image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = blue&amp;gt;mounts = [&lt;br /&gt;
              &amp;quot;/mnt/Emulation/resources:/assets/romm/resources:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# exemple de compatibilite avec assets ROMM&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# pack de bios pour retroarch&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/roms:/ROMs:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;&amp;lt;font color = black&amp;gt;# roms dans le dossier par default, a ajouter dans le menu de Pegasus pour certains dossiers..&amp;lt;/font&amp;gt;&lt;br /&gt;
          ]&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;name = &#039;WolfPegasus&#039;&lt;br /&gt;
          ports = []&lt;br /&gt;
          ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On redémarre le conteneur :&lt;br /&gt;
 # docker restart opt-wolf-1&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter que contrairement à ROMM Retroarch réclame d&#039;avoir tous les bios dans le même dossier.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Montage utile pour Pegasus ==&lt;br /&gt;
Installer des bin d&#039;émulateur :&lt;br /&gt;
 &amp;quot;/opt/emulator:/opt/emulator:rw&amp;quot;,&lt;br /&gt;
Artwork pour Game and Watch (mame) ( exemple avec &amp;quot;&amp;quot;) :&lt;br /&gt;
 # mkdir -p &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios/retroarchbioses&amp;lt;/font&amp;gt;/mame&lt;br /&gt;
 # ln -s &amp;lt;font color = blue&amp;gt;/mnt/Emulation/roms/&amp;lt;/font&amp;gt;g-and-w_artwork &amp;lt;font color = blue&amp;gt;/mnt/Emulation/bios/retroarchbioses&amp;lt;/font&amp;gt;/mame/artwork&lt;br /&gt;
&lt;br /&gt;
= Importer un thème dans Pegasus =&lt;br /&gt;
 # cd /etc/wolf/profile-data/&amp;lt;font color = blue&amp;gt;user&amp;lt;/font&amp;gt;/WolfPegasus/.config/pegasus-frontend/themes/ &lt;br /&gt;
Exemple avec &amp;quot;gameOS&amp;quot; (normalement déjà présent..) :&lt;br /&gt;
 # wget &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/PlayingKarrde/gameOS/archive/master.zip&lt;br /&gt;
 # unzip master.zip&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
Voir [[ROMM#convertir_les_gamelist.xml_en_metadata.txt|ce lien]]&lt;br /&gt;
&lt;br /&gt;
= Moonlight Wev Stream =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream Source]&lt;br /&gt;
== Docker ==&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
=== Auhtentification ===&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
== vhost ==&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3242</id>
		<title>Gowstream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3242"/>
		<updated>2026-03-21T15:24:04Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Montage utile pour Pegasus */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= LXC Debian =&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html Source]&lt;br /&gt;
== Prérequis ==&lt;br /&gt;
* LXC avec Privilèges et Nesting&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Virtual devices support ===&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html#_virtual_devices_support Source]&lt;br /&gt;
&lt;br /&gt;
Depuis le serveur :&lt;br /&gt;
 # vi /etc/udev/rules.d/85-wolf-virtual-inputs.rules&lt;br /&gt;
&lt;br /&gt;
 # Allows Wolf to acces /dev/uinput (only needed for joypad support)&lt;br /&gt;
 KERNEL==&amp;quot;uinput&amp;quot;, SUBSYSTEM==&amp;quot;misc&amp;quot;, MODE=&amp;quot;0660&amp;quot;, GROUP=&amp;quot;input&amp;quot;, OPTIONS+=&amp;quot;static_node=uinput&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Allows Wolf to access /dev/uhid (only needed for DualSense emulation)&lt;br /&gt;
 KERNEL==&amp;quot;uhid&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Joypads&lt;br /&gt;
 KERNEL==&amp;quot;hidraw*&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf X-Box One (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf gamepad (virtual) motion sensors&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf Nintendo (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On redémarre le serveur ou l&#039;on charge la configuration avec la commande :&lt;br /&gt;
 # udevadm control --reload-rules &amp;amp;&amp;amp; udevadm trigger&lt;br /&gt;
== NVidia ==&lt;br /&gt;
Après création du conteneur, depuis le serveur :&lt;br /&gt;
 # vi /etc/pve/lxc/&amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt;.conf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 dev0: /dev/uinput&lt;br /&gt;
 dev1: /dev/uhid&lt;br /&gt;
 dev2: /dev/nvidia0&lt;br /&gt;
 dev3: /dev/nvidiactl&lt;br /&gt;
 dev4: /dev/nvidia-modeset&lt;br /&gt;
 dev5: /dev/nvidia-uvm&lt;br /&gt;
 dev6: /dev/nvidia-uvm-tools&lt;br /&gt;
 dev7: /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
 dev8: /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
 lxc.cgroup2.devices.allow: a&lt;br /&gt;
 lxc.cap.drop:&lt;br /&gt;
 lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /run/udev mnt/udev none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /dev mnt/dev none bind,optional,create=dir&lt;br /&gt;
On démarre le LXC, ensuite depuis le LXC :&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
On installe le pilote NVidia ([https://lugwiki.stcgrupo.es/index.php?title=GPU_Passthrough référence]), exemple :&lt;br /&gt;
 # bash /opt/nvidia-driver/NVIDIA-Linux-x86_64-&amp;lt;font color = green&amp;gt;590.48.01&amp;lt;/font&amp;gt;.run --no-kernel-module&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch.sh&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch-fbc.sh&lt;br /&gt;
On installe docker et docker-compose :&lt;br /&gt;
 # apt install docker.io docker-compose-plugin curl&lt;br /&gt;
On install le docker NVidia :&lt;br /&gt;
 # cd /opt/&lt;br /&gt;
 # curl &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile | docker build -t gow/nvidia-driver:latest -f - --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) .&lt;br /&gt;
 # docker create --rm --mount source=nvidia-driver-vol,destination=/usr/nvidia gow/nvidia-driver:latest sh&lt;br /&gt;
On installe le conteneur de GOW :&lt;br /&gt;
 # mkdir /opt/gow&lt;br /&gt;
 # cd /opt/gow/&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3&amp;quot;&lt;br /&gt;
 services:&lt;br /&gt;
   wolf:&lt;br /&gt;
     image: ghcr.io/games-on-whales/wolf:stable&lt;br /&gt;
     environment:&lt;br /&gt;
       - NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol&lt;br /&gt;
       #- WOLF_RENDER_NODE=/dev/dri/renderD12X  ##pour multiGPU&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /etc/wolf/:/etc/wolf:rw&lt;br /&gt;
       - /var/run/docker.sock:/var/run/docker.sock:rw&lt;br /&gt;
       - /mnt/udev:/run/udev:rw&lt;br /&gt;
       - /mnt/dev:/dev:rw&lt;br /&gt;
       - nvidia-driver-vol:/usr/nvidia:rw&lt;br /&gt;
     devices:&lt;br /&gt;
       - /dev/dri&lt;br /&gt;
       - /dev/uinput&lt;br /&gt;
       - /dev/uhid&lt;br /&gt;
       - /dev/nvidia-uvm&lt;br /&gt;
       - /dev/nvidia-uvm-tools&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
       - /dev/nvidiactl&lt;br /&gt;
       - /dev/nvidia0&lt;br /&gt;
       - /dev/nvidia-modeset&lt;br /&gt;
     device_cgroup_rules:&lt;br /&gt;
       - &#039;c 13:* rmw&#039;&lt;br /&gt;
     network_mode: host&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   nvidia-driver-vol:&lt;br /&gt;
     external: true&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= Autoriser client =&lt;br /&gt;
Lors de la première tentative de connexion d&#039;un client il faut rentrer le pin dans un lien web pour l&#039;autoriser, depuis le LXC :&lt;br /&gt;
 # docker ps&lt;br /&gt;
Exemple :&lt;br /&gt;
 CONTAINER ID   IMAGE                                       COMMAND            CREATED          STATUS          PORTS      NAMES&lt;br /&gt;
 c5e43d2fc724   ghcr.io/games-on-whales/pulseaudio:master   &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes   4713/tcp   WolfPulseAudio&lt;br /&gt;
 c581ab5d8827   ghcr.io/games-on-whales/wolf:stable         &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes              &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # docker logs &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt; Insert pin at &amp;lt;font color = blue&amp;gt;&amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;192.168.1.123:47989/pin/#EXEMPLE&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Par défaut :&lt;br /&gt;
 WOLF_CFG_FILE=/etc/wolf/cfg/config.toml&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
== Monter un dossier dans une app ==&lt;br /&gt;
Exemple avec Pegasus : ([https://games-on-whales.github.io/wildlife/apps/pegasus/ source])&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
Modifier :&lt;br /&gt;
&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;[[profiles.apps]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     icon_png_path = &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;games-on-whales.github.io/wildlife/apps/pegasus/assets/icon.png&#039;&lt;br /&gt;
     start_virtual_compositor = true&lt;br /&gt;
     title = &#039;Pegasus&#039;&lt;br /&gt;
 &lt;br /&gt;
         [profiles.apps.runner]&lt;br /&gt;
         base_create_json = &#039;&#039;&#039;{&lt;br /&gt;
   &amp;quot;HostConfig&amp;quot;: {&lt;br /&gt;
     &amp;quot;IpcMode&amp;quot;: &amp;quot;host&amp;quot;,&lt;br /&gt;
     &amp;quot;CapAdd&amp;quot;: [&amp;quot;NET_RAW&amp;quot;, &amp;quot;MKNOD&amp;quot;, &amp;quot;NET_ADMIN&amp;quot;, &amp;quot;SYS_ADMIN&amp;quot;, &amp;quot;SYS_NICE&amp;quot;],&lt;br /&gt;
     &amp;quot;Privileged&amp;quot;: false,&lt;br /&gt;
     &amp;quot;DeviceCgroupRules&amp;quot;: [&amp;quot;c 13:* rmw&amp;quot;, &amp;quot;c 244:* rmw&amp;quot;]&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 &#039;&#039;&#039;&lt;br /&gt;
         devices = []&lt;br /&gt;
         env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
         image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&lt;br /&gt;
         &amp;lt;font color = green&amp;gt;mounts = []&amp;lt;/font&amp;gt;&lt;br /&gt;
         name = &#039;WolfPegasus&#039;&lt;br /&gt;
         ports = []&lt;br /&gt;
         type = &#039;docker&#039;&lt;br /&gt;
Par, exemple :&lt;br /&gt;
&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
          devices = []&lt;br /&gt;
          env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
          image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = blue&amp;gt;mounts = [&lt;br /&gt;
              &amp;quot;/mnt/Emulation/resources:/assets/romm/resources:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# exemple de compatibilite avec assets ROMM&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# pack de bios pour retroarch&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/roms:/ROMs:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;&amp;lt;font color = black&amp;gt;# roms dans le dossier par default, a ajouter dans le menu de Pegasus pour certains dossiers..&amp;lt;/font&amp;gt;&lt;br /&gt;
          ]&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;name = &#039;WolfPegasus&#039;&lt;br /&gt;
          ports = []&lt;br /&gt;
          ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On redémarre le conteneur :&lt;br /&gt;
 # docker restart opt-wolf-1&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter que contrairement à ROMM Retroarch réclame d&#039;avoir tous les bios dans le même dossier.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Montage utile pour Pegasus ==&lt;br /&gt;
Installer des bin d&#039;émulateur :&lt;br /&gt;
 &amp;quot;/opt/emulator:/opt/emulator:rw&amp;quot;,&lt;br /&gt;
Artwork pour Game ans Watch (mame) :&lt;br /&gt;
 &amp;quot;/mnt/Emulation/roms/g-and-w_artwork:/home/retro/bioses/mame/artwork:ro&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
= Importer un thème dans Pegasus =&lt;br /&gt;
 # cd /etc/wolf/profile-data/&amp;lt;font color = blue&amp;gt;user&amp;lt;/font&amp;gt;/WolfPegasus/.config/pegasus-frontend/themes/ &lt;br /&gt;
Exemple avec &amp;quot;gameOS&amp;quot; (normalement déjà présent..) :&lt;br /&gt;
 # wget &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/PlayingKarrde/gameOS/archive/master.zip&lt;br /&gt;
 # unzip master.zip&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
Voir [[ROMM#convertir_les_gamelist.xml_en_metadata.txt|ce lien]]&lt;br /&gt;
&lt;br /&gt;
= Moonlight Wev Stream =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream Source]&lt;br /&gt;
== Docker ==&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
=== Auhtentification ===&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
== vhost ==&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3241</id>
		<title>Gowstream</title>
		<link rel="alternate" type="text/html" href="https://lugwiki.stcgrupo.es/index.php?title=Gowstream&amp;diff=3241"/>
		<updated>2026-03-21T15:23:14Z</updated>

		<summary type="html">&lt;p&gt;Admin : /* Monter un dossier dans une app */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= LXC Debian =&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html Source]&lt;br /&gt;
== Prérequis ==&lt;br /&gt;
* LXC avec Privilèges et Nesting&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Virtual devices support ===&lt;br /&gt;
[https://games-on-whales.github.io/wolf/stable/user/quickstart.html#_virtual_devices_support Source]&lt;br /&gt;
&lt;br /&gt;
Depuis le serveur :&lt;br /&gt;
 # vi /etc/udev/rules.d/85-wolf-virtual-inputs.rules&lt;br /&gt;
&lt;br /&gt;
 # Allows Wolf to acces /dev/uinput (only needed for joypad support)&lt;br /&gt;
 KERNEL==&amp;quot;uinput&amp;quot;, SUBSYSTEM==&amp;quot;misc&amp;quot;, MODE=&amp;quot;0660&amp;quot;, GROUP=&amp;quot;input&amp;quot;, OPTIONS+=&amp;quot;static_node=uinput&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Allows Wolf to access /dev/uhid (only needed for DualSense emulation)&lt;br /&gt;
 KERNEL==&amp;quot;uhid&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # Joypads&lt;br /&gt;
 KERNEL==&amp;quot;hidraw*&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, GROUP=&amp;quot;input&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf X-Box One (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf PS5 (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf gamepad (virtual) motion sensors&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
 SUBSYSTEMS==&amp;quot;input&amp;quot;, ATTRS{name}==&amp;quot;Wolf Nintendo (virtual) pad&amp;quot;, MODE=&amp;quot;0660&amp;quot;, ENV{ID_SEAT}=&amp;quot;seat9&amp;quot;&lt;br /&gt;
&lt;br /&gt;
On redémarre le serveur ou l&#039;on charge la configuration avec la commande :&lt;br /&gt;
 # udevadm control --reload-rules &amp;amp;&amp;amp; udevadm trigger&lt;br /&gt;
== NVidia ==&lt;br /&gt;
Après création du conteneur, depuis le serveur :&lt;br /&gt;
 # vi /etc/pve/lxc/&amp;lt;font color = blue&amp;gt;100&amp;lt;/font&amp;gt;.conf&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 dev0: /dev/uinput&lt;br /&gt;
 dev1: /dev/uhid&lt;br /&gt;
 dev2: /dev/nvidia0&lt;br /&gt;
 dev3: /dev/nvidiactl&lt;br /&gt;
 dev4: /dev/nvidia-modeset&lt;br /&gt;
 dev5: /dev/nvidia-uvm&lt;br /&gt;
 dev6: /dev/nvidia-uvm-tools&lt;br /&gt;
 dev7: /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
 dev8: /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
 lxc.cgroup2.devices.allow: a&lt;br /&gt;
 lxc.cap.drop:&lt;br /&gt;
 lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /run/udev mnt/udev none bind,optional,create=dir&lt;br /&gt;
 lxc.mount.entry: /dev mnt/dev none bind,optional,create=dir&lt;br /&gt;
On démarre le LXC, ensuite depuis le LXC :&lt;br /&gt;
 # apt update &amp;amp;&amp;amp; apt upgrade&lt;br /&gt;
On installe le pilote NVidia ([https://lugwiki.stcgrupo.es/index.php?title=GPU_Passthrough référence]), exemple :&lt;br /&gt;
 # bash /opt/nvidia-driver/NVIDIA-Linux-x86_64-&amp;lt;font color = green&amp;gt;590.48.01&amp;lt;/font&amp;gt;.run --no-kernel-module&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch.sh&lt;br /&gt;
 # bash /opt/nvidia-driver/nvidia-patch/patch-fbc.sh&lt;br /&gt;
On installe docker et docker-compose :&lt;br /&gt;
 # apt install docker.io docker-compose-plugin curl&lt;br /&gt;
On install le docker NVidia :&lt;br /&gt;
 # cd /opt/&lt;br /&gt;
 # curl &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile | docker build -t gow/nvidia-driver:latest -f - --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) .&lt;br /&gt;
 # docker create --rm --mount source=nvidia-driver-vol,destination=/usr/nvidia gow/nvidia-driver:latest sh&lt;br /&gt;
On installe le conteneur de GOW :&lt;br /&gt;
 # mkdir /opt/gow&lt;br /&gt;
 # cd /opt/gow/&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
 version: &amp;quot;3&amp;quot;&lt;br /&gt;
 services:&lt;br /&gt;
   wolf:&lt;br /&gt;
     image: ghcr.io/games-on-whales/wolf:stable&lt;br /&gt;
     environment:&lt;br /&gt;
       - NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol&lt;br /&gt;
       #- WOLF_RENDER_NODE=/dev/dri/renderD12X  ##pour multiGPU&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /etc/wolf/:/etc/wolf:rw&lt;br /&gt;
       - /var/run/docker.sock:/var/run/docker.sock:rw&lt;br /&gt;
       - /mnt/udev:/run/udev:rw&lt;br /&gt;
       - /mnt/dev:/dev:rw&lt;br /&gt;
       - nvidia-driver-vol:/usr/nvidia:rw&lt;br /&gt;
     devices:&lt;br /&gt;
       - /dev/dri&lt;br /&gt;
       - /dev/uinput&lt;br /&gt;
       - /dev/uhid&lt;br /&gt;
       - /dev/nvidia-uvm&lt;br /&gt;
       - /dev/nvidia-uvm-tools&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap1&lt;br /&gt;
       - /dev/nvidia-caps/nvidia-cap2&lt;br /&gt;
       - /dev/nvidiactl&lt;br /&gt;
       - /dev/nvidia0&lt;br /&gt;
       - /dev/nvidia-modeset&lt;br /&gt;
     device_cgroup_rules:&lt;br /&gt;
       - &#039;c 13:* rmw&#039;&lt;br /&gt;
     network_mode: host&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
 &lt;br /&gt;
 volumes:&lt;br /&gt;
   nvidia-driver-vol:&lt;br /&gt;
     external: true&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
&lt;br /&gt;
= Autoriser client =&lt;br /&gt;
Lors de la première tentative de connexion d&#039;un client il faut rentrer le pin dans un lien web pour l&#039;autoriser, depuis le LXC :&lt;br /&gt;
 # docker ps&lt;br /&gt;
Exemple :&lt;br /&gt;
 CONTAINER ID   IMAGE                                       COMMAND            CREATED          STATUS          PORTS      NAMES&lt;br /&gt;
 c5e43d2fc724   ghcr.io/games-on-whales/pulseaudio:master   &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes   4713/tcp   WolfPulseAudio&lt;br /&gt;
 c581ab5d8827   ghcr.io/games-on-whales/wolf:stable         &amp;quot;/entrypoint.sh&amp;quot;   29 minutes ago   Up 29 minutes              &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 # docker logs &amp;lt;font color = blue&amp;gt;opt-wolf-1&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt; Insert pin at &amp;lt;font color = blue&amp;gt;&amp;lt;nowiki&amp;gt;http://&amp;lt;/nowiki&amp;gt;192.168.1.123:47989/pin/#EXEMPLE&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Par défaut :&lt;br /&gt;
 WOLF_CFG_FILE=/etc/wolf/cfg/config.toml&lt;br /&gt;
&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
== Monter un dossier dans une app ==&lt;br /&gt;
Exemple avec Pegasus : ([https://games-on-whales.github.io/wildlife/apps/pegasus/ source])&lt;br /&gt;
 # vi /etc/wolf/cfg/config.toml&lt;br /&gt;
Modifier :&lt;br /&gt;
&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;[[profiles.apps]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     icon_png_path = &#039;&amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;games-on-whales.github.io/wildlife/apps/pegasus/assets/icon.png&#039;&lt;br /&gt;
     start_virtual_compositor = true&lt;br /&gt;
     title = &#039;Pegasus&#039;&lt;br /&gt;
 &lt;br /&gt;
         [profiles.apps.runner]&lt;br /&gt;
         base_create_json = &#039;&#039;&#039;{&lt;br /&gt;
   &amp;quot;HostConfig&amp;quot;: {&lt;br /&gt;
     &amp;quot;IpcMode&amp;quot;: &amp;quot;host&amp;quot;,&lt;br /&gt;
     &amp;quot;CapAdd&amp;quot;: [&amp;quot;NET_RAW&amp;quot;, &amp;quot;MKNOD&amp;quot;, &amp;quot;NET_ADMIN&amp;quot;, &amp;quot;SYS_ADMIN&amp;quot;, &amp;quot;SYS_NICE&amp;quot;],&lt;br /&gt;
     &amp;quot;Privileged&amp;quot;: false,&lt;br /&gt;
     &amp;quot;DeviceCgroupRules&amp;quot;: [&amp;quot;c 13:* rmw&amp;quot;, &amp;quot;c 244:* rmw&amp;quot;]&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 &#039;&#039;&#039;&lt;br /&gt;
         devices = []&lt;br /&gt;
         env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
         image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&lt;br /&gt;
         &amp;lt;font color = green&amp;gt;mounts = []&amp;lt;/font&amp;gt;&lt;br /&gt;
         name = &#039;WolfPegasus&#039;&lt;br /&gt;
         ports = []&lt;br /&gt;
         type = &#039;docker&#039;&lt;br /&gt;
Par, exemple :&lt;br /&gt;
&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;...&lt;br /&gt;
          devices = []&lt;br /&gt;
          env = [ &#039;RUN_SWAY=1&#039;, &#039;GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*&#039; ]&lt;br /&gt;
          image = &#039;ghcr.io/games-on-whales/pegasus:edge&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = blue&amp;gt;mounts = [&lt;br /&gt;
              &amp;quot;/mnt/Emulation/resources:/assets/romm/resources:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# exemple de compatibilite avec assets ROMM&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/bios/retroarchbioses:/home/retro/bioses:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;,&amp;lt;font color = black&amp;gt;# pack de bios pour retroarch&amp;lt;/font&amp;gt;&lt;br /&gt;
              &amp;quot;/mnt/Emulation/roms:/ROMs:&amp;lt;font color = red&amp;gt;ro&amp;lt;/font&amp;gt;&amp;quot;&amp;lt;font color = black&amp;gt;# roms dans le dossier par default, a ajouter dans le menu de Pegasus pour certains dossiers..&amp;lt;/font&amp;gt;&lt;br /&gt;
          ]&amp;lt;/font&amp;gt;&lt;br /&gt;
          &amp;lt;font color = grey&amp;gt;name = &#039;WolfPegasus&#039;&lt;br /&gt;
          ports = []&lt;br /&gt;
          ...&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On redémarre le conteneur :&lt;br /&gt;
 # docker restart opt-wolf-1&lt;br /&gt;
{{Méta bandeau&lt;br /&gt;
  | niveau = information&lt;br /&gt;
  | icône = loupe&lt;br /&gt;
  | texte  = A noter que contrairement à ROMM Retroarch réclame d&#039;avoir tous les bios dans le même dossier.&lt;br /&gt;
 }}&lt;br /&gt;
&lt;br /&gt;
== Montage utile pour Pegasus ==&lt;br /&gt;
Installer des bin d&#039;émulateur :&lt;br /&gt;
 &amp;quot;/opt/emulator:/opt/emulator:rw&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
= Importer un thème dans Pegasus =&lt;br /&gt;
 # cd /etc/wolf/profile-data/&amp;lt;font color = blue&amp;gt;user&amp;lt;/font&amp;gt;/WolfPegasus/.config/pegasus-frontend/themes/ &lt;br /&gt;
Exemple avec &amp;quot;gameOS&amp;quot; (normalement déjà présent..) :&lt;br /&gt;
 # wget &amp;lt;nowiki&amp;gt;https://&amp;lt;/nowiki&amp;gt;github.com/PlayingKarrde/gameOS/archive/master.zip&lt;br /&gt;
 # unzip master.zip&lt;br /&gt;
= convertir les gamelist.xml en metadata.txt =&lt;br /&gt;
Voir [[ROMM#convertir_les_gamelist.xml_en_metadata.txt|ce lien]]&lt;br /&gt;
&lt;br /&gt;
= Moonlight Wev Stream =&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream Source]&lt;br /&gt;
== Docker ==&lt;br /&gt;
[https://github.com/MrCreativ3001/moonlight-web-stream/blob/master/docker/README.md Source]&lt;br /&gt;
 # mkdir /opt/moonlightwebstream&lt;br /&gt;
 # cd /opt/moonlightwebstream&lt;br /&gt;
 # vi docker-compose.yml&lt;br /&gt;
&lt;br /&gt;
  services:&lt;br /&gt;
   moonlight-web:&lt;br /&gt;
     image: mrcreativ3001/moonlight-web-stream:latest&lt;br /&gt;
     container_name: moonlight-web&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     ports:&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;8080:8080&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
       - &amp;quot;&amp;lt;font color = green&amp;gt;40000-40100:40000-40100/udp&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
     volumes:&lt;br /&gt;
       - /opt/moonlightwebstream/server:/moonlight-web/server&lt;br /&gt;
&lt;br /&gt;
 # docker compose up -d&lt;br /&gt;
== Configuration ==&lt;br /&gt;
 # vi /opt/moonlightwebstream/server/config.json&lt;br /&gt;
=== Auhtentification ===&lt;br /&gt;
Au minimum renseigner les champs suivant :&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
         &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_utilisateur&amp;lt;/font&amp;gt;&amp;quot;,&lt;br /&gt;
         &amp;quot;credential&amp;quot;: &amp;quot;&amp;lt;font color = blue&amp;gt;un_mot_de_passe&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
         &amp;lt;font color = grey&amp;gt;...&amp;lt;/font&amp;gt;&lt;br /&gt;
== vhost ==&lt;br /&gt;
 server {&lt;br /&gt;
     listen 443 ssl http2;&lt;br /&gt;
     server_name &amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
     ssl_certificate /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/fullchain.pem;&lt;br /&gt;
     ssl_certificate_key /etc/letsencrypt/live/&amp;lt;font color = blue&amp;gt;moonlight.tondomaine.com&amp;lt;/font&amp;gt;/privkey.pem;&lt;br /&gt;
 &lt;br /&gt;
     location /api/host/stream {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.123:8080&amp;lt;/font&amp;gt;/api/host/stream;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
         proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     location / {&lt;br /&gt;
         proxy_pass http://&amp;lt;font color = blue&amp;gt;192.168.1.123:8080&amp;lt;/font&amp;gt;/;&lt;br /&gt;
         proxy_http_version 1.1;&lt;br /&gt;
         proxy_set_header Host $host;&lt;br /&gt;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
         proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
         proxy_read_timeout 86400;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>