MyGo 云音乐
用 Go 与 MyGo 写成的第三方网易云音乐桌面客户端。原生界面,没有 WebView;边下边播,无损与 Hi-Res,逐字歌词与「律动」,刘海播放器。
Translation pending
Générer des carrousels photo pour TikTok à partir d'images d'une personne fictive
Translation pending
AI summary · README
MediaPython · HTMLGitHub @georgescoldOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Générateur local de carrousels photo pour TikTok : 5 images d'une même personne fictive, dans 5 lieux différents, prêtes à recevoir un texte incrusté. Interface web locale, génération via l'API fal. En ligne — https://images-celibataires.vercel.app, protégé par mot de passe. Fonctions Vercel, photos et bibliothèque dans Supabase. En local — start.bat, tout sur la machine. python gen/sauvegarde.py envoie vers Supabase ce qui manque en ligne.Read the full README ↗
Générateur local de carrousels photo pour TikTok : 5 images d'une même personne fictive, dans 5 lieux différents, prêtes à recevoir un texte incrusté.
Interface web locale, génération via l'API fal.
En ligne — https://images-celibataires.vercel.app, protégé par mot de passe. Fonctions Vercel, photos et bibliothèque dans Supabase.
En local — start.bat, tout sur la machine. python gen/sauvegarde.py
envoie vers Supabase ce qui manque en ligne.
Double-clic sur start.bat, ou :
python gen/serveur.py --ouvrir
Puis ouvrir http://127.0.0.1:8420 — et pas localhost : sous Windows,
localhost se résout d'abord en IPv6, le serveur n'écoute qu'en IPv4, et
chaque requête attend deux secondes avant de retomber. Mesuré : 2051 ms
contre 2 ms.
pip install pillow) — start.bat l'installe si besoin.env à la racine :FAL_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
Copier .env.example et y mettre sa clé. Le fichier .env n'est pas versionné.
| Étape | Modèle | Prix |
|---|---|---|
| Photo 1, le hero (texte → image) | fal-ai/flux-2-pro | 0,030 $ |
| Photos 2 à 5 (même visage, image → image) | fal-ai/flux-2-pro/edit | 0,045 $ |
| Contrôle de chaque photo (2 appels) | openrouter/router/vision | 0,001 $ |
0,21 $ le carrousel de 5 photos, environ 80 secondes. Le contrôle ajoute 0,006 $ et 25 secondes ; chaque photo qu'il fait refaire coûte 0,045 $ de plus. Mesuré sur un profil de 58 ans : une photo refaite, soit 0,25 $ au total.
Ces deux modèles ont été retenus après un banc d'essai sur 14 modèles
text-to-image. Prix mesurés sur les autres candidats : flux-2 klein 9B
0,005 $ (le meilleur rapport qualité-prix, ~80 % du résultat pour six fois
moins cher), flux-2 [dev] 0,012 $, mai-image-2.5 0,0395 $,
seedream v5 pro 0,0675 $, nano-banana-2 0,080 $.
Après génération, chaque image passe par un post-traitement local gratuit qui fait autant que le modèle : recadrage en 640×800, léger flou, bruit de capteur, étalonnage doux, horizon de travers et double compression JPEG. C'est ce qui transforme une image IA propre en photo qui sort d'un téléphone.
« + 5 photos » sur la fiche d'une personne repart de son visage plutôt que d'en inventer un autre : sa description, ses signes particuliers et sa photo 1 sont déjà dans sa fiche. Les nouvelles photos reprennent les cinq mêmes textes en boucle — prénom, âge, profession, recherche, appel à commenter — si bien qu'un lot de cinq se poste tel quel comme un second carrousel de la même femme. Environ 0,22 $ le lot.
La référence est l'image brute de la photo 1, pas celle qui est publiée : celle-ci a reçu flou, bruit et double compression, et le modèle d'édition s'en servirait pour dessiner un visage un peu plus flou à chaque fois. En ligne, la photo brute est donc déposée dans le stockage à la génération.
La petite flèche ↻, en haut à droite de chaque photo, refait cette photo-là. Même personne et même décor — le lieu est déjà consommé dans les registres, et c'est lui qui donne sa place à la photo dans le carrousel — mais pose, cadrage, angle, lumière et tenue sont retirés au sort, et la pose ratée est exclue du tirage : la rejouer à l'identique la raterait encore. Environ 0,05 $, pas de confirmation — la flèche est petite, posée sur la photo concernée, et c'est un geste de retouche.
L'exclusion de la pose ratée a longtemps été percée. La fiche range la pose sous sa forme finale — « phone » devenu « camera » en selfie, texte mis au masculin — qui ne correspondait plus au texte de la banque qu'on cherchait à exclure ; et quand toutes les poses d'une scène avaient déjà servi, le tirage repartait de la banque entière, pose ratée comprise. La flèche retirait ainsi la même pose une fois sur cinq pour une femme, deux fois sur cinq pour un homme. Depuis : zéro sur 360 reprises simulées.
La photo 1 se refait depuis elle-même. Elle porte le visage de référence des autres : la repasser en text-to-image donnerait un autre visage, qui ne collerait plus aux suivantes. Le modèle d'édition, lui, garde les traits et redessine la scène. Une fois refaite, sa version brute remplace l'ancienne dans le stockage : les lots suivants repartaient sinon d'un visage que la bibliothèque ne montrait plus.
L'étoile met une personne dans l'onglet « Favoris », qui est une catégorie à
part et non un filtre : on y trouve aussi bien des femmes à poster que des
archivées. Une femme mise à la corbeille en sort. En local les favoris sont une
simple liste dans gen/_favoris.json, ce qui permet d'en marquer une générée
avant l'interface, qui n'a pas de meta.json ; en ligne, c'est une colonne de
la fiche.
Le bouton « Générer le carrousel » ne se bloque plus pendant une génération : on
peut en lancer une deuxième, une troisième, un « + 5 photos » ou des « ↻ » sur
n'importe qui — y compris sur une personne dont un lot tourne déjà — et elles
tournent ensemble. Le panneau « Générations en cours », en
haut de page, les montre toutes d'où qu'elles viennent : lancée sur le
téléphone, une génération apparaît sur l'iPad avec son pourcentage et son étape
(« Photo 3 terminée », « Photo 2 refaite : … »), et chaque appareil voit son
issue — elle reste affichée deux minutes après la fin, et la bibliothèque se
recharge d'elle-même. La liste vient du serveur (/api/job sans identifiant), pas
de la mémoire de l'appareil qui a lancé.
Le pourcentage compte les photos terminées sur le total prévu — 5 pour un profil ou un lot, 1 pour une reprise — et la photo en cours compte pour moitié, sans quoi la barre resterait à zéro pendant toute la première. L'interface relit la liste toutes les 3 secondes quand quelque chose tourne, toutes les 12 sinon, et plus du tout quand l'écran est caché ; elle se remet à jour dès qu'on y revient.
Ce qui rend le parallélisme sûr :
verrous, dont la clé primaire refuse un second preneur, levée d'office
après 30 secondes si la fonction qui la tenait a été tuée. Mesuré en ligne :
quatre créations lancées au même instant, quatre prénoms différents, et les
quatre dans le registre ;L'interface en ligne est faite pour être utilisée au doigt, et vérifiée de 320 px (iPhone SE) à 1440 px : aucun défilement horizontal, aucune cible tactile sous 44 px, aucun texte sous 13 px.
L'atelier s'installe comme une app, sur l'écran d'accueil, en plein écran et avec son icône. Sur Android, Chrome propose l'installation : un bandeau « Installer l'atelier » en bas d'écran déclenche sa vraie boîte d'installation. Sur iPhone et iPad il n'existe aucune invitation qu'une page puisse déclencher — l'installation passe forcément par Partager, puis « Sur l'écran d'accueil » — alors le bandeau explique le geste. Il apparaît dès la page de connexion, c'est-à-dire dès la première ouverture sur le téléphone. Il ne s'affiche ni à la souris (le navigateur propose déjà l'installation dans sa barre d'adresse), ni une fois l'app installée, ni pendant 30 jours après un refus.
Sur iOS, une app installée a ses propres cookies, séparés de ceux de Safari : il faut s'y reconnecter une fois après l'installation, puis tous les 30 jours, durée de la session.
Le service worker (sw.js, à la racine parce qu'il ne contrôle que les pages
situées sous son propre chemin) ne met rien en cache, et c'est voulu : l'atelier
ne sert à rien hors ligne, et l'interface locale est relue à chaque requête pour
être modifiable à chaud — un cache la figerait. Il ne fait que deux choses :
permettre à Chrome d'envoyer son invitation, qui exige un gestionnaire fetch, et
remplacer hors ligne la page d'erreur du navigateur par un message clair — ce qui
compte dans une app installée, où il n'y a plus de barre d'adresse. Les icônes
sortent de python gen/faire_icones.py.
« Télécharger » range la photo dans la galerie — par un chemin différent sur chaque système, parce que ce qui marche sur l'un échoue sur l'autre :
Dans les deux premiers cas, les photos partent dès que le doigt touche le bouton, pas quand il se relève, et viennent directement du CDN du stockage.
« Copier le texte » passe par le presse-papiers moderne et, s'il refuse, par
une zone de texte sélectionnée — sur iOS, select() seul n'y sélectionne rien.
La flèche ↻ reste discrète à la souris jusqu'au survol ; au doigt, où il n'y a pas de survol, elle est toujours visible et à la taille d'un doigt.
Sur téléphone, les onglets se partagent la largeur en trois, compteur sous le libellé ; l'étoile rejoint le nom ; les actions d'une fiche forment une grille de deux colonnes ; et la dominante colorimétrique et le nom de dossier, qui servent à l'atelier et pas au choix d'une photo, s'effacent. La ligne de compteurs de l'en-tête, qui répète ceux des onglets, s'efface aussi : l'en-tête reste collé en haut de l'écran, et chaque ligne y coûte en permanence.
Le format a été reconstitué à partir d'un compte source analysé au scraper : 13 carrousels, 40 300 vues médianes, 144 700 au meilleur. Le chiffre qui explique tout est le ratio commentaires/likes de 14,4 %, là où la normale sur TikTok est de 1 à 3 %. Le compte ne fait pas de la belle photo, il fabrique une machine à commentaires.
Constats repris dans le générateur :
Le serveur local est un processus persistant qui écrit sur le disque ; Vercel exécute des fonctions sans état sur un système de fichiers éphémère. Trois choses ont donc été déplacées :
registres, relus et renvoyés sous verrou au moment du tirage.
Ils ont longtemps été écrits dans le bucket — qui n'accepte que du JPEG : chaque
envoi était refusé sans bruit, et chaque génération en ligne repartait de
registres vides. La règle des 40 prénoms, la carence des lieux, l'exclusion des
poses déjà jouées et la rotation des variantes de cartes n'ont donc jamais
fonctionné en ligne avant cette table.Le code de génération n'est pas dupliqué : on recrée dans /tmp
l'arborescence qu'il attend et on le laisse travailler. Une génération prend
environ 95 secondes, sous la limite de 300 s déclarée dans vercel.json.
web/app.html est fabriqué depuis gen/interface.html par
python gen/faire_web.py, qui échoue bruyamment si une règle de
transformation ne s'applique plus. Une seule source pour les deux interfaces.
On ne modifie jamais web/app.html à la main : deux retouches faites
directement dedans — les appels de la carte de personnalité en ligne, et la
gestion d'erreur de la bibliothèque — ont été effacées par une régénération, et
la carte est restée cassée en ligne jusqu'à ce qu'on s'en aperçoive. Elles vivent
maintenant dans la source et dans les règles, et le fichier généré le rappelle
en tête.
Les vignettes et les photos pleine taille sont servies directement par le CDN
Supabase, avec des URL signées fabriquées en un seul appel groupé. Les signer
une par une demandait 165 requêtes enchaînées, soit 24 secondes de bibliothèque ;
les faire transiter par une fonction coûtait 8,5 secondes par image. Les photos
pleine taille passaient encore par une fonction jusqu'ici — treize allers-retours
pour un « Tout télécharger » ; api/media.py n'est plus qu'un repli, pour une
adresse expirée. Suffixée de &download=nom.jpg, une URL signée répond « pièce
jointe » : un clic télécharge au lieu d'ouvrir l'image.
Les fonctions tournent à Paris ("regions": ["cdg1"] dans vercel.json, une
seule région sur le plan Hobby). Par défaut, Vercel les place à Washington, alors
que la base est à Paris : chaque lecture traversait l'Atlantique deux fois. Mesuré
avant le changement : 0,6 s pour la requête la plus simple, 1,2 à 1,6 s pour la
bibliothèque, qui en fait quatre.
Rien n'attend le serveur à l'écran. Archiver, restaurer, supprimer et mettre en favori s'affichent en moins de 2 ms ; la requête part en arrière-plan, dans l'ordre des gestes pour une même fiche. Si elle échoue, un message le dit et la bibliothèque est relue telle que le serveur la connaît — la fiche revient. Chaque onglet est gardé en mémoire, et les deux autres sont lus dès le premier affichage : on passe de l'un à l'autre instantanément, la version à jour arrivant derrière. Une lecture partie avant une action est ignorée, sans quoi elle ferait réapparaître la fiche qu'on vient de supprimer.
Une vignette garde la même adresse tant que sa signature est valable. Les URL signées changent à chaque lecture de la bibliothèque, et le navigateur retéléchargeait toutes les images après chaque action.
La bibliothèque est gardée sur l'appareil (localStorage, quelques Ko par
fiche) : à la réouverture de l'app, elle s'affiche en moins d'une
milliseconde, et la version à jour la remplace dès qu'elle arrive. Pas au-delà de
100 minutes : les adresses des images, signées pour deux heures, auraient expiré.
La bibliothèque ne transporte plus les plans des photos ni la description du
visage, que l'écran n'affiche pas.
| Fichier | Rôle |
|---|---|
start.bat | Lancement : trouve Python, vérifie Pillow, démarre tout |
gen/ouvre.bat | Attend que le serveur réponde puis ouvre le navigateur |
gen/serveur.py | Serveur local, API, vignettes, zip, archives, corbeille |
gen/interface.html | Interface (relue à chaque requête, modifiable à chaud) |
gen/carrousel.py | Le générateur complet, et l'ajout de photos à un profil |
gen/controle.py | Contrôle visuel de chaque photo avant de la garder |
gen/runner.py | Appels fal, lecture de la clé |
gen/visages.py | ~747 milliards de visages, versions féminine et masculine |
gen/poses.py | 86 poses sûres, contraintes d'anatomie et de regard |
gen/lieux.py | 225 fonds, carence de 30 générations avant réutilisation |
gen/filters.py | Étalonnage doux et continu, par persona |
gen/identite.py | Prénoms INSEE par cohorte, métiers et textes à incruster |
gen/genre.py | Conversion féminin → masculin des banques de textes |
gen/pipeline.py | Post-traitement « vraie photo de téléphone » |
gen/sauvegarde.py | Envoi des carrousels locaux vers Supabase |
gen/faire_web.py | Fabrique web/app.html depuis l'interface locale |
gen/faire_icones.py | Dessine les icônes de l'app installable |
sw.js, web/manifest.webmanifest, web/installer.js | L'app installable sur l'écran d'accueil |
api/*.py | Fonctions Vercel : bibliothèque, génération, extension, médias, session |
web/*.html | Interface en ligne et page de connexion |
Les poses à risque sont bannies, pas rafistolées. 23 poses sur 86 ont été supprimées parce que le modèle les rate systématiquement : troisième bras, main désincarnée, articulation impossible. Contracter les deux biceps devant un miroir reste faux même en écrivant explicitement « un seul bras levé, le téléphone dans l'autre main » — le geste est trop associé à la symétrie dans les données d'entraînement.
Un fond ne peut pas revenir avant 30 générations. À 5 photos par profil, cela bloque 150 fonds en permanence, d'où les 225 de la banque. Le tirage privilégie les types de scène dont la réserve est la plus libre, sinon un type s'épuise localement. Vérifié par simulation sur 300 profils : zéro violation.
Les prénoms viennent de l'état civil, pas de l'intuition. Les six banques
sont le classement réel des 60 prénoms les plus donnés à chaque cohorte de
naissance, calculé sur le fichier des prénoms de
l'INSEE — de 46 % des naissances
pour les plus jeunes à 81 % pour les hommes nés entre 1956 et 1964. Deux
précautions à la lecture du fichier : l'état civil enregistre les variantes
accentuées séparément (182 343 JEROME contre 22 847 JÉRÔME, qui sont le même
prénom, et dont la graphie majoritaire est la fautive), et un prénom suit sa
génération et non un âge — une femme de 45 ans est née en 1981 aujourd'hui et en
1991 dans dix ans. Les banques sont donc indexées par année de naissance, ce qui
les garde justes sans y revenir.
Un prénom ne revient pas avant 40 profils, et c'est arithmétique plutôt que
probabiliste : 60 prénoms par banque contre une fenêtre de 40, si bien que même
quarante profils consécutifs de la même cohorte en laissent vingt de libres.
python gen/identite.py le vérifie par simulation — sur 400 profils d'âges
tirés au hasard, 134 prénoms différents et le retour le plus court à 41 profils.
Les banques de textes ne sont pas dupliquées pour les hommes, elles sont
converties. La seule difficulté de l'anglais est « her », tantôt possessif
(her free hand → his) tantôt complément (behind her → him) ; la règle
qui tranche est ce qui suit le mot, avec une exception devant les déterminants
et les adverbes. Vérifié sur les 119 textes de la banque.
Chaque photo est relue avant d'être gardée, sur l'image brute — le flou, le
bruit et la double compression du post-traitement sont ajoutés exprès, et un juge
les prendrait pour des défauts. Ce n'est pas théorique : sur la version dégradée
d'une photo à trois bras, il ne les voit plus. Trois questions, toutes objectives :
est-ce la même personne que sur la photo 1, le corps est-il possible, y a-t-il un
élément qui ne peut pas exister. Une photo recalée est refaite, deux fois au plus,
avec la faute constatée réinjectée dans le prompt. ATELIER_CONTROLE=0 désactive
tout.
L'âge n'est pas contrôlé, et c'est voulu. Il l'a été, avec relance à la clé, et c'était une erreur à deux titres. C'est un avis et non un constat : le même visage était estimé 35 puis 40 ans d'un appel à l'autre. Et relancer n'y changeait rien : les photos 2 à 5 recopient le visage de la photo 1, et le modèle d'image rajeunit systématiquement au-delà de 55 ans. Sur une demande à 58 ans, huit relances d'affilée pour « paraît 45 ans » ont poussé la génération en ligne à 437 secondes ; Vercel l'a tuée à 300, au milieu de la quatrième photo, avant qu'elle ait rien enregistré.
L'âge se règle maintenant à la source, dans la description du visage
(gen/visages.py). Deux mécanismes :
Comparé sur les mêmes tirages à 58 ans : visages lisses de 45-50 ans avant, femmes de la fin de la cinquantaine après, toujours séduisantes ; sans surcharge à 45 ans, nettement sexagénaires à 64. Les taches de l'âge sont décrites sur les mains et non sur le visage : autorisées sur le visage, elles laissaient le modèle y semer trois ou quatre points sombres par photo, jamais les mêmes d'une photo à l'autre.
En ligne, une génération ne peut plus être tuée. Plus aucune relance après 150 secondes, plus aucune photo entamée après 235 : au pire, une photo imparfaite est gardée, ou le carrousel s'arrête à quatre photos — mais il est enregistré. Et un job resté « en cours » plus de 330 secondes est déclaré en échec : une fonction tuée ne peut pas écrire son propre échec, et l'interface l'attendait indéfiniment.
Mesure sur 20 photos déjà générées : zéro faux positif sur le corps, et les vrais défauts attrapés — une femme de 49 ans au visage de 35, deux visages qui ont dérivé, un double biceps où une troisième main tient le téléphone.
Les trois appels ne sont pas une précaution, ce sont des mesures. Chaque fois qu'une question a rejoint les autres dans le même appel, la réponse s'est dégradée — et toujours en silence, en répondant « ok ».
| Question | Posée seule | Posée avec les autres |
|---|---|---|
| Hauteur du visage, sujet lointain | 6 % | 14 %, au-dessus du seuil |
| Bras d'un double biceps à trois bras | 3 détections sur 3 | 0 sur 3 |
La deuxième ligne est la plus instructive : il a suffi d'accompagner la question d'une liste de ce qu'il fallait chercher — bras partant d'un mauvais endroit, articulation à l'envers, torse vrillé — pour qu'elle passe de 3/3 à 0/3. L'énumération fait comparer l'image à une liste ; son absence oblige à la regarder. La consigne sur le corps tient donc en trois lignes, et demande de compter plutôt que de juger : on agit sur les nombres — plus de deux bras, de deux mains, de deux jambes — et pas sur l'avis. Moins de deux n'est jamais une faute : un membre sort du cadre à presque chaque photo.
Sur la photo où le sujet est minuscule dans le cadre — une règle du format, pas un accident — il répond « ce n'est pas la même personne » avec 90 % de certitude et cite les traits qui diffèrent, sur un visage haut de 50 pixels. Lui demander de douter ne change rien, sa certitude vaut 90 dans les deux cas. En revanche il localise très bien. Un verdict qui porte sur le visage déclenche donc un troisième appel qui ne demande que la boîte englobante ; sous 12 % de la hauteur d'image, le reproche est écarté. Mesuré : gros plan 30 à 44 %, plan moyen 18 à 23 %, femme au bout de la rue 5,9 %.
La photo 1 a une exigence de plus : son visage doit être lisible, puisque les quatre autres s'y comparent. Un carrousel dont la photo 1 portait des lunettes noires de nuit a vu ses quatre suivantes partir chacune de son côté.
La cohérence du visage se dégrade toujours à la génération, davantage au-delà de 50 ans et quand les lumières des scènes tirées sont éloignées ; le contrôle la rattrape au lieu de l'empêcher. L'âge rendu dépend encore du tirage : d'un carrousel à l'autre, une femme de 58 ans peut paraître 55 ou 62, et les photos d'un même carrousel suivent toutes le visage de la première.
| Nom | Rôle |
|---|---|
FAL_KEY | Clé fal, pour la génération et le contrôle |
SUPABASE_URL | URL du projet Supabase |
SUPABASE_SERVICE_KEY | Clé de service, accès au stockage et aux tables |
ATELIER_MDP | Mot de passe de l'interface en ligne |
SESSION_SECRET | Signature du cookie de session |
ATELIER_CONTROLE | 0 pour désactiver le contrôle visuel (actif par défaut) |
En local elles se lisent depuis .env ; en ligne depuis les variables du
projet Vercel. Le fichier .env n'est jamais versionné.